[
  {
    "path": ".clippy.toml",
    "content": "disallowed-methods = [\n  { path = \"str::to_ascii_lowercase\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_lowercase` instead.\" },\n  { path = \"str::to_ascii_uppercase\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_uppercase` instead.\" },\n  { path = \"str::to_lowercase\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_to_lowercase` instead.\" },\n  { path = \"str::to_uppercase\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_to_uppercase` instead.\" },\n  { path = \"str::replace\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_replace` instead.\" },\n  { path = \"str::replacen\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_replacen` instead.\" },\n]\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.{mts,ts,js,mjs}]\nindent_style = space\nindent_size = 2\n\n[*.{css}]\nindent_style = space\nindent_size = 2\n\n[*.{json,yml,yaml}]\nindent_style = space\nindent_size = 2\n\n[*.{md}]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".gitattributes",
    "content": "*       text=auto\n\n*.sh    text eol=lf merge=union\n*.rs    text eol=lf merge=union\n*.js    text eol=lf merge=union\n*.mjs   text eol=lf merge=union\n*.ts    text eof=lf merge=union\n*.mts   text eof=lf merge=union\n*.toml  text eol=lf merge=union\n*.json  text eol=lf merge=union\n*.yaml  text eol=lf merge=union\n*.yml   text eol=lf merge=union\n*.md    text eol=lf merge=union\n*.css   text eol=lf merge=union\n*.html  text eol=lf merge=union\n*.csv   text eol=lf merge=union\n"
  },
  {
    "path": ".github/renovate.json",
    "content": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:recommended\",\n    \"npm:unpublishSafe\",\n    \":semanticCommits\"\n  ],\n  \"timezone\": \"Etc/UTC\",\n  \"schedule\": [\n    \"* 0-4 * * 1-3\"\n  ],\n  \"labels\": [\n    \"dependencies\"\n  ],\n  \"commitMessagePrefix\": \"chore: \",\n  \"commitMessageAction\": \"bump up\",\n  \"commitMessageTopic\": \"{{depName}} version\",\n  \"ignoreDeps\": [],\n  \"packageRules\": [\n    {\n      \"groupName\": \"all non-major dependencies\",\n      \"groupSlug\": \"all-minor-patch\",\n      \"matchPackageNames\": [\n        \"*\"\n      ],\n      \"matchUpdateTypes\": [\n        \"minor\",\n        \"patch\"\n      ]\n    },\n    {\n      \"groupName\": \"all devDependencies\",\n      \"groupSlug\": \"all-dev-dependencies\",\n      \"matchDepTypes\": [\n        \"devDependencies\"\n      ],\n      \"rangeStrategy\": \"bump\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\nenv:\n  ACTION_CACHE_PATH: |\n    ~/.bun/install/cache\n    node_modules/\npermissions:\n  contents: write\n  id-token: write\non:\n  workflow_dispatch:\n    inputs:\n      publish:\n        required: false\n        type: boolean\n  push:\n    paths:\n      - packages/**\n      - bun.lock\n      - \"!crates/**\"\n      - \"!packages/@ivi/compiler/**\"\n      - \"!tests/compiler/**\"\n    branches:\n      - master\n  pull_request:\n    paths:\n      - packages/**\n      - bun.lock\n      - \"!crates/**\"\n      - \"!packages/@ivi/compiler/**\"\n      - \"!tests/compiler/**\"\n    branches:\n      - master\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\njobs:\n  publish:\n    name: Publish\n    if: \"${{ github.event_name != 'pull_request' && (inputs.publish || startsWith(github.event.head_commit.message, 'publish:')) }}\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24\n      - uses: oven-sh/setup-bun@v2\n      - uses: extractions/setup-just@v4\n      - uses: actions/cache@v5\n        with:\n          path: ${{ env.ACTION_CACHE_PATH }}\n          key: CI\n      - run: just init\n      - run: just tsc\n      - name: Publish to NPM\n        run: |\n          npm config set provenance true\n          echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" >> ~/.npmrc\n          just publish --provenance --access public\n"
  },
  {
    "path": ".github/workflows/napi-libraries.yml",
    "content": "name: NAPI Libraries\nenv:\n  DEBUG: napi:*\n  MACOSX_DEPLOYMENT_TARGET: \"10.13\"\n  CARGO_INCREMENTAL: \"1\"\n  ACTION_CACHE_PATH: |\n    ~/.cargo/bin/\n    ~/.cargo/registry/index/\n    ~/.cargo/registry/cache/\n    ~/.cargo/git/db/\n    target/\n    ~/.bun/install/cache\n    node_modules/\npermissions:\n  contents: write\n  id-token: write\non:\n  workflow_dispatch:\n    inputs:\n      publish:\n        required: false\n        type: boolean\n  push:\n    paths:\n      - crates/**\n      - packages/@ivi/compiler/**\n      - tests/compiler/**\n      - Cargo.lock\n    branches:\n      - master\n  pull_request:\n    paths:\n      - crates/**\n      - packages/@ivi/compiler/**\n      - tests/compiler/**\n      - Cargo.lock\n    branches:\n      - master\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\njobs:\n  build-napi:\n    strategy:\n      fail-fast: false\n      matrix:\n        settings:\n          - host: ubuntu-latest\n            target: x86_64-unknown-linux-gnu\n            build: just napi build --release --target x86_64-unknown-linux-gnu --use-napi-cross\n          - host: ubuntu-latest\n            target: aarch64-unknown-linux-gnu\n            build: just napi build --release --target aarch64-unknown-linux-gnu --use-napi-cross\n          - host: macos-latest\n            target: x86_64-apple-darwin\n            build: just napi build --release --target x86_64-apple-darwin\n          - host: macos-latest\n            target: aarch64-apple-darwin\n            build: just napi build --release --target aarch64-apple-darwin\n          - host: windows-latest\n            target: x86_64-pc-windows-msvc\n            build: just napi build --release --target x86_64-pc-windows-msvc\n          - host: windows-latest\n            target: aarch64-pc-windows-msvc\n            build: just napi build --release --target aarch64-pc-windows-msvc\n    name: stable - ${{ matrix.settings.target }}\n    runs-on: ${{ matrix.settings.host }}\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24\n      - uses: oven-sh/setup-bun@v2\n      - uses: extractions/setup-just@v4\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: stable\n          targets: ${{ matrix.settings.target }}\n      - uses: actions/cache@v5\n        with:\n          path: ${{ env.ACTION_CACHE_PATH }}\n          key: NAPI-${{ matrix.settings.target }}-${{ matrix.settings.host }}\n      - run: just init\n      - run: ${{ matrix.settings.build }}\n      - uses: actions/upload-artifact@v7\n        with:\n          name: NAPI-${{ matrix.settings.target }}\n          path: ./packages/@ivi/compiler/ivi-compiler.*.node\n          if-no-files-found: error\n  test:\n    name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }}\n    needs:\n      - build-napi\n    strategy:\n      fail-fast: false\n      matrix:\n        settings:\n          - host: ubuntu-latest\n            target: x86_64-unknown-linux-gnu\n            architecture: x64\n          - host: ubuntu-24.04-arm\n            target: aarch64-unknown-linux-gnu\n            architecture: arm64\n          - host: windows-latest\n            target: x86_64-pc-windows-msvc\n            architecture: x64\n          # Bun doesn't have windows arm64 binaries\n          # - host: windows-11-arm\n          #   target: aarch64-pc-windows-msvc\n          #   architecture: arm64\n          - host: macos-latest\n            target: aarch64-apple-darwin\n            architecture: arm64\n          # Bun is broken on x86_64 macos\n          # - host: macos-latest\n          #   target: x86_64-apple-darwin\n          #   architecture: x64\n    runs-on: ${{ matrix.settings.host }}\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24\n          architecture: ${{ matrix.settings.architecture }}\n      - uses: oven-sh/setup-bun@v2\n      - uses: extractions/setup-just@v4\n      - uses: actions/cache@v5\n        with:\n          path: ${{ env.ACTION_CACHE_PATH }}\n          key: ${{ matrix.settings.target }}-${{ matrix.settings.host }}\n      - run: just init\n        if: steps.cache.outputs.cache-hit != 'true'\n      - uses: actions/download-artifact@v8\n        with:\n          name: NAPI-${{ matrix.settings.target }}\n          path: ./packages/@ivi/compiler/\n      - run: just napi test\n  publish:\n    name: Publish\n    if: \"${{ github.event_name != 'pull_request' && (inputs.publish || startsWith(github.event.head_commit.message, 'publish:')) }}\"\n    runs-on: ubuntu-latest\n    needs:\n      - test\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24\n      - uses: oven-sh/setup-bun@v2\n      - uses: extractions/setup-just@v4\n      - uses: actions/cache@v5\n        with:\n          path: ${{ env.ACTION_CACHE_PATH }}\n          key: NAPI-x86_64-unknown-linux-gnu-ubuntu-latest\n      - run: just init\n        if: steps.cache.outputs.cache-hit != 'true'\n      - uses: actions/download-artifact@v8\n        with:\n          pattern: NAPI-*\n          path: napi-artifacts\n      - run: just napi artifacts\n      - name: Publish to NPM\n        run: |\n          npm config set provenance true\n          echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" >> ~/.npmrc\n          just napi publish --provenance --access public\n"
  },
  {
    "path": ".github/workflows/napi-release.yml",
    "content": "name: NAPI Release\npermissions:\n  contents: write\n  id-token: write\non:\n  workflow_dispatch:\n    inputs:\n      increment:\n        type: choice\n        required: true\n        description: Increment version\n        options: \n        - patch\n        - minor\n        - major\njobs:\n  publish:\n    name: Release new @ivi/compiler version\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 24\n      - uses: oven-sh/setup-bun@v2\n      - uses: extractions/setup-just@v4\n      - name: Increment versions and push changes\n        run: |\n          just napi increment-versions ${{ github.event.inputs.increment }}\n          git config --global user.name \"GitHub Action\"\n          git config --global user.email \"username@users.noreply.github.com\"\n          git commit -a -m \"publish: @ivi/compiler $(jq -r .version ./packages/@ivi/compiler/package.json)\"\n          git push\n"
  },
  {
    "path": ".gitignore",
    "content": "/examples/**/dist\n/packages/**/dist\n/tests/dist\n\n.rsync-filter\n.DS_Store\n\n# Rust\n/target/\n\n# NAPI-rs\n/napi-artifacts/\n\n# JS\nnode_modules\n*.tsbuildinfo\nnpm-debug.log\n\n# Editors\n/.idea/\n/.vscode/\n!/.vscode/settings.json\n"
  },
  {
    "path": ".rustfmt.toml",
    "content": "unstable_features = true\nversion = \"Two\"\nstyle_edition = \"2024\"\nedition = \"2024\"\nformat_strings = true\nformat_code_in_doc_comments = true\nhex_literal_case = \"Lower\"\nwrap_comments = true\nreorder_modules = true\nreorder_impl_items = true\nuse_field_init_shorthand = true\nuse_small_heuristics = \"Max\"\ngroup_imports = \"StdExternalCrate\"\nimports_granularity = \"Crate\"\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## Requirements\n\n- Bun\n- Just\n\n## Getting Started\n\n1. Clone the git repository: `git clone git@github.com:localvoid/ivi.git`\n2. Go into the cloned folder: `cd ivi/`\n3. Install all dependencies: `just init`\n\n## Tasks\n\n- `just init` - initializes development environment.\n- `just napi build` - builds NAPI bindings.\n- `just tsc` - builds typescript packages.\n- `just napi test` - runs NAPI tests.\n- `just test` - runs tests.\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nmembers = [\"crates/*\", \"packages/@ivi/compiler\"]\nresolver = \"3\"\n\n[workspace.dependencies]\nthiserror = \"2\"\nrustc-hash = \"2\"\nindexmap = \"2.10\"\noxc_allocator = \"0.121\"\noxc_ast = \"0.121\"\noxc_codegen = \"0.121\"\noxc_data_structures = \"0.121\"\noxc_diagnostics = \"0.121\"\noxc_ecmascript = \"0.121\"\noxc_parser = \"0.121\"\noxc_semantic = \"0.121\"\noxc_span = \"0.121\"\noxc_syntax = \"0.121\"\noxc_traverse = \"0.121\"\nnapi = \"3\"\nnapi-derive = \"3\"\nnapi-build = \"2\"\nivi_compiler = { path = \"./crates/ivi_compiler\" }\n\n[workspace.lints.clippy]\ndbg_macro = \"warn\"\ntodo = \"warn\"\nunimplemented = \"warn\"\nprint_stdout = \"warn\"\nprint_stderr = \"warn\"\nallow_attributes = \"warn\"\nclone_on_ref_ptr = \"warn\"\nself_named_module_files = \"warn\"\nempty_drop = \"warn\"\nempty_structs_with_brackets = \"warn\"\nexit = \"warn\"\nget_unwrap = \"warn\"\nrc_buffer = \"warn\"\nrc_mutex = \"warn\"\nrest_pat_in_fully_bound_structs = \"warn\"\nunnecessary_safety_comment = \"warn\"\nundocumented_unsafe_blocks = \"warn\"\ninfinite_loop = \"warn\"\nmap_with_unused_argument_over_ranges = \"warn\"\nunused_result_ok = \"warn\"\npathbuf_init_then_push = \"warn\"\ncollapsible_if = \"allow\"\ncollapsible_else_if = \"allow\"\ncollapsible_match = \"allow\"\n\n[profile.release]\nopt-level = 3\ncodegen-units = 1\nlto = \"fat\"\npanic = \"abort\"\nstrip = true\nsplit-debuginfo = \"packed\"\n\n[profile.dev]\ndebug = false\n\n[profile.release-with-debug]\ninherits = \"release\"\nstrip = false\ndebug = true\n\n[profile.coverage]\ninherits = \"release\"\nopt-level = 2\ncodegen-units = 256\nlto = \"thin\"\ndebug-assertions = true\noverflow-checks = true\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-2024 Boris Kaul <localvoid@gmail.com>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "crates/ivi_compiler/Cargo.toml",
    "content": "[package]\nname = \"ivi_compiler\"\nversion = \"0.1.0\"\nedition = \"2024\"\nauthors = [\"Boris Kaul <localvoid@gmail.com>\"]\nlicense = \"MIT\"\nhomepage = \"https://github.com/localvoid/ivi\"\nrepository = \"https://github.com/localvoid/ivi\"\ndescription = \"ivi template compiler\"\n\n[dependencies]\nthiserror.workspace = true\nrustc-hash.workspace = true\nindexmap.workspace = true\noxc_allocator.workspace = true\noxc_ast.workspace = true\noxc_codegen.workspace = true\noxc_data_structures.workspace = true\noxc_diagnostics.workspace = true\noxc_ecmascript.workspace = true\noxc_parser.workspace = true\noxc_semantic.workspace = true\noxc_span.workspace = true\noxc_syntax.workspace = true\noxc_traverse.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/ivi_compiler/src/chunk/mod.rs",
    "content": "use oxc_allocator::{Allocator, Vec as ArenaVec};\nuse oxc_ast::ast::*;\nuse oxc_semantic::Scoping;\nuse oxc_span::SPAN;\nuse oxc_traverse::{Traverse, traverse_mut};\nuse rustc_hash::FxHashMap;\n\nuse crate::{\n    context::{TraverseCtx, TraverseCtxState},\n    tpl::opcodes::prop_op,\n};\n\npub fn compile_chunk<'a>(\n    program: &mut Program<'a>,\n    allocator: &'a Allocator,\n    scoping: Scoping,\n    strings: &FxHashMap<String, u8>,\n) {\n    let mut t = ChunkCompiler::new(strings);\n    traverse_mut(&mut t, allocator, program, scoping, TraverseCtxState::default());\n}\n\nstruct ChunkCompiler<'ctx> {\n    strings: &'ctx FxHashMap<String, u8>,\n}\n\nimpl<'ctx> ChunkCompiler<'ctx> {\n    pub fn new(strings: &'ctx FxHashMap<String, u8>) -> Self {\n        Self { strings }\n    }\n}\n\nimpl<'a> Traverse<'a, TraverseCtxState<'a>> for ChunkCompiler<'_> {\n    fn enter_expression(&mut self, node: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {\n        match node {\n            Expression::ArrayExpression(expr) => {\n                if expr.elements.len() == 1 {\n                    if let Some(Expression::StringLiteral(s)) = expr.elements[0].as_expression() {\n                        if s.value == STRINGS_UUID {\n                            let mut indexed: Vec<_> = self.strings.iter().collect();\n                            indexed.sort_by_key(|e| e.1);\n\n                            let mut strings = ctx.ast.vec_with_capacity(self.strings.len());\n                            for (s, _) in indexed {\n                                strings.push(ArrayExpressionElement::StringLiteral(\n                                    ctx.ast.alloc_string_literal(SPAN, ctx.ast.atom(s), None),\n                                ));\n                            }\n                            *node = ctx.ast.expression_array(SPAN, strings);\n                        }\n                    }\n                }\n            }\n            Expression::CallExpression(expr) => {\n                if let Some(\"__IVI_TPL__\") = expr.callee_name() {\n                    if let Some(mut arg0) = expr.arguments.pop() {\n                        if let Some(Expression::CallExpression(call)) = arg0.as_expression_mut() {\n                            if call.arguments.len() > 5 {\n                                if let Some(Argument::ArrayExpression(tpl_strings)) =\n                                    call.arguments.pop()\n                                {\n                                    let prop_op_codes = &mut call.arguments[2];\n                                    update_prop_op_codes(\n                                        prop_op_codes.as_expression_mut().unwrap(),\n                                        &tpl_strings.elements,\n                                        self.strings,\n                                    );\n                                }\n                            }\n                            *node = arg0.into_expression();\n                        }\n                    }\n                }\n            }\n            _ => {}\n        }\n    }\n}\n\nfn update_prop_op_codes<'a>(\n    expr: &mut Expression,\n    tpl_strings: &ArenaVec<'a, ArrayExpressionElement<'a>>,\n    strings: &FxHashMap<String, u8>,\n) {\n    match expr {\n        // dedupe(op_codes)\n        Expression::CallExpression(c) => {\n            if let Some(e) = c.arguments.get_mut(0).and_then(|a| a.as_expression_mut()) {\n                update_prop_op_codes(e, tpl_strings, strings);\n            }\n        }\n        Expression::ArrayExpression(a) => {\n            for el in &mut a.elements {\n                if let Some(Expression::NumericLiteral(op)) = el.as_expression_mut() {\n                    let v = op.value as u32;\n                    let ty = v & prop_op::TYPE_MASK;\n                    if ty != prop_op::SET_NODE && ty != prop_op::COMMON && ty != prop_op::DIRECTIVE\n                    {\n                        let i = v >> prop_op::DATA_SHIFT;\n                        let s = &tpl_strings[i as usize];\n                        if let ArrayExpressionElement::StringLiteral(s) = s {\n                            if let Some(new_index) = strings.get(s.value.as_str()) {\n                                op.value = ((v & ((1 << prop_op::DATA_SHIFT) - 1))\n                                    | ((*new_index as u32) << prop_op::DATA_SHIFT))\n                                    as f64;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        _ => {}\n    }\n}\n\nconst STRINGS_UUID: &str = \"IVI:fa7327d9-0034-492d-bfdf-576548b2d9cc\";\n"
  },
  {
    "path": "crates/ivi_compiler/src/context.rs",
    "content": "use std::marker::PhantomData;\n\npub type TraverseCtx<'a> = oxc_traverse::TraverseCtx<'a, TraverseCtxState<'a>>;\n\n#[derive(Default)]\npub struct TraverseCtxState<'a> {\n    data: PhantomData<&'a ()>,\n}\n"
  },
  {
    "path": "crates/ivi_compiler/src/import.rs",
    "content": "use oxc_ast::{\n    NONE,\n    ast::{\n        Expression, ImportDeclarationSpecifier, ImportOrExportKind, ModuleExportName, Statement,\n    },\n};\nuse oxc_semantic::SymbolFlags;\nuse oxc_span::SPAN;\nuse oxc_traverse::BoundIdentifier;\n\nuse crate::context::TraverseCtx;\n\n#[derive(Default)]\npub struct ImportSymbols<'a> {\n    descriptor_id: Option<BoundIdentifier<'a>>,  // _T\n    html_id: Option<BoundIdentifier<'a>>,        // _hN\n    html_el_id: Option<BoundIdentifier<'a>>,     // _hE\n    svg_id: Option<BoundIdentifier<'a>>,         // _sN\n    svg_el_id: Option<BoundIdentifier<'a>>,      // _sE\n    tpl_id: Option<BoundIdentifier<'a>>,         // _t\n    empty_array_id: Option<BoundIdentifier<'a>>, // _t\n\n    hoist_id: Option<BoundIdentifier<'a>>,\n    dedupe_id: Option<BoundIdentifier<'a>>,\n}\n\nimpl<'a> ImportSymbols<'a> {\n    pub fn template_descriptor(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {\n        get(&mut self.descriptor_id, \"_T\", ctx)\n    }\n\n    pub fn html_template(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {\n        get(&mut self.html_id, \"_hN\", ctx)\n    }\n\n    pub fn html_element(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {\n        get(&mut self.html_el_id, \"_hE\", ctx)\n    }\n\n    pub fn svg_template(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {\n        get(&mut self.svg_id, \"_sN\", ctx)\n    }\n\n    pub fn svg_element(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {\n        get(&mut self.svg_el_id, \"_sE\", ctx)\n    }\n\n    pub fn create_from_template(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {\n        get(&mut self.tpl_id, \"_t\", ctx)\n    }\n\n    pub fn empty_array(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {\n        get(&mut self.empty_array_id, \"EMPTY_ARRAY\", ctx)\n    }\n\n    pub fn hoist(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {\n        get(&mut self.hoist_id, \"hoist\", ctx)\n    }\n\n    pub fn dedupe(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {\n        get(&mut self.dedupe_id, \"dedupe\", ctx)\n    }\n\n    pub fn create_import_statements(&self, ctx: &mut TraverseCtx<'a>) -> Vec<Statement<'a>> {\n        let mut imports = Vec::new();\n        let mut specifiers = ctx.ast.vec();\n        if let Some(id) = &self.descriptor_id {\n            specifiers.push(spec(\"_T\", id, ctx));\n        }\n        if let Some(id) = &self.html_id {\n            specifiers.push(spec(\"_hN\", id, ctx));\n        }\n        if let Some(id) = &self.html_el_id {\n            specifiers.push(spec(\"_hE\", id, ctx));\n        }\n        if let Some(id) = &self.svg_id {\n            specifiers.push(spec(\"_sN\", id, ctx));\n        }\n        if let Some(id) = &self.svg_el_id {\n            specifiers.push(spec(\"_sE\", id, ctx));\n        }\n        if let Some(id) = &self.tpl_id {\n            specifiers.push(spec(\"_t\", id, ctx));\n        }\n        if let Some(id) = &self.empty_array_id {\n            specifiers.push(spec(\"EMPTY_ARRAY\", id, ctx));\n        }\n        if !specifiers.is_empty() {\n            imports.push(Statement::ImportDeclaration(ctx.ast.alloc_import_declaration(\n                SPAN,\n                Some(specifiers),\n                ctx.ast.string_literal(SPAN, ctx.ast.atom(\"ivi\"), None),\n                None,\n                NONE,\n                ImportOrExportKind::Value,\n            )));\n        }\n\n        let mut specifiers = ctx.ast.vec();\n        if let Some(id) = &self.hoist_id {\n            specifiers.push(spec(\"hoist\", id, ctx));\n        }\n        if let Some(id) = &self.dedupe_id {\n            specifiers.push(spec(\"dedupe\", id, ctx));\n        }\n        if !specifiers.is_empty() {\n            imports.push(Statement::ImportDeclaration(ctx.ast.alloc_import_declaration(\n                SPAN,\n                Some(specifiers),\n                ctx.ast.string_literal(SPAN, ctx.ast.atom(\"oveo\"), None),\n                None,\n                NONE,\n                ImportOrExportKind::Value,\n            )));\n        }\n\n        imports\n    }\n}\n\nfn get<'a>(\n    cell: &mut Option<BoundIdentifier<'a>>,\n    name: &'static str,\n    ctx: &mut TraverseCtx<'a>,\n) -> Expression<'a> {\n    if let Some(id) = cell {\n        id.create_read_expression(ctx)\n    } else {\n        let uid = ctx.generate_uid_in_root_scope(name, SymbolFlags::ConstVariable);\n        let read = uid.create_read_expression(ctx);\n        *cell = Some(uid);\n        read\n    }\n}\n\nfn spec<'a>(\n    name: &'static str,\n    id: &BoundIdentifier<'a>,\n    ctx: &mut TraverseCtx<'a>,\n) -> ImportDeclarationSpecifier<'a> {\n    ImportDeclarationSpecifier::ImportSpecifier(ctx.ast.alloc_import_specifier(\n        SPAN,\n        ModuleExportName::IdentifierName(ctx.ast.identifier_name(SPAN, ctx.ast.atom(name))),\n        id.create_binding_identifier(ctx),\n        ImportOrExportKind::Value,\n    ))\n}\n"
  },
  {
    "path": "crates/ivi_compiler/src/lib.rs",
    "content": "use std::path::PathBuf;\n\nuse oxc_allocator::Allocator;\nuse oxc_codegen::{Codegen, CodegenOptions};\nuse oxc_diagnostics::GraphicalReportHandler;\nuse oxc_parser::Parser;\nuse oxc_semantic::SemanticBuilder;\nuse oxc_span::SourceType;\nuse rustc_hash::{FxHashMap, FxHashSet};\n\nmod chunk;\nmod context;\nmod import;\nmod module;\nmod oveo;\nmod tpl;\n\n#[derive(Default, Debug)]\npub struct CompilerOptions {\n    pub dedupe_strings: bool,\n    pub oveo: bool,\n}\n\npub struct CompilerOutput {\n    pub code: String,\n    pub map: String,\n}\n\n#[derive(Debug, thiserror::Error)]\npub enum CompilerError {\n    #[error(\"Invalid module type: {0}\")]\n    ModuleType(String),\n    #[error(\"Unable to parse javascript file: {0}\")]\n    SyntaxError(String),\n    #[error(\"Unable to parse javascript file: {0}\")]\n    SemanticError(String),\n    #[error(\"Invalid template: {0}\")]\n    InvalidTemplate(String),\n}\n\npub fn compile_module(\n    source_text: &str,\n    module_type: &str,\n    options: &CompilerOptions,\n    strings: &mut FxHashSet<String>,\n) -> Result<CompilerOutput, CompilerError> {\n    let allocator = Allocator::default();\n    let source_type = match module_type {\n        \"js\" => SourceType::mjs(),\n        \"jsx\" => SourceType::jsx(),\n        \"ts\" => SourceType::ts(),\n        \"tsx\" => SourceType::tsx(),\n        _ => return Err(CompilerError::ModuleType(module_type.to_string())),\n    };\n    let ret = Parser::new(&allocator, source_text, source_type).parse();\n    if let Some(err) = ret.errors.first() {\n        return Err(CompilerError::SyntaxError(err.to_string()));\n    }\n\n    let mut program = ret.program;\n\n    let ret = SemanticBuilder::new().with_excess_capacity(1.0).build(&program);\n    if let Some(err) = ret.errors.first() {\n        return Err(CompilerError::SemanticError(err.to_string()));\n    }\n\n    let scoping = ret.semantic.into_scoping();\n    let mut errors = module::compile_module(&mut program, &allocator, scoping, options, strings);\n    if let Some(err) = errors.drain(..).next() {\n        let report_handler = GraphicalReportHandler::new();\n        let mut s = String::new();\n        let _ = report_handler\n            .render_report(&mut s, err.with_source_code(source_text.to_string()).as_ref());\n        return Err(CompilerError::InvalidTemplate(s));\n    }\n\n    let result = Codegen::new()\n        .with_options(CodegenOptions {\n            source_map_path: Some(PathBuf::new()),\n            ..Default::default()\n        })\n        .build(&program);\n\n    Ok(CompilerOutput {\n        code: result.code,\n        map: result.map.map_or_else(String::default, |v| v.to_json_string()),\n    })\n}\n\npub fn compile_chunk(\n    source_text: &str,\n    strings: &FxHashMap<String, u8>,\n) -> Result<CompilerOutput, CompilerError> {\n    let allocator = Allocator::default();\n    let source_type = SourceType::default();\n    let ret = Parser::new(&allocator, source_text, source_type).parse();\n    if let Some(err) = ret.errors.first() {\n        return Err(CompilerError::SyntaxError(err.to_string()));\n    }\n\n    let mut program = ret.program;\n\n    let ret = SemanticBuilder::new().with_excess_capacity(0.1).build(&program);\n    if let Some(err) = ret.errors.first() {\n        return Err(CompilerError::SemanticError(err.to_string()));\n    }\n\n    let scoping = ret.semantic.into_scoping();\n\n    chunk::compile_chunk(&mut program, &allocator, scoping, strings);\n\n    let result = Codegen::new()\n        .with_options(CodegenOptions {\n            source_map_path: Some(PathBuf::new()),\n            ..Default::default()\n        })\n        .build(&program);\n\n    Ok(CompilerOutput {\n        code: result.code,\n        map: result.map.map_or_else(String::default, |v| v.to_json_string()),\n    })\n}\n"
  },
  {
    "path": "crates/ivi_compiler/src/module/mod.rs",
    "content": "use std::collections::hash_map;\n\nuse oxc_allocator::{Address, Allocator, GetAddress, TakeIn};\nuse oxc_ast::ast::*;\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_semantic::{Scoping, SymbolId};\nuse oxc_traverse::{Traverse, traverse_mut};\nuse rustc_hash::{FxHashMap, FxHashSet};\n\nuse crate::{\n    CompilerOptions,\n    context::{TraverseCtx, TraverseCtxState},\n    import::ImportSymbols,\n    oveo::oveo_intrinsic,\n    tpl::{TemplateKind, compile_template},\n};\n\npub fn compile_module<'a>(\n    program: &mut Program<'a>,\n    allocator: &'a Allocator,\n    scoping: Scoping,\n    options: &CompilerOptions,\n    strings: &mut FxHashSet<String>,\n) -> Vec<OxcDiagnostic> {\n    let mut t = ModuleCompiler::new(options, strings);\n    traverse_mut(&mut t, allocator, program, scoping, TraverseCtxState::default());\n    t.errors\n}\n\nstruct ModuleCompiler<'a, 'ctx> {\n    options: &'ctx CompilerOptions,\n    strings: &'ctx mut FxHashSet<String>,\n    ivi_module: FxHashMap<SymbolId, IviSymbol>,\n    imports: ImportSymbols<'a>,\n    statements: Vec<Address>,\n    templates: FxHashMap<Address, Vec<Statement<'a>>>,\n    errors: Vec<OxcDiagnostic>,\n}\n\nimpl<'a, 'ctx> ModuleCompiler<'a, 'ctx> {\n    pub fn new(options: &'ctx CompilerOptions, strings: &'ctx mut FxHashSet<String>) -> Self {\n        Self {\n            options,\n            strings,\n            ivi_module: FxHashMap::default(),\n            imports: ImportSymbols::default(),\n            statements: Vec::new(),\n            templates: FxHashMap::default(),\n            errors: Vec::new(),\n        }\n    }\n\n    fn resolve(&self, expr: &Expression<'a>, scoping: &Scoping) -> Option<IviSymbol> {\n        match expr {\n            Expression::Identifier(id) => {\n                let r = scoping.get_reference(id.reference_id());\n                if let Some(symbol_id) = r.symbol_id() {\n                    self.ivi_module.get(&symbol_id).copied()\n                } else {\n                    None\n                }\n            }\n            Expression::StaticMemberExpression(expr) => {\n                if let Some(IviSymbol::Module) = self.resolve(&expr.object, scoping) {\n                    match expr.property.name.as_str() {\n                        \"component\" => Some(IviSymbol::Component),\n                        \"html\" => Some(IviSymbol::Html),\n                        \"svg\" => Some(IviSymbol::Svg),\n                        _ => None,\n                    }\n                } else {\n                    None\n                }\n            }\n            _ => None,\n        }\n    }\n\n    fn add_template_decl(&mut self, address: Address, decl: Statement<'a>) {\n        match self.templates.entry(address) {\n            hash_map::Entry::Occupied(mut entry) => {\n                entry.get_mut().push(decl);\n            }\n            hash_map::Entry::Vacant(entry) => {\n                entry.insert(vec![decl]);\n            }\n        }\n    }\n}\n\nimpl<'a> Traverse<'a, TraverseCtxState<'a>> for ModuleCompiler<'a, '_> {\n    fn enter_statement(&mut self, node: &mut Statement<'a>, _ctx: &mut TraverseCtx<'a>) {\n        self.statements.push(node.address());\n    }\n\n    fn exit_statement(&mut self, _node: &mut Statement<'a>, _ctx: &mut TraverseCtx<'a>) {\n        self.statements.pop();\n    }\n\n    fn exit_expression(&mut self, node: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {\n        match node {\n            Expression::CallExpression(expr) if self.options.oveo => {\n                // hoist render functions\n                // component(() => hoist(() => { .. }));\n                if let Some(IviSymbol::Component) = self.resolve(&expr.callee, ctx.scoping()) {\n                    if let Some(Argument::ArrowFunctionExpression(expr)) = expr.arguments.get_mut(0)\n                    {\n                        if expr.expression {\n                            if let Some(Statement::ExpressionStatement(expr_stmt)) =\n                                &mut expr.body.statements.get_mut(0)\n                            {\n                                expr_stmt.expression = oveo_intrinsic(\n                                    expr_stmt.expression.take_in(ctx.ast.allocator),\n                                    self.imports.hoist(ctx),\n                                    ctx,\n                                );\n                            }\n                        }\n                    }\n                }\n            }\n            Expression::TaggedTemplateExpression(expr) => {\n                if let Some(ivi) = self.resolve(&expr.tag, ctx.scoping()) {\n                    let kind = match ivi {\n                        IviSymbol::Html => TemplateKind::Html,\n                        IviSymbol::Svg => TemplateKind::Svg,\n                        _ => {\n                            return;\n                        }\n                    };\n                    match compile_template(\n                        &mut expr.quasi,\n                        ctx,\n                        kind,\n                        &mut self.imports,\n                        self.options.oveo,\n                        self.options.dedupe_strings,\n                    ) {\n                        Ok(result) => {\n                            for s in result.strings {\n                                self.strings.insert(s);\n                            }\n                            let address = self.statements[0];\n                            for decl in result.decl {\n                                self.add_template_decl(address, decl);\n                            }\n                            *node = result.expr;\n                        }\n                        Err(error) => {\n                            self.errors.push(error);\n                        }\n                    }\n                }\n            }\n            _ => {}\n        }\n    }\n\n    fn exit_import_declaration(\n        &mut self,\n        node: &mut ImportDeclaration<'a>,\n        _ctx: &mut TraverseCtx<'a>,\n    ) {\n        // Resolve ivi module\n        if let Some(specifiers) = &node.specifiers {\n            let source = &node.source;\n            if source.value != \"ivi\" {\n                return;\n            }\n\n            for spec in specifiers {\n                match spec {\n                    // import { imported } from \"source\"\n                    // import { imported as local } from \"source\"\n                    ImportDeclarationSpecifier::ImportSpecifier(spec) => {\n                        let s = match spec.imported.name().as_str() {\n                            \"component\" => IviSymbol::Component,\n                            \"html\" => IviSymbol::Html,\n                            \"svg\" => IviSymbol::Svg,\n                            _ => {\n                                continue;\n                            }\n                        };\n                        self.ivi_module.insert(spec.local.symbol_id(), s);\n                    }\n                    // import * as local from \"source\"\n                    ImportDeclarationSpecifier::ImportNamespaceSpecifier(spec) => {\n                        self.ivi_module.insert(spec.local.symbol_id(), IviSymbol::Module);\n                    }\n                    _ => {}\n                }\n            }\n        }\n    }\n\n    fn exit_program(&mut self, node: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {\n        let imports = self.imports.create_import_statements(ctx);\n        if !imports.is_empty() {\n            let index = node\n                .body\n                .iter()\n                .position(|stmt| !matches!(stmt, Statement::ImportDeclaration(_)))\n                .unwrap_or(node.body.len());\n\n            node.body.splice(index..index, imports);\n        }\n\n        if !self.templates.is_empty() {\n            let statements = &mut node.body;\n            let mut new_statements =\n                ctx.ast.vec_with_capacity(statements.len() + self.templates.len());\n\n            for stmt in statements.drain(..) {\n                if let Some(s) = self.templates.remove(&stmt.address()) {\n                    new_statements.extend(s.into_iter());\n                }\n                new_statements.push(stmt);\n            }\n\n            *statements = new_statements;\n        }\n    }\n}\n\n#[derive(Clone, Copy)]\nenum IviSymbol {\n    Module,\n    Component,\n    Html,\n    Svg,\n}\n"
  },
  {
    "path": "crates/ivi_compiler/src/oveo.rs",
    "content": "use oxc_ast::{NONE, ast::Expression};\nuse oxc_span::SPAN;\n\nuse crate::context::TraverseCtx;\n\n// annotation(expr)\npub fn oveo_intrinsic<'a>(\n    expr: Expression<'a>,\n    callee: Expression<'a>,\n    ctx: &mut TraverseCtx<'a>,\n) -> Expression<'a> {\n    ctx.ast.expression_call(SPAN, callee, NONE, ctx.ast.vec_from_array([expr.into()]), false)\n}\n"
  },
  {
    "path": "crates/ivi_compiler/src/tpl/emit.rs",
    "content": "use indexmap::IndexSet;\nuse oxc_allocator::{TakeIn, Vec as ArenaVec};\nuse oxc_ast::{\n    AstBuilder,\n    ast::{Expression, TemplateElement, TemplateElementValue},\n};\nuse oxc_span::SPAN;\n\nuse crate::{\n    context::TraverseCtx,\n    import::ImportSymbols,\n    oveo::oveo_intrinsic,\n    tpl::{\n        TemplateKind,\n        opcodes::{child_op, common_prop_type, prop_op, state_op, template_flags},\n        parser::{\n            TElement, TNode, TNodeKind, TProperty, TPropertyAttributeValue, TPropertyStyleValue,\n        },\n    },\n};\n\npub enum TemplateNode<'a> {\n    Block(TemplateBlock<'a>),\n    Text(String),\n    Expr(usize),\n}\n\npub struct TemplateBlock<'a> {\n    pub statics: Expression<'a>,\n    pub flags: u32,\n    pub props_op_codes: Vec<u32>,\n    pub child_op_codes: Vec<u32>,\n    pub state_op_codes: Vec<u32>,\n    pub strings: IndexSet<String>,\n    pub expressions: Vec<usize>,\n}\n\npub fn emit_root_element<'a>(\n    node: &TNode,\n    kind: TemplateKind,\n    expressions: &mut ArenaVec<'a, Expression<'a>>,\n    ctx: &mut TraverseCtx<'a>,\n    imports: &mut ImportSymbols<'a>,\n    oveo: bool,\n) -> TemplateNode<'a> {\n    match &node.kind {\n        TNodeKind::Element(e) => {\n            let statics = emit_static_template(e, expressions, &mut ctx.ast);\n\n            let expr_map = create_expr_map(e, ctx, expressions, imports, oveo);\n\n            let state_op_codes = emit_state_op_codes(e);\n            let (props_op_codes, strings) = emit_props_op_codes(node, &expr_map);\n            let child_op_codes = emit_child_op_codes(node, &expr_map);\n\n            let state_slots = count_state_slots(&state_op_codes);\n            let child_slots = count_child_slots(&child_op_codes);\n\n            let mut flags = state_slots | (child_slots << template_flags::CHILDREN_SIZE_SHIFT);\n            if let TemplateKind::Svg = kind {\n                flags |= template_flags::SVG;\n            }\n            TemplateNode::Block(TemplateBlock {\n                statics,\n                flags,\n                props_op_codes,\n                child_op_codes,\n                state_op_codes,\n                strings,\n                expressions: expr_map.iter().copied().collect(),\n            })\n        }\n        TNodeKind::Text(t) => TemplateNode::Text(t.value.clone()),\n        TNodeKind::Expr(e) => TemplateNode::Expr(e.index.inner()),\n    }\n}\n\nfn count_state_slots(op_codes: &[u32]) -> u32 {\n    let mut count = 1;\n    for op in op_codes {\n        if *op & state_op::SAVE != 0\n            || (*op & state_op::ENTER_OR_REMOVE != 0 && (op >> state_op::OFFSET_SHIFT) == 0)\n        {\n            count += 1\n        }\n    }\n    count\n}\n\nfn count_child_slots(op_codes: &[u32]) -> u32 {\n    let mut count = 0;\n    for op in op_codes {\n        if op & child_op::TYPE == child_op::CHILD {\n            count += 1;\n        }\n    }\n    count\n}\n\nfn create_expr_map<'a>(\n    root: &TElement,\n    ctx: &mut TraverseCtx<'a>,\n    expressions: &mut ArenaVec<'a, Expression<'a>>,\n    imports: &mut ImportSymbols<'a>,\n    oveo: bool,\n) -> IndexSet<usize> {\n    let mut map = IndexSet::default();\n    _create_expr_map(&mut map, root, ctx, expressions, imports, oveo);\n    map\n}\n\nfn _create_expr_map<'a>(\n    map: &mut IndexSet<usize>,\n    node: &TElement,\n    ctx: &mut TraverseCtx<'a>,\n    expressions: &mut ArenaVec<'a, Expression<'a>>,\n    imports: &mut ImportSymbols<'a>,\n    oveo: bool,\n) {\n    for p in &node.properties {\n        match p {\n            TProperty::Attribute(p) => {\n                if let TPropertyAttributeValue::Expr(v) = &p.value\n                    && !v.hoist\n                {\n                    map.insert(v.index.inner());\n                }\n            }\n            TProperty::Value(p) => {\n                map.insert(p.value.inner());\n            }\n            TProperty::DOMValue(p) => {\n                map.insert(p.value.inner());\n            }\n            TProperty::Style(p) => {\n                if let TPropertyStyleValue::Expr(v) = &p.value {\n                    map.insert(v.inner());\n                }\n            }\n            TProperty::Event(p) => {\n                let i = p.value.inner();\n                if oveo {\n                    expressions[i] = oveo_intrinsic(\n                        expressions[i].take_in(ctx.ast.allocator),\n                        imports.hoist(ctx),\n                        ctx,\n                    );\n                }\n                map.insert(i);\n            }\n            TProperty::Directive(p) => {\n                map.insert(p.inner());\n            }\n        }\n    }\n\n    for c in &node.children {\n        match &c.kind {\n            TNodeKind::Element(e) => {\n                _create_expr_map(map, e, ctx, expressions, imports, oveo);\n            }\n            TNodeKind::Expr(e) => {\n                map.insert(e.index.inner());\n            }\n            _ => {}\n        }\n    }\n}\n\nfn emit_static_template<'a>(\n    node: &TElement,\n    template_expressions: &mut ArenaVec<'a, Expression<'a>>,\n    ast: &mut AstBuilder<'a>,\n) -> Expression<'a> {\n    let mut static_part = String::new();\n    let mut quasis = ast.vec();\n    let mut expressions = ast.vec();\n    // Node doesn't have any children elements/texts or static properties\n    let mut is_simple_node = true;\n    _emit_static_template(\n        &mut is_simple_node,\n        node,\n        &mut static_part,\n        &mut quasis,\n        &mut expressions,\n        template_expressions,\n        ast,\n    );\n\n    if is_simple_node {\n        ast.expression_string_literal(SPAN, ast.atom(&node.tag), None)\n    } else {\n        quasis.push(ast.template_element(\n            SPAN,\n            TemplateElementValue { raw: ast.atom(&static_part), cooked: None },\n            true,\n            false,\n        ));\n\n        ast.expression_template_literal(SPAN, quasis, expressions)\n    }\n}\n\nfn _emit_static_template<'a>(\n    is_simple_node: &mut bool,\n    node: &TElement,\n    static_part: &mut String,\n    quasis: &mut ArenaVec<'a, TemplateElement<'a>>,\n    expressions: &mut ArenaVec<'a, Expression<'a>>,\n    template_expressions: &mut ArenaVec<'a, Expression<'a>>,\n    ast: &mut AstBuilder<'a>,\n) {\n    static_part.push('<');\n    static_part.push_str(&node.tag);\n\n    let mut style = String::new();\n    for p in &node.properties {\n        match p {\n            TProperty::Attribute(p) => match &p.value {\n                TPropertyAttributeValue::String(v) => {\n                    if p.key == \"style\" {\n                        if style.is_empty() {\n                            style = v.clone();\n                        } else {\n                            style.push(';');\n                            style.push_str(v);\n                        }\n                    } else {\n                        *is_simple_node = false;\n                        static_part.push(' ');\n                        static_part.push_str(&p.key);\n                        static_part.push_str(\"=\\\"\");\n                        static_part.push_str(v);\n                        static_part.push('\"');\n                    }\n                }\n                TPropertyAttributeValue::Bool => {\n                    *is_simple_node = false;\n                    static_part.push(' ');\n                    static_part.push_str(&p.key);\n                }\n                TPropertyAttributeValue::Expr(v) => {\n                    if v.hoist {\n                        *is_simple_node = false;\n                        static_part.push(' ');\n                        static_part.push_str(&p.key);\n                        static_part.push_str(\"=\\\"\");\n                        quasis.push(ast.template_element(\n                            SPAN,\n                            TemplateElementValue { raw: ast.atom(static_part), cooked: None },\n                            false,\n                            false,\n                        ));\n                        expressions\n                            .push(template_expressions[v.index.inner()].take_in(ast.allocator));\n                        static_part.clear();\n                        static_part.push('\"');\n                    }\n                }\n            },\n            TProperty::Style(p) => {\n                if let TPropertyStyleValue::String(v) = &p.value {\n                    if !style.is_empty() {\n                        style.push(';');\n                    }\n                    style.push_str(&p.key);\n                    style.push(':');\n                    style.push_str(v);\n                }\n            }\n            _ => {}\n        }\n    }\n    if !style.is_empty() {\n        *is_simple_node = false;\n        static_part.push_str(\" style=\\\"\");\n        static_part.push_str(&style);\n        static_part.push('\"');\n    }\n    static_part.push('>');\n    if node.void {\n        return;\n    }\n\n    let mut siblings_state = 0;\n    for c in &node.children {\n        match &c.kind {\n            TNodeKind::Element(c) => {\n                *is_simple_node = false;\n                _emit_static_template(\n                    is_simple_node,\n                    c,\n                    static_part,\n                    quasis,\n                    expressions,\n                    template_expressions,\n                    ast,\n                );\n                siblings_state = 0;\n            }\n            TNodeKind::Text(n) => {\n                *is_simple_node = false;\n                if (siblings_state & 3) == 3 {\n                    static_part.push_str(\"<!>\");\n                }\n                siblings_state = 1;\n                static_part.push_str(&n.value);\n            }\n            TNodeKind::Expr(_) => {\n                siblings_state |= 2;\n            }\n        }\n    }\n\n    static_part.push_str(\"</\");\n    static_part.push_str(&node.tag);\n    static_part.push('>');\n}\n\nfn emit_props_op_codes(node: &TNode, expr_map: &IndexSet<usize>) -> (Vec<u32>, IndexSet<String>) {\n    let mut op_codes = Vec::new();\n    let mut strings = IndexSet::new();\n    _emit_props_op_codes(&mut op_codes, node, true, &mut strings, expr_map);\n    (op_codes, strings)\n}\nfn _emit_props_op_codes(\n    op_codes: &mut Vec<u32>,\n    node: &TNode,\n    is_root: bool,\n    strings: &mut IndexSet<String>,\n    expr_map: &IndexSet<usize>,\n) {\n    fn string_index(strings: &mut IndexSet<String>, key: &str) -> u32 {\n        if let Some(i) = strings.get_index_of(key) {\n            i as u32\n        } else {\n            let (i, _) = strings.insert_full(key.to_string());\n            i as u32\n        }\n    }\n    if let TNodeKind::Element(e) = &node.kind {\n        if node.props_exprs > 0 {\n            if !is_root {\n                op_codes\n                    .push(prop_op::SET_NODE | ((node.state_index as u32) << prop_op::DATA_SHIFT));\n            }\n            for p in &e.properties {\n                match p {\n                    TProperty::Attribute(p) => {\n                        if let TPropertyAttributeValue::Expr(expr) = &p.value {\n                            if let Some(i) = expr_map.get_index_of(&expr.index.inner()) {\n                                if p.key == \"class\" {\n                                    op_codes.push(\n                                        prop_op::COMMON\n                                            | (common_prop_type::CLASS_NAME << prop_op::DATA_SHIFT)\n                                            | ((i as u32) << prop_op::INPUT_SHIFT),\n                                    );\n                                } else {\n                                    op_codes.push(\n                                        prop_op::ATTRIBUTE\n                                            | (string_index(strings, &p.key)\n                                                << prop_op::DATA_SHIFT)\n                                            | ((i as u32) << prop_op::INPUT_SHIFT),\n                                    );\n                                }\n                            }\n                        }\n                    }\n                    TProperty::Value(p) => {\n                        match p.key.as_str() {\n                            \"textContent\" => {\n                                op_codes.push(\n                                    prop_op::COMMON\n                                        | (common_prop_type::TEXT_CONTENT << prop_op::DATA_SHIFT)\n                                        | ((p.value.inner() as u32) << prop_op::INPUT_SHIFT),\n                                );\n                            }\n                            \"innerHTML\" => {\n                                op_codes.push(\n                                    prop_op::COMMON\n                                        | (common_prop_type::INNER_HTML << prop_op::DATA_SHIFT)\n                                        | ((p.value.inner() as u32) << prop_op::INPUT_SHIFT),\n                                );\n                            }\n                            _ => {\n                                if let Some(i) = expr_map.get_index_of(&p.value.inner()) {\n                                    op_codes.push(\n                                        prop_op::PROPERTY\n                                            | (string_index(strings, &p.key)\n                                                << prop_op::DATA_SHIFT)\n                                            | ((i as u32) << prop_op::INPUT_SHIFT),\n                                    );\n                                }\n                            }\n                        };\n                    }\n                    TProperty::DOMValue(p) => {\n                        match p.key.as_str() {\n                            \"textContent\" => {\n                                op_codes.push(\n                                    prop_op::COMMON\n                                        | (common_prop_type::TEXT_CONTENT << prop_op::DATA_SHIFT)\n                                        | ((p.value.inner() as u32) << prop_op::INPUT_SHIFT),\n                                );\n                            }\n                            \"innerHTML\" => {\n                                op_codes.push(\n                                    prop_op::COMMON\n                                        | (common_prop_type::INNER_HTML << prop_op::DATA_SHIFT)\n                                        | ((p.value.inner() as u32) << prop_op::INPUT_SHIFT),\n                                );\n                            }\n                            _ => {\n                                if let Some(i) = expr_map.get_index_of(&p.value.inner()) {\n                                    op_codes.push(\n                                        prop_op::DIFF_DOM_PROPERTY\n                                            | (string_index(strings, &p.key)\n                                                << prop_op::DATA_SHIFT)\n                                            | ((i as u32) << prop_op::INPUT_SHIFT),\n                                    );\n                                }\n                            }\n                        };\n                    }\n                    TProperty::Style(p) => {\n                        if let TPropertyStyleValue::Expr(expr_index) = &p.value {\n                            if let Some(i) = expr_map.get_index_of(&expr_index.inner()) {\n                                op_codes.push(\n                                    prop_op::STYLE\n                                        | (string_index(strings, &p.key) << prop_op::DATA_SHIFT)\n                                        | ((i as u32) << prop_op::INPUT_SHIFT),\n                                );\n                            }\n                        }\n                    }\n                    TProperty::Event(p) => {\n                        if let Some(i) = expr_map.get_index_of(&p.value.inner()) {\n                            op_codes.push(\n                                prop_op::EVENT\n                                    | (string_index(strings, &p.key) << prop_op::DATA_SHIFT)\n                                    | ((i as u32) << prop_op::INPUT_SHIFT),\n                            );\n                        }\n                    }\n                    TProperty::Directive(p) => {\n                        if let Some(i) = expr_map.get_index_of(&p.inner()) {\n                            op_codes\n                                .push(prop_op::DIRECTIVE | ((i as u32) << prop_op::INPUT_SHIFT));\n                        }\n                    }\n                }\n            }\n        }\n\n        for c in &e.children {\n            _emit_props_op_codes(op_codes, c, false, strings, expr_map);\n        }\n    }\n}\n\nfn emit_state_op_codes(node: &TElement) -> Vec<u32> {\n    let mut op_codes = Vec::new();\n    _emit_state_op_codes(&mut op_codes, node);\n    op_codes\n}\n\nfn _emit_state_op_codes(op_codes: &mut Vec<u32>, node: &TElement) {\n    mod state_flags {\n        pub const PREV_TEXT: u32 = 1;\n        pub const PREV_EXPR: u32 = 1 << 1;\n    }\n    let mut state = 0;\n    'outer: for c in &node.children {\n        match &c.kind {\n            TNodeKind::Element(e) => {\n                let mut op = 0;\n                if state & state_flags::PREV_EXPR != 0 || c.children_exprs > 0 || c.props_exprs > 0\n                {\n                    op = state_op::SAVE;\n                }\n\n                let current_op_index = op_codes.len();\n                op_codes.push(op);\n\n                if c.flags & TNode::HAS_EXPRESSIONS != 0 {\n                    _emit_state_op_codes(op_codes, e);\n                    let children_offset = op_codes.len() - (current_op_index + 1);\n                    if children_offset > 0 {\n                        op |= state_op::ENTER_OR_REMOVE\n                            | ((children_offset as u32) << state_op::OFFSET_SHIFT);\n                        op_codes[current_op_index] = op;\n                    }\n                }\n                if c.flags & (TNode::HAS_NEXT_EXPRESSION | TNode::HAS_NEXT_DOM_NODE)\n                    != (TNode::HAS_NEXT_EXPRESSION | TNode::HAS_NEXT_DOM_NODE)\n                {\n                    if op == 0 {\n                        op_codes.pop();\n                    }\n                    break 'outer;\n                }\n                state = 0;\n            }\n            TNodeKind::Text(_) => {\n                if state & (state_flags::PREV_TEXT | state_flags::PREV_EXPR)\n                    == (state_flags::PREV_TEXT | state_flags::PREV_EXPR)\n                {\n                    op_codes.push(state_op::ENTER_OR_REMOVE);\n                } else if state & state_flags::PREV_EXPR != 0 {\n                    op_codes.push(state_op::SAVE);\n                } else if c.flags & (TNode::HAS_NEXT_EXPRESSION | TNode::HAS_NEXT_DOM_NODE)\n                    != (TNode::HAS_NEXT_EXPRESSION | TNode::HAS_NEXT_DOM_NODE)\n                {\n                    break 'outer;\n                } else {\n                    op_codes.push(0);\n                }\n                state = state_flags::PREV_TEXT;\n            }\n            TNodeKind::Expr(_) => {\n                state |= state_flags::PREV_EXPR;\n            }\n        }\n    }\n}\n\nfn emit_child_op_codes(node: &TNode, expr_map: &IndexSet<usize>) -> Vec<u32> {\n    let mut op_codes = Vec::new();\n    _emit_child_op_codes(&mut op_codes, node, true, expr_map);\n    op_codes\n}\n\nfn _emit_child_op_codes(\n    op_codes: &mut Vec<u32>,\n    node: &TNode,\n    is_root: bool,\n    expr_map: &IndexSet<usize>,\n) {\n    if let TNodeKind::Element(e) = &node.kind {\n        if node.children_exprs > 0 {\n            if !is_root {\n                op_codes.push(\n                    child_op::SET_PARENT | ((node.state_index as u32) << child_op::VALUE_SHIFT),\n                );\n            }\n            let mut prev_state_index = None;\n            let mut prev_expr = false;\n            for c in e.children.iter().rev() {\n                if let TNodeKind::Expr(expr_index) = &c.kind {\n                    if let Some(prev_state_index) = prev_state_index\n                        && !prev_expr\n                    {\n                        op_codes.push(\n                            child_op::SET_NEXT\n                                | ((prev_state_index as u32) << child_op::VALUE_SHIFT),\n                        );\n                    }\n                    op_codes.push(\n                        child_op::CHILD\n                            | ((expr_map.get_index_of(&expr_index.index.inner()).unwrap() as u32)\n                                << child_op::VALUE_SHIFT),\n                    );\n                    prev_expr = true;\n                } else {\n                    prev_expr = false;\n                    prev_state_index = Some(c.state_index);\n                }\n            }\n        }\n        for c in e.children.iter().rev() {\n            _emit_child_op_codes(op_codes, c, false, expr_map);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/ivi_compiler/src/tpl/html.rs",
    "content": "pub fn is_html_void_element(tag: &str) -> bool {\n    matches!(\n        tag,\n        \"audio\"\n            | \"video\"\n            | \"embed\"\n            | \"input\"\n            | \"param\"\n            | \"source\"\n            | \"track\"\n            | \"area\"\n            | \"base\"\n            | \"link\"\n            | \"meta\"\n            | \"br\"\n            | \"col\"\n            | \"hr\"\n            | \"img\"\n            | \"wbr\"\n    )\n}\n"
  },
  {
    "path": "crates/ivi_compiler/src/tpl/mod.rs",
    "content": "use indexmap::IndexSet;\nuse oxc_allocator::TakeIn;\nuse oxc_ast::{NONE, ast::*};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_semantic::SymbolFlags;\nuse oxc_span::SPAN;\n\nuse crate::{\n    context::TraverseCtx, import::ImportSymbols, oveo::oveo_intrinsic, tpl::emit::TemplateNode,\n};\n\nmod emit;\nmod html;\npub mod opcodes;\nmod parser;\n\npub struct CompiledTemplate<'a> {\n    pub decl: Vec<Statement<'a>>,\n    pub expr: Expression<'a>,\n    pub strings: Vec<String>,\n}\n\n#[derive(Clone, Copy)]\npub enum TemplateKind {\n    Html,\n    Svg,\n}\n\npub fn compile_template<'a>(\n    tpl: &mut TemplateLiteral<'a>,\n    ctx: &mut TraverseCtx<'a>,\n    kind: TemplateKind,\n    imports: &mut ImportSymbols<'a>,\n    oveo: bool,\n    dedupe_strings: bool,\n) -> Result<CompiledTemplate<'a>, OxcDiagnostic> {\n    let mut decl = Vec::new();\n    let mut exprs = Vec::new();\n    let mut strings = Vec::new();\n    let nodes = parser::parse_template(tpl, ctx.scoping())?;\n\n    for n in &nodes {\n        let e = emit::emit_root_element(n, kind, &mut tpl.expressions, ctx, imports, oveo);\n        match e {\n            TemplateNode::Block(t) => {\n                let uid = ctx.generate_uid_in_root_scope(\"_TPL_\", SymbolFlags::ConstVariable);\n\n                // const _TPL_ = __IVI_TPL__(_T(statics, ..opcodes));\n                let statics = if let Expression::StringLiteral(_) = t.statics {\n                    ctx.ast.expression_call(\n                        SPAN,\n                        match kind {\n                            TemplateKind::Html => imports.html_element(ctx),\n                            TemplateKind::Svg => imports.svg_element(ctx),\n                        },\n                        NONE,\n                        ctx.ast.vec1(t.statics.into()),\n                        false,\n                    )\n                } else {\n                    ctx.ast.expression_call(\n                        SPAN,\n                        match kind {\n                            TemplateKind::Html => imports.html_template(ctx),\n                            TemplateKind::Svg => imports.svg_template(ctx),\n                        },\n                        NONE,\n                        ctx.ast.vec1(t.statics.into()),\n                        false,\n                    )\n                };\n                let statics =\n                    if oveo { oveo_intrinsic(statics, imports.dedupe(ctx), ctx) } else { statics };\n\n                let mut arguments = ctx.ast.vec_with_capacity(6);\n                arguments.push(statics.into());\n                arguments.push(\n                    ctx.ast\n                        .expression_numeric_literal(SPAN, t.flags as f64, None, NumberBase::Decimal)\n                        .into(),\n                );\n                arguments\n                    .push(op_codes_into_expression(&t.props_op_codes, ctx, imports, oveo).into());\n                arguments\n                    .push(op_codes_into_expression(&t.child_op_codes, ctx, imports, oveo).into());\n                arguments\n                    .push(op_codes_into_expression(&t.state_op_codes, ctx, imports, oveo).into());\n                if !t.strings.is_empty() {\n                    arguments.push(strings_into_expression(&t.strings, ctx).into());\n                    strings.extend(t.strings.into_iter());\n                }\n                let template_descriptor = ctx.ast.expression_call(\n                    SPAN,\n                    imports.template_descriptor(ctx),\n                    NONE,\n                    arguments,\n                    false,\n                );\n                let v = ctx.ast.declaration_variable(\n                    SPAN,\n                    VariableDeclarationKind::Const,\n                    ctx.ast.vec1(ctx.ast.variable_declarator(\n                        SPAN,\n                        VariableDeclarationKind::Const,\n                        ctx.ast.binding_pattern_binding_identifier(SPAN, uid.name),\n                        NONE,\n                        Some(if dedupe_strings {\n                            ctx.ast.expression_call(\n                                SPAN,\n                                ctx.ast.expression_identifier(SPAN, ctx.ast.atom(\"__IVI_TPL__\")),\n                                NONE,\n                                ctx.ast.vec_from_array([template_descriptor.into()]),\n                                false,\n                            )\n                        } else {\n                            template_descriptor\n                        }),\n                        false,\n                    )),\n                    false,\n                );\n                decl.push(v.into());\n\n                // _t(_TPL_, [expressions])\n                let call_expressions = if t.expressions.is_empty() {\n                    ctx.ast.vec_from_array([uid.create_read_expression(ctx).into()])\n                } else {\n                    ctx.ast.vec_from_array([\n                        uid.create_read_expression(ctx).into(),\n                        ctx.ast\n                            .expression_array(\n                                SPAN,\n                                ctx.ast.vec_from_iter(t.expressions.iter().map(|i| {\n                                    tpl.expressions[*i].take_in(ctx.ast.allocator).into()\n                                })),\n                            )\n                            .into(),\n                    ])\n                };\n                let call = ctx.ast.expression_call(\n                    SPAN,\n                    imports.create_from_template(ctx),\n                    NONE,\n                    call_expressions,\n                    false,\n                );\n                exprs.push(call);\n            }\n            TemplateNode::Text(text) => {\n                exprs.push(ctx.ast.expression_string_literal(SPAN, ctx.ast.atom(&text), None));\n            }\n            TemplateNode::Expr(i) => {\n                exprs.push(tpl.expressions[i].take_in(ctx.ast.allocator));\n            }\n        }\n    }\n\n    let expr = if exprs.len() > 1 {\n        ctx.ast.expression_array(SPAN, ctx.ast.vec_from_iter(exprs.into_iter().map(|e| e.into())))\n    } else {\n        exprs.pop().unwrap()\n    };\n    Ok(CompiledTemplate { decl, expr, strings })\n}\n\nfn op_codes_into_expression<'a>(\n    op_codes: &[u32],\n    ctx: &mut TraverseCtx<'a>,\n    imports: &mut ImportSymbols<'a>,\n    oveo: bool,\n) -> Expression<'a> {\n    if op_codes.is_empty() {\n        imports.empty_array(ctx)\n    } else {\n        let expr = ctx.ast.expression_array(\n            SPAN,\n            ctx.ast.vec_from_iter(op_codes.iter().map(|o| {\n                ctx.ast\n                    .expression_numeric_literal(SPAN, *o as f64, None, NumberBase::Decimal)\n                    .into()\n            })),\n        );\n        if oveo { oveo_intrinsic(expr, imports.dedupe(ctx), ctx) } else { expr }\n    }\n}\n\nfn strings_into_expression<'a>(\n    strings: &IndexSet<String>,\n    ctx: &mut TraverseCtx<'a>,\n) -> Expression<'a> {\n    ctx.ast.expression_array(\n        SPAN,\n        ctx.ast.vec_from_iter(\n            strings\n                .iter()\n                .map(|s| ctx.ast.expression_string_literal(SPAN, ctx.ast.atom(s), None).into()),\n        ),\n    )\n}\n"
  },
  {
    "path": "crates/ivi_compiler/src/tpl/opcodes.rs",
    "content": "pub mod template_flags {\n    pub const CHILDREN_SIZE_SHIFT: u32 = 6;\n    pub const SVG: u32 = 1 << 12;\n}\n\npub mod state_op {\n    pub const SAVE: u32 = 0b01;\n    pub const ENTER_OR_REMOVE: u32 = 0b10;\n    pub const OFFSET_SHIFT: u32 = 2;\n}\n\npub mod common_prop_type {\n    pub const CLASS_NAME: u32 = 0;\n    pub const TEXT_CONTENT: u32 = 1;\n    pub const INNER_HTML: u32 = 2;\n}\n\npub mod prop_op {\n    pub const SET_NODE: u32 = 0;\n    pub const COMMON: u32 = 1;\n    pub const ATTRIBUTE: u32 = 2;\n    pub const PROPERTY: u32 = 3;\n    pub const DIFF_DOM_PROPERTY: u32 = 4;\n    pub const STYLE: u32 = 5;\n    pub const EVENT: u32 = 6;\n    pub const DIRECTIVE: u32 = 7;\n    pub const TYPE_MASK: u32 = 0b111;\n    pub const INPUT_SHIFT: u32 = 3;\n    pub const DATA_SHIFT: u32 = 9;\n}\n\npub mod child_op {\n    pub const CHILD: u32 = 0b00;\n    pub const SET_NEXT: u32 = 0b01;\n    pub const SET_PARENT: u32 = 0b11;\n    pub const TYPE: u32 = 0b11;\n    pub const VALUE_SHIFT: u32 = 2;\n}\n"
  },
  {
    "path": "crates/ivi_compiler/src/tpl/parser.rs",
    "content": "use oxc_ast::ast::{Expression, TemplateElement, TemplateLiteral};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_semantic::Scoping;\n\nuse crate::tpl::html::is_html_void_element;\n\n#[derive(Clone, Copy)]\npub struct ExprIndex(usize);\n\nimpl ExprIndex {\n    pub fn inner(self) -> usize {\n        self.0\n    }\n}\n\npub struct THoistableExpr {\n    pub index: ExprIndex,\n    pub hoist: bool,\n}\n\npub struct TNode {\n    pub kind: TNodeKind,\n    pub flags: u8,\n    pub state_index: u16,\n    pub children_exprs: u16,\n    pub props_exprs: u16,\n}\n\npub enum TNodeKind {\n    Element(TElement),\n    Text(TText),\n    Expr(TExpr),\n}\n\nimpl TNode {\n    pub const HAS_EXPRESSIONS: u8 = 1;\n    pub const HAS_NEXT_EXPRESSION: u8 = 1 << 1;\n    pub const HAS_NEXT_DOM_NODE: u8 = 1 << 2;\n\n    fn new(kind: TNodeKind) -> Self {\n        Self { kind, flags: 0, state_index: 0, children_exprs: 0, props_exprs: 0 }\n    }\n\n    fn text(text: &str) -> Self {\n        Self {\n            kind: TNodeKind::Text(TText { value: text.to_string() }),\n            flags: 0,\n            state_index: 0,\n            children_exprs: 0,\n            props_exprs: 0,\n        }\n    }\n\n    fn space_text() -> Self {\n        Self::text(\" \")\n    }\n}\n\npub struct TElement {\n    pub tag: String,\n    pub properties: Vec<TProperty>,\n    pub children: Vec<TNode>,\n    pub void: bool,\n}\n\npub struct TText {\n    pub value: String,\n}\n\npub struct TExpr {\n    pub index: ExprIndex,\n}\n\npub enum TProperty {\n    Attribute(TPropertyAttribute),\n    Value(TPropertyValue),\n    DOMValue(TPropertyDOMValue),\n    Style(TPropertyStyle),\n    Event(TPropertyEvent),\n    Directive(ExprIndex),\n}\n\npub struct TPropertyAttribute {\n    pub key: String,\n    pub value: TPropertyAttributeValue,\n}\n\npub enum TPropertyAttributeValue {\n    String(String),\n    Bool,\n    Expr(THoistableExpr),\n}\n\npub struct TPropertyValue {\n    pub key: String,\n    pub value: ExprIndex,\n}\n\npub struct TPropertyDOMValue {\n    pub key: String,\n    pub value: ExprIndex,\n}\n\npub struct TPropertyStyle {\n    pub key: String,\n    pub value: TPropertyStyleValue,\n}\n\npub enum TPropertyStyleValue {\n    String(String),\n    Expr(ExprIndex),\n}\n\npub struct TPropertyEvent {\n    pub key: String,\n    pub value: ExprIndex,\n}\n\npub fn parse_template<'a>(\n    tpl: &'a TemplateLiteral,\n    scoping: &'a Scoping,\n) -> Result<Vec<TNode>, OxcDiagnostic> {\n    let mut parser = Parser::new(scoping, &tpl.quasis, &tpl.expressions);\n    let mut nodes = parser.parse_children_list()?;\n    for n in &mut nodes {\n        update_flags(n);\n        assign_state_slots(n);\n    }\n    Ok(nodes)\n}\n\n#[derive(Debug, Clone)]\nstruct Parser<'a> {\n    scoping: &'a Scoping,\n    quasis: &'a [TemplateElement<'a>],\n    expressions: &'a [Expression<'a>],\n    text: &'a str,\n    expr_cursor: usize,\n}\n\nimpl<'a> Parser<'a> {\n    fn new(\n        scoping: &'a Scoping,\n        quasis: &'a [TemplateElement<'a>],\n        expressions: &'a [Expression<'a>],\n    ) -> Self {\n        Self {\n            scoping,\n            quasis,\n            expressions,\n            text: quasis[0].value.cooked.unwrap().as_str(),\n            expr_cursor: 0,\n        }\n    }\n\n    fn current_element(&self) -> &TemplateElement<'a> {\n        &self.quasis[self.expr_cursor]\n    }\n\n    fn is_end(&self) -> bool {\n        self.text.is_empty() && self.expr_cursor == self.expressions.len()\n    }\n\n    fn peek_char(&self) -> Option<char> {\n        self.text.chars().next()\n    }\n\n    fn peek_nth_char(&self, n: usize) -> Option<char> {\n        self.text.chars().nth(n)\n    }\n\n    fn try_consume_char(&mut self, expected: char) -> Option<char> {\n        if let Some(c) = self.text.chars().next()\n            && c == expected\n        {\n            self.text = &self.text[expected.len_utf8()..];\n            Some(expected)\n        } else {\n            None\n        }\n    }\n\n    fn consume_char(&mut self, expected: char) -> Result<(), OxcDiagnostic> {\n        if let Some(c) = self.text.chars().next()\n            && c == expected\n        {\n            self.text = &self.text[expected.len_utf8()..];\n            Ok(())\n        } else {\n            let parts = self.text.split_at(self.text.len().min(10));\n            Err(OxcDiagnostic::error(format!(\"Expected a '{expected}' char: {}\", parts.0))\n                .with_label(self.current_element().span))\n        }\n    }\n\n    fn advance(&mut self, i: usize) {\n        self.text = &self.text[i..];\n    }\n\n    fn consume_expr(&mut self) -> Result<usize, OxcDiagnostic> {\n        if self.text.is_empty() && (self.expr_cursor) < self.expressions.len() {\n            let i = self.expr_cursor;\n            self.expr_cursor += 1;\n            self.text = self.current_element().value.cooked.unwrap().as_str();\n            Ok(i)\n        } else {\n            Err(OxcDiagnostic::error(\"Expected an expression\")\n                .with_label(self.current_element().span))\n        }\n    }\n\n    fn consume_whitespace(&mut self) -> WhitespaceState {\n        let mut state = 0;\n        let mut len = self.text.len();\n        for (i, c) in self.text.char_indices() {\n            match c {\n                ' ' | '\\t' => {\n                    state |= WhitespaceState::WHITESPACE;\n                }\n                '\\n' | '\\r' => {\n                    state |= WhitespaceState::WHITESPACE | WhitespaceState::CONTAINS_NEWLINE;\n                }\n                '\\x0b' => {\n                    state |= WhitespaceState::WHITESPACE | WhitespaceState::CONTAINS_VERTICAL_TAB;\n                }\n                _ => {\n                    len = i;\n                    break;\n                }\n            }\n        }\n        if len == 0 {\n            WhitespaceState(0)\n        } else {\n            self.advance(len);\n            WhitespaceState(state)\n        }\n    }\n\n    fn parse_tag_name(&mut self) -> Result<String, OxcDiagnostic> {\n        let mut len = self.text.len();\n        for (i, c) in self.text.char_indices() {\n            match c {\n                '0'..='9' | 'a'..='z' | 'A'..='Z' | '_' => {}\n                '-' => {\n                    if i == 0 {\n                        len = 0;\n                        break;\n                    }\n                }\n                _ => {\n                    len = i;\n                    break;\n                }\n            }\n        }\n        if len > 0 {\n            let id = self.text[..len].to_string();\n            self.advance(len);\n            Ok(id)\n        } else {\n            let parts = self.text.split_at(self.text.len().min(10));\n            Err(OxcDiagnostic::error(format!(\"Invalid tag name: {}\", parts.0))\n                .with_label(self.current_element().span))\n        }\n    }\n\n    fn parse_attribute_name(&mut self) -> Result<String, OxcDiagnostic> {\n        let mut len = self.text.len();\n        for (i, c) in self.text.char_indices() {\n            match c {\n                '0'..='9' | 'a'..='z' | 'A'..='Z' | '_' => {}\n                '-' => {\n                    if i == 0 {\n                        len = 0;\n                        break;\n                    }\n                }\n                _ => {\n                    len = i;\n                    break;\n                }\n            }\n        }\n        if len > 0 {\n            let id = self.text[..len].to_string();\n            self.advance(len);\n            Ok(id)\n        } else {\n            let parts = self.text.split_at(self.text.len().min(10));\n            Err(OxcDiagnostic::error(format!(\"Invalid attribute name: {}\", parts.0))\n                .with_label(self.current_element().span))\n        }\n    }\n\n    fn parse_js_property(&mut self) -> Result<String, OxcDiagnostic> {\n        let mut len = self.text.len();\n        for (i, c) in self.text.char_indices() {\n            match c {\n                '0'..='9' | 'a'..='z' | 'A'..='Z' | '_' | '$' => {}\n                _ => {\n                    len = i;\n                    break;\n                }\n            }\n        }\n        if len > 0 {\n            let id = self.text[..len].to_string();\n            self.advance(len);\n            Ok(id)\n        } else {\n            let parts = self.text.split_at(self.text.len().min(10));\n            Err(OxcDiagnostic::error(format!(\"Invalid property name: {}\", parts.0))\n                .with_label(self.current_element().span))\n        }\n    }\n\n    fn parse_style_name(&mut self) -> Result<String, OxcDiagnostic> {\n        let mut len = self.text.len();\n        for (i, c) in self.text.char_indices() {\n            match c {\n                '0'..='9' | 'a'..='z' | 'A'..='Z' | '-' | '_' => {}\n                _ => {\n                    len = i;\n                    break;\n                }\n            }\n        }\n        if len > 0 {\n            let id = self.text[..len].to_string();\n            self.advance(len);\n            Ok(id)\n        } else {\n            let parts = self.text.split_at(self.text.len().min(10));\n            Err(OxcDiagnostic::error(format!(\"Invalid style name: {}\", parts.0))\n                .with_label(self.current_element().span))\n        }\n    }\n\n    fn parse_children_list(&mut self) -> Result<Vec<TNode>, OxcDiagnostic> {\n        let mut children = Vec::new();\n        let mut whitespace_state = self.consume_whitespace();\n        while !self.is_end() {\n            if let Some(c) = self.peek_char() {\n                if c == '<' {\n                    if whitespace_state.should_insert_whitespace() {\n                        children.push(TNode::space_text());\n                    }\n                    match self.peek_nth_char(1) {\n                        Some('/') => {\n                            break;\n                        }\n                        Some('!') => {\n                            self.parse_comment();\n                        }\n                        _ => {\n                            children.push(TNode::new(TNodeKind::Element(self.parse_element()?)));\n                        }\n                    }\n                } else {\n                    children.push(TNode::new(TNodeKind::Text(self.parse_text(whitespace_state)?)));\n                }\n            } else {\n                let index = self.consume_expr()?;\n                if whitespace_state.should_insert_whitespace() {\n                    children.push(TNode::space_text());\n                }\n                children.push(TNode::new(TNodeKind::Expr(TExpr { index: ExprIndex(index) })));\n            }\n\n            whitespace_state = self.consume_whitespace();\n        }\n        Ok(children)\n    }\n\n    fn parse_comment(&mut self) {\n        let mut len = self.text.len();\n        for (i, c) in self.text.char_indices() {\n            if c == '>' {\n                len = i + 1;\n                break;\n            }\n        }\n        if len > 0 {\n            self.advance(len);\n        }\n    }\n\n    fn parse_element(&mut self) -> Result<TElement, OxcDiagnostic> {\n        self.advance(1);\n        let tag = self.parse_tag_name()?;\n        self.consume_whitespace();\n        let properties = self.parse_attributes()?;\n\n        let mut children = Vec::new();\n        let mut void = false;\n        if self.try_consume_char('/').is_some() {\n            self.consume_char('>')?;\n        } else {\n            self.consume_char('>')?;\n\n            if is_html_void_element(&tag) {\n                void = true;\n            } else {\n                children = self.parse_children_list()?;\n                self.consume_char('<')?;\n                self.consume_char('/')?;\n                self.advance(tag.len());\n                self.consume_whitespace();\n                self.consume_char('>')?;\n            }\n        }\n        Ok(TElement { tag, properties, children, void })\n    }\n\n    fn parse_text(&mut self, whitespace_state: WhitespaceState) -> Result<TText, OxcDiagnostic> {\n        let mut text = String::new();\n        let mut len = self.text.len();\n        let mut whitespace_state = whitespace_state;\n        for (i, c) in self.text.char_indices() {\n            match c {\n                '<' => {\n                    len = i;\n                    break;\n                }\n                ' ' | '\\t' => {\n                    whitespace_state.0 |= WhitespaceState::WHITESPACE;\n                    continue;\n                }\n                '\\n' | '\\r' => {\n                    whitespace_state.0 |= WhitespaceState::CONTAINS_NEWLINE;\n                    continue;\n                }\n                '\\x0b' => {\n                    whitespace_state.0 |= WhitespaceState::CONTAINS_VERTICAL_TAB;\n                    continue;\n                }\n                _ => {}\n            }\n            if whitespace_state.0 & WhitespaceState::WHITESPACE != 0\n                && (whitespace_state.0\n                    & (WhitespaceState::TEXT_CONTENT | WhitespaceState::CONTAINS_VERTICAL_TAB)\n                    != 0\n                    || whitespace_state.0 & WhitespaceState::CONTAINS_NEWLINE == 0)\n            {\n                text.push(' ');\n            }\n            whitespace_state.0 = WhitespaceState::TEXT_CONTENT;\n            text.push(c);\n        }\n        if whitespace_state.should_insert_whitespace() {\n            text.push(' ');\n        }\n        self.advance(len);\n        if text.len() <= (1 << 16) {\n            Ok(TText { value: text })\n        } else {\n            // Text nodes are splitted into two nodes when they exceed their length limit (64k).\n            // https://github.com/chromium/chromium/blob/91159249db3086f17b28b7a060f55ec0345c24c7/third_party/blink/renderer/core/dom/text.h#L42\n            Err(OxcDiagnostic::error(\"Text is too long (>64Kb)\")\n                .with_label(self.current_element().span))\n        }\n    }\n\n    fn parse_attributes(&mut self) -> Result<Vec<TProperty>, OxcDiagnostic> {\n        let mut properties = Vec::new();\n\n        while !self.is_end() {\n            if let Some(c) = self.peek_char() {\n                match c {\n                    '/' | '>' => {\n                        return Ok(properties);\n                    }\n                    '.' => {\n                        self.advance(1);\n                        let key = self.parse_js_property()?;\n                        self.consume_char('=')?;\n                        let expr_index = self.consume_expr()?;\n                        properties.push(TProperty::Value(TPropertyValue {\n                            key,\n                            value: ExprIndex(expr_index),\n                        }));\n                    }\n                    '*' => {\n                        self.advance(1);\n                        let key = self.parse_js_property()?;\n                        self.consume_char('=')?;\n                        let expr_index = self.consume_expr()?;\n                        properties.push(TProperty::DOMValue(TPropertyDOMValue {\n                            key,\n                            value: ExprIndex(expr_index),\n                        }));\n                    }\n                    '@' => {\n                        self.advance(1);\n                        let key = self.parse_js_property()?;\n                        self.consume_char('=')?;\n                        let expr_index = self.consume_expr()?;\n                        properties.push(TProperty::Event(TPropertyEvent {\n                            key,\n                            value: ExprIndex(expr_index),\n                        }));\n                    }\n                    '~' => {\n                        self.advance(1);\n                        let key = self.parse_style_name()?;\n                        self.consume_char('=')?;\n                        let value = if self.peek_char().is_some() {\n                            TPropertyStyleValue::String(self.parse_attribute_string()?)\n                        } else {\n                            TPropertyStyleValue::Expr(ExprIndex(self.consume_expr()?))\n                        };\n                        properties.push(TProperty::Style(TPropertyStyle { key, value }));\n                    }\n                    _ => {\n                        let key = self.parse_attribute_name()?;\n                        let mut hoist = false;\n                        let value;\n                        if self.try_consume_char('=').is_some() {\n                            if let Some('\"') = self.peek_char() {\n                                value =\n                                    TPropertyAttributeValue::String(self.parse_attribute_string()?);\n                            } else {\n                                let expr_index = self.consume_expr()?;\n                                if key == \"class\" {\n                                    let expr = &self.expressions[expr_index];\n                                    // Hoist symbols from the root scope\n                                    if is_hoistable_expr(expr, self.scoping) {\n                                        hoist = true;\n                                    }\n                                }\n                                value = TPropertyAttributeValue::Expr(THoistableExpr {\n                                    index: ExprIndex(expr_index),\n                                    hoist,\n                                });\n                            }\n                        } else {\n                            value = TPropertyAttributeValue::Bool;\n                        }\n                        properties.push(TProperty::Attribute(TPropertyAttribute { key, value }));\n                    }\n                }\n            } else {\n                properties.push(TProperty::Directive(ExprIndex(self.consume_expr()?)))\n            }\n            self.consume_whitespace();\n        }\n\n        let parts = self.text.split_at(self.text.len().min(10));\n        Err(OxcDiagnostic::error(format!(\"Expected a '>' char: {}\", parts.0))\n            .with_label(self.current_element().span))\n    }\n\n    fn parse_attribute_string(&mut self) -> Result<String, OxcDiagnostic> {\n        let delim;\n        let mut chars = self.text.char_indices();\n        if let Some((_, c)) = chars.next() {\n            match c {\n                '\\'' | '\"' => {\n                    delim = c;\n                }\n                _ => {\n                    return Err(OxcDiagnostic::error(\n                        \"Invalid string value, it should start with '\\\"' char.\",\n                    )\n                    .with_label(self.current_element().span));\n                }\n            }\n            for (i, c) in chars {\n                if c == delim {\n                    let v = self.text[1..i].to_string();\n                    self.advance(i + 1);\n                    return Ok(v);\n                }\n            }\n            Err(OxcDiagnostic::error(\"Invalid string value, it should end with '\\\"' char.\")\n                .with_label(self.current_element().span))\n        } else {\n            Err(OxcDiagnostic::error(\"Invalid string value\")\n                .with_label(self.current_element().span))\n        }\n    }\n}\n\n#[derive(Clone, Copy)]\nstruct WhitespaceState(u8);\n\nimpl WhitespaceState {\n    const WHITESPACE: u8 = 1;\n    const CONTAINS_NEWLINE: u8 = 1 << 1;\n    const CONTAINS_VERTICAL_TAB: u8 = 1 << 2;\n    const TEXT_CONTENT: u8 = 1 << 3;\n\n    fn should_insert_whitespace(self) -> bool {\n        self.0 & WhitespaceState::WHITESPACE != 0\n            && (self.0 & WhitespaceState::CONTAINS_NEWLINE == 0\n                || self.0 & WhitespaceState::CONTAINS_VERTICAL_TAB != 0)\n    }\n}\n\n// RTL pass\nfn update_flags(node: &mut TNode) {\n    _update_flags(node, 0);\n}\nfn _update_flags(node: &mut TNode, flags: u8) -> u8 {\n    let mut flags = flags;\n    if let TNodeKind::Element(e) = &mut node.kind {\n        let mut props_exprs = 0;\n        for p in &e.properties {\n            match p {\n                TProperty::Attribute(p) => {\n                    if let TPropertyAttributeValue::Expr(e) = &p.value\n                        && !e.hoist\n                    {\n                        props_exprs += 1;\n                        break;\n                    }\n                }\n                TProperty::Style(p) => {\n                    if let TPropertyStyleValue::Expr(_) = &p.value {\n                        props_exprs += 1;\n                        break;\n                    }\n                }\n                TProperty::Value(_)\n                | TProperty::DOMValue(_)\n                | TProperty::Event(_)\n                | TProperty::Directive(_) => {\n                    props_exprs += 1;\n                    break;\n                }\n            }\n        }\n\n        let mut siblings_flags = 0;\n        let mut children_exprs = 0;\n        for c in e.children.iter_mut().rev() {\n            match &mut c.kind {\n                TNodeKind::Element(_) => {\n                    let f = _update_flags(c, siblings_flags);\n                    if f & TNode::HAS_EXPRESSIONS != 0 {\n                        flags |= TNode::HAS_EXPRESSIONS;\n                        siblings_flags |= TNode::HAS_NEXT_EXPRESSION | TNode::HAS_NEXT_DOM_NODE;\n                    } else {\n                        siblings_flags |= TNode::HAS_NEXT_DOM_NODE;\n                    }\n                }\n                TNodeKind::Text(_) => {\n                    c.flags = siblings_flags;\n                    siblings_flags |= TNode::HAS_NEXT_DOM_NODE;\n                }\n                TNodeKind::Expr(_) => {\n                    siblings_flags |= TNode::HAS_NEXT_EXPRESSION;\n                    c.flags = siblings_flags;\n                    children_exprs += 1;\n                }\n            }\n        }\n\n        if props_exprs > 0 || children_exprs > 0 {\n            flags |= TNode::HAS_EXPRESSIONS;\n        }\n        node.flags = flags;\n        node.props_exprs = props_exprs;\n        node.children_exprs = children_exprs;\n    }\n    flags\n}\n\nfn assign_state_slots(node: &mut TNode) {\n    _assign_state_slots(node, 1);\n}\n\nfn _assign_state_slots(node: &mut TNode, mut state_index: u16) -> u16 {\n    if let TNodeKind::Element(e) = &mut node.kind {\n        let mut prev_expr = false;\n        for c in &mut e.children {\n            match c.kind {\n                TNodeKind::Element(_) => {\n                    if prev_expr {\n                        prev_expr = false;\n                        c.state_index = state_index;\n                        state_index += 1;\n                    } else if c.props_exprs > 0 || c.children_exprs > 0 {\n                        c.state_index = state_index;\n                        state_index += 1;\n                    }\n                    state_index = _assign_state_slots(c, state_index);\n                }\n                TNodeKind::Text(_) => {\n                    if prev_expr {\n                        prev_expr = false;\n                        c.state_index = state_index;\n                        state_index += 1;\n                    }\n                }\n                TNodeKind::Expr(_) => {\n                    prev_expr = true;\n                }\n            }\n        }\n    }\n\n    state_index\n}\n\nfn is_hoistable_expr<'a>(expr: &Expression<'a>, scoping: &Scoping) -> bool {\n    match expr {\n        Expression::Identifier(id) => {\n            let r = scoping.get_reference(id.reference_id());\n            if let Some(symbol_id) = r.symbol_id()\n                && scoping.symbol_scope_id(symbol_id) == scoping.root_scope_id()\n            {\n                return true;\n            }\n        }\n        Expression::StaticMemberExpression(expr) => {\n            return is_hoistable_expr(&expr.object, scoping);\n        }\n        _ => {}\n    }\n    false\n}\n"
  },
  {
    "path": "docs/internals/dynamic-lists.md",
    "content": "# Dynamic Lists\n\nJust some reminders:\n\n## Lazy rendering\n\nIt may seem like a good idea to render dynamic lists lazily and even avoid\nstoring keys in memory, but it may lead to some subtle bugs with mutable\nentries.\n\n## Dynamic Lists with Immutable Entries\n\nIt is possible to create stateless node for immutable lists by storing\nentries, key function and render function in a stateless node. On initial\nmount we will need to invoke render function for each entry and keys can be\ncompletely ignored. And when dynamic list is updated, we can lazily recover\nold keys from previous entries.\n\nDon't think that it is worth it in most real-world scenarios, and we can\nalways just memoize stateless node with dynamic list in a component state.\n"
  },
  {
    "path": "docs/internals/misc.md",
    "content": "# Misc\n\n## Type Casting\n\nThe current code base contains a lot of type casting. In idiomatic typescript\nit should be implemented with type guards, and if javascript toolchains\nsupported inlining in some reliable way instead of relying on their\nheuristics, the code could be way much cleaner with type guard functions.\n\n## Root Entry Functions\n\nEntry functions (update, dirtyCheck, unmount, etc) should save and restore\nrender context to avoid edge cases when they invoked synchronously in a\ndifferent root context.\n"
  },
  {
    "path": "docs/internals/perf.md",
    "content": "# Perf\n\n## `var` vs `let`\n\nIn some places variables are declared with `var` instead of `let`, it is a\nmicro optimization that propably won't have any significant impact on\nperformance, especially when the JIT kicks in.\n\n```js\nfunction __var(i) {\n  var j = i;\n  return function _var() {\n    return j;\n  }\n}\n\nfunction __let(i) {\n  let j = i;\n  return function _let() {\n    return j;\n  };\n}\n```\n\nIn the example above, `_var` function will have the following bytecode in V8:\n\n```txt\nLdaImmutableCurrentContextSlot [2]\nReturn\n```\n\nAnd `_let` function:\n\n```txt\nLdaImmutableCurrentContextSlot [2]\nThrowReferenceErrorIfHole[0];\nReturn\n```\n\n## `if (a === true) {}` vs `if (a) {}`\n\nIn a lot of places there are explicit strict equality checks to avoid\n`toBool()` coercion. Sometimes we can avoid explicit checks when JIT compiler\nis going to inline functions and will be able to eliminate `toBool()`\ncoercion. For example, `_isArray()` calls doesn't use strict equality checks.\n`(a === true)`\n\n```txt\n0x738b024    24  488b5518             REX.W movq rdx, [rbp + 0x18];\n0x738b028    28  493995b0000000       REX.W cmpq[r13 + 0xb0](root(true_value)), rdx;\n0x738b02f    2f  0f8424000000         jz 0x738b059 < +0x59 >\n0x738b035    35  48b80000000014000000 REX.W movq rax, 0x1400000000;\n0x738b03f    3f  488b4de8             REX.W movq rcx, [rbp - 0x18];\n0x738b043    43  488be5               REX.W movq rsp, rbp;\n0x738b046    46  5d                   pop rbp;\n0x738b047    47  4883f902             REX.W cmpq rcx, 0x2;\n0x738b04b    4b  7f03                 jg 0x738b050 < +0x50 >\n0x738b04d    4d  c21000               ret 0x10;\n`(a)`, inlines `toBool()`\n0x618b024    24  488b5518             REX.W movq rdx, [rbp + 0x18];\n0x618b028    28  f6c201               testb rdx, 0x1;\n0x618b02b    2b  0f84a7000000         jz 0x618b0d8 < +0xd8 >\n0x618b031    31  493995b8000000       REX.W cmpq[r13 + 0xb8](root(false_value)), rdx;\n0x618b038    38  0f8459000000         jz 0x618b097 < +0x97 >\n0x618b03e    3e  493995c0000000       REX.W cmpq[r13 + 0xc0](root(empty_string)), rdx;\n0x618b045    45  0f844c000000         jz 0x618b097 < +0x97 >\n0x618b04b    4b  488b4aff             REX.W movq rcx, [rdx - 0x1];\n0x618b04f    4f  f6410d10             testb[rcx + 0xd], 0x10;\n0x618b053    53  0f853e000000         jnz 0x618b097 < +0x97 >\n0x618b059    59  49398d38010000       REX.W cmpq[r13 + 0x138](root(heap_number_map)), rcx;\n0x618b060    60  0f8484000000         jz 0x618b0ea < +0xea >\n0x618b066    66  49398db8010000       REX.W cmpq[r13 + 0x1b8](root(bigint_map)), rcx;\n0x618b06d    6d  0f846c000000         jz 0x618b0df < +0xdf >\n0x618b073    73  48b8000000000a000000 REX.W movq rax, 0xa00000000;\n0x618b07d    7d  488b4de8             REX.W movq rcx, [rbp - 0x18];\n0x618b081    81  488be5               REX.W movq rsp, rbp;\n0x618b084    84  5d                   pop rbp;\n0x618b085    85  4883f902             REX.W cmpq rcx, 0x2;\n0x618b089    89  7f03                 jg 0x618b08e < +0x8e >\n0x618b08b    8b  c21000               ret 0x10;\n```\n\n## Polymorphic Call-Sites\n\nIt is not always a good idea to optimize for monomorphic call-sites. If there\nis a low degree polymorphism, it can be better to use different shapes.\nIn some cases compiler can optimize several polymorphic call-sites and\nperform just one shape check. To understand how to reorganize code, so that\ncompiler could better optimize it, it is necessary to understand aliasing:\nhttps://en.wikipedia.org/wiki/Aliasing_(computing)\n\n## Additional Resources\n\n- https://v8.dev/docs/turbofan\n- https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html\n- https://benediktmeurer.de/2017/06/29/javascript-optimization-patterns-part2/\n- https://github.com/thlorenz/v8-perf/blob/master/language-features.md\n"
  },
  {
    "path": "docs/internals/template-compiler.md",
    "content": "# Template Compiler\n\n## Markers\n\nComment markers `<!>` should be inserted into template to delineate a slot\nposition for an expression when it is inserted between two text nodes. E.g.\n\n    <div>prefix${expr}suffix</div>\n\nShould produce the following HTML template:\n\n    <div>prefix<!>suffix</div>\n\n## OpCodes\n\nWhen properties `PropOpCode` are starting to update:\n\n- currentNode is assigned to template root node\n- properties are updated starting from the root node\n\nBecause of this invariants we can avoid adding SetNode opCode for root nodes.\n\nWhen children `ChildOpCode` are starting to update:\n\n- parentNode is assigned to root node\n- nextNode is assigned to null\n\nBecause of this invariants we can avoid generating state and children opcodes\nin cases like:\n\n```html\n<div>\n  <span>${expr}</span>\n</div>\n```\n\nHere we don't need to traverse DOM tree when mounting, because we already know\nits root node and dynamic child is positioned in the end. So, `stateOpCodes`\nshould be empty, and `childOpCodes` should have only `UpdateChild` opCode.\n\n## Ideas\n\n### SMI Arrays vs Strings for OpCodes\n\nIt is possible to encode OpCodes as strings and get deduplication via string\ninterning. OpCodes encoded as strings should also have smaller size and faster\nto parse. But overall it is probably not worth it.\n\n### Optimize PropOpCode encoding to reduce code size\n\nIn majority of templates, the size of expressions array, state array, etc\nis lower than 16. So instead of storing indexes in a contiguous set of bits,\nwe can store lowest bits in the first 4 bits and the rest in the highest\nbits, e.g.:\n\n```txt\nPropOpCode {\n  type:3,\n  expr_lo:4,\n  data_lo:4,\n  expr_hi:6,\n  data_hi:..,\n}\n\nexpr = ((op >> 3) & Mask4) | ((op >> 11) & Mask6);\ndata = ((op >> 7) & Mask4) | (op >> 17);\n```\n\nAnd since we are deduplicating all data and storing it in a shared array,\nwe can sort it by the number of occurences in templates, so that indices for\nthe 16 most common keys will be able to fit into 4 bits.\n\n### Store string length in StateOpCodes to separate static text nodes\n\nIt is possible to avoid injecting `<!>` comment nodes to separate static\nstrings by replacing remove opCode with split opCode that is going to store\nstring length.\n"
  },
  {
    "path": "docs/misc/migrating-from-react.md",
    "content": "# Migrating From React\n\nThis document shows how to rewrite examples from the https://react.dev/learn/\ndocumentation with ivi API.\n\n## Table of Contents\n\n- [Describing the UI](#describing-the-ui)\n  - [Your first component](#your-first-component)\n  - [Importing and exporting component](#importing-and-exporting-components)\n  - [Writing markup with JSX](#writing-markup-with-jsx)\n  - [Javascript in JSX with curly braces](#javascript-in-jsx-with-curly-braces)\n  - [Passing props to components](#passing-props-to-a-component)\n  - [Conditional rendering](#conditional-rendering)\n  - [Rendering lists](#rendering-lists)\n  - [Keeping components pure](#keeping-components-pure)\n- [Adding interactivity](#adding-interactivity)\n  - [Responding to events](#responding-to-events)\n  - [State: a component's memory](#state-a-components-memory)\n  - [Updating objects in state](#updating-objects-in-state)\n- [Managing state](#managing-state)\n  - [Reacting to input with state](#reacting-to-input-with-state)\n  - [Choosing the state structure](#choosing-the-state-structure)\n  - [Preserving and resetting state](#preserving-and-resetting-state)\n  - [Extracting state logic into a reducer](#extracting-state-logic-into-a-reducer)\n  - [Passing data deeply with context](#passing-data-deeply-with-context)\n- [Escape hatches](#escape-hatches)\n  - [Referencing values with refs](#referencing-values-with-refs)\n  - [Manipulating the DOM with refs](#manipulating-the-dom-with-refs)\n  - [Synchronizing with Effects](#synchronizing-with-effects)\n  - [Removing Effect dependencies](#removing-effect-dependencies)\n  - [Reusing logic with custom hooks](#reusing-logic-with-custom-hooks)\n\n## [Describing the UI](https://react.dev/learn/describing-the-ui)\n\n### [Your first component](https://react.dev/learn/describing-the-ui#your-first-component)\n\nReact:\n\n```js\nfunction Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/MK3eW3As.jpg\"\n      alt=\"Katherine Johnson\"\n    />\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\nivi:\n\n```js\nimport { html } from 'ivi';\n\nconst Profile = () => html`\n  <img\n    src=\"https://i.imgur.com/MK3eW3As.jpg\"\n    alt=\"Katherine Johnson\"\n  />\n`;\n\nconst Gallery = () => html`\n  <section>\n    <h1>Amazing scientists</h1>\n    ${Profile()}\n    ${Profile()}\n    ${Profile()}\n  </section>\n`;\nexport default Gallery;\n```\n\n### [Importing and exporting components](https://react.dev/learn/describing-the-ui#importing-and-exporting-components)\n\nReact:\n\n```js\nimport Profile from './Profile.js';\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\nivi:\n\n```js\nimport { html } from 'ivi';\nimport Profile from './Profile.js';\n\nconst Gallery = () => html`\n  section\n    h1 'Amazing scientists'\n    ${Profile()}\n    ${Profile()}\n    ${Profile()}\n`;\n\nexport default Gallery;\n```\n\n### [Writing markup with JSX](https://react.dev/learn/describing-the-ui#writing-markup-with-jsx)\n\nReact:\n\n```js\nexport default function TodoList() {\n  return (\n    <>\n      <h1>Hedy Lamarr's Todos</h1>\n      <img\n        src=\"https://i.imgur.com/yXOvdOSs.jpg\"\n        alt=\"Hedy Lamarr\"\n        className=\"photo\"\n      />\n      <ul>\n        <li>Invent new traffic lights</li>\n        <li>Rehearse a movie scene</li>\n        <li>Improve spectrum technology</li>\n      </ul>\n    </>\n  );\n}\n```\n\nivi:\n\n```js\nimport { html } from 'ivi';\n\nconst TodoList = () => html`\n  <h1>Hedy Lamarr's Todos</h1>\n  <img\n    class\"photo\"\n    src=\"https://i.imgur.com/yXOvdOSs.jpg\"\n    alt=\"Hedy Lamarr\"\n  />\n  <ul>\n    <li>Invent new traffic lights</li>\n    <li>Rehearse a movie scene</li>\n    <li>Improve spectrum technology</li>\n  </ul>\n`;\nexport default TodoList;\n```\n### [JavaScript in JSX with curly braces](https://react.dev/learn/describing-the-ui#javascript-in-jsx-with-curly-braces)\n\nReact:\n\n```js\nconst person = {\n  name: 'Gregorio Y. Zara',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person.name}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src=\"https://i.imgur.com/7vQD0fPs.jpg\"\n        alt=\"Gregorio Y. Zara\"\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\nivi:\n\n```js\nconst person = {\n  name: 'Gregorio Y. Zara',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nconst TodoList = () => html`\n  <div\n    ~background-color=${person.theme.backgroundColor}\n    ~color=${person.theme.color}\n  >\n    <h1>${person.name}'s Todos</h1>\n    <img\n      class=\"avatar\"\n      src=\"https://i.imgur.com/7vQD0fPs.jpg\"\n      alt=\"Gregorio Y. Zara\"\n    />\n    <ul>\n      <li>Improve the videophone</li>\n      <li>Prepare aeronautics lectures</li>\n      <li>Work on the alcohol-fuelled engine</li>\n    </ul>\n  </div>\n`;\nexport default TodoList;\n```\n\n### [Passing props to a component](https://react.dev/learn/describing-the-ui#passing-props-to-a-component)\n\nReact:\n\n```jsx\nimport { getImageUrl } from './utils.js'\n\nexport default function Profile() {\n  return (\n    <Card>\n      <Avatar\n        size={100}\n        person={{\n          name: 'Katsuko Saruhashi',\n          imageId: 'YfeOqp2'\n        }}\n      />\n    </Card>\n  );\n}\n```\n\nivi:\n\n```js\nimport { getImageUrl } from './utils.js'\n\nconst Profile = () => (\n  Card(\n    Avatar({\n      size: 100,\n      person: {\n        name: 'Katsuko Saruhashi',\n        imageId: 'YfeOqp2'\n      },\n    }),\n  )\n);\nexport default Profile;\n```\n\n### [Conditional Rendering](https://react.dev/learn/describing-the-ui#conditional-rendering)\n\nReact:\n\n```js\nfunction Item({ name, isPacked }) {\n  return (\n    <li className=\"item\">\n      {name} {isPacked && '✔'}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item\n          isPacked={true}\n          name=\"Space suit\"\n        />\n        <Item\n          isPacked={true}\n          name=\"Helmet with a golden leaf\"\n        />\n        <Item\n          isPacked={false}\n          name=\"Photo of Tam\"\n        />\n      </ul>\n    </section>\n  );\n}\n```\n\nivi:\n\n```js\nimport { html } from \"ivi\";\n\nconst Item = ({ name, isPacked }) => html`\n  <li class=\"item\">\n    ${name} ${isPacked && '✔'}\n  </li>\n`;\n\nconst PackingList = () => html`\n  <section>\n    <h1>Sally Ride's Packing List</h1>\n    <ul>\n      ${Item({ isPacked: true, name: 'Space suit' })}\n      ${Item({ isPacked: true, name: 'Helmet with a golden leaf' })}\n      ${Item({ isPacked: false, name: 'Photo of Tam' })}\n    </ul>\n  </section>\n`;\nexport default PackingList;\n```\n\n### [Rendering lists](https://react.dev/learn/describing-the-ui#rendering-lists)\n\nReact:\n\n```js\nimport { people } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nexport default function List() {\n  const listItems = people.map(person =>\n    <li key={person.id}>\n      <img\n        src={getImageUrl(person)}\n        alt={person.name}\n      />\n      <p>\n        <b>{person.name}:</b>\n        {' ' + person.profession + ' '}\n        known for {person.accomplishment}\n      </p>\n    </li>\n  );\n  return (\n    <article>\n      <h1>Scientists</h1>\n      <ul>{listItems}</ul>\n    </article>\n  );\n}\n```\n\nivi:\n\n```js\nimport { List } from 'ivi';\nimport { html } from 'ivi';\nimport { people } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nconst ScientistsList = () => html`\n  <article>\n    <h1>Scientists</h1>\n    <ul>\n      ${List(people, (person) => person.id, (person) => html`\n        <li>\n          <img\n            src=${getImageUrl(person)}\n            alt=${person.name}\n          />\n          <p>\n            <b>${person.name}:</b>\n            \\v ${person.profession}\n            \\v known for  ${person.accomplishment}\n          </p>\n        </li>\n      `)}\n    </ul>\n  </article>\n`;\nexport default ScientistsList;\n```\n\n### [Keeping components pure](https://react.dev/learn/describing-the-ui#keeping-components-pure)\n\nReact:\n\n```js\nfunction Cup({ guest }) {\n  return <h2>Tea cup for guest #{guest}</h2>;\n}\n\nexport default function TeaSet() {\n  return (\n    <>\n      <Cup guest={1} />\n      <Cup guest={2} />\n      <Cup guest={3} />\n    </>\n  );\n}\n```\n\nivi:\n\n```js\nimport { html } from 'ivi';\n\nconst Cup = ({ guest }) => html`\n  <h2>Tea cup for guest #${guest}</h2>\n`;\n\nconst TeaSet = () => [\n  Cup({ guest: 1 }),\n  Cup({ guest: 2 }),\n  Cup({ guest: 3 }),\n];\nexport default TeaSet;\n```\n\n## [Adding Interactivity](https://react.dev/learn/adding-interactivity)\n\n### [Responding to events](https://react.dev/learn/adding-interactivity#responding-to-events)\n\nReact:\n\n```js\nexport default function App() {\n  return (\n    <Toolbar\n      onPlayMovie={() => alert('Playing!')}\n      onUploadImage={() => alert('Uploading!')}\n    />\n  );\n}\n\nfunction Toolbar({ onPlayMovie, onUploadImage }) {\n  return (\n    <div>\n      <Button onClick={onPlayMovie}>\n        Play Movie\n      </Button>\n      <Button onClick={onUploadImage}>\n        Upload Image\n      </Button>\n    </div>\n  );\n}\n\nfunction Button({ onClick, children }) {\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\nivi:\n\n```js\nimport { component, useState } from 'ivi';\nimport { html } from 'ivi';\n\nconst App = component((c) => {\n  const onPlayMovie = () => alert('Playing!');\n  const onUploadImage = () => alert('Uploading!');\n  return () => ToolBar({ onPlayMovie, onUploadImage });\n});\nexport default App;\n\nconst Toolbar = ({ onPlayMovie, onUploadImage }) => html`\n  <div>\n    ${Button({ onClick: onPlayMovie, children: 'Play Movie' })}\n    ${Button({ onClick: onUploadImage, children: 'Upload Image' })}\n  </div>\n`;\n\nconst Button = ({ onClick, children }) => html`\n  <button @click=${onClick}>\n    ${children}\n  </button>\n`;\n```\n\n### [State: a component’s memory](https://react.dev/learn/adding-interactivity#state-a-components-memory)\n\nReact:\n\n```js\nimport { useState } from 'react';\nimport { sculptureList } from './data.js';\n\nexport default function Gallery() {\n  const [index, setIndex] = useState(0);\n  const [showMore, setShowMore] = useState(false);\n\n  function handleNextClick() {\n    setIndex(index + 1);\n  }\n\n  function handleMoreClick() {\n    setShowMore(!showMore);\n  }\n\n  let sculpture = sculptureList[index];\n  return (\n    <>\n      <button onClick={handleNextClick}>\n        Next\n      </button>\n      <h2>\n        <i>{sculpture.name} </i>\n        by {sculpture.artist}\n      </h2>\n      <h3>\n        ({index + 1} of {sculptureList.length})\n      </h3>\n      <button onClick={handleMoreClick}>\n        {showMore ? 'Hide' : 'Show'} details\n      </button>\n      {showMore && <p>{sculpture.description}</p>}\n      <img\n        src={sculpture.url}\n        alt={sculpture.alt}\n      />\n    </>\n  );\n}\n```\n\nivi:\n\n```js\nimport { component, useState } from 'ivi';\nimport { html } from 'ivi';\nimport { sculptureList } from './data.js';\n\nconst Gallery = () => {\n  const [index, setIndex] = useState(0);\n  const [showMore, setShowMore] = useState(false);\n\n  function handleNextClick() {\n    setIndex(index + 1);\n  }\n\n  function handleMoreClick() {\n    setShowMore(!showMore);\n  }\n\n  return () => {\n    const index = index();\n    const showMore = showMore();\n    const sculpture = sculptureList[index];\n    return html`\n      <button @click=${handleNextClick}>Next</button>\n      <h2>\n        <i>${sculpture.name}</i>\n        \\v by ${sculpture.artist}\n      </h2>\n      <h3>(${index + 1} of ${sculptureList.length}</h3>\n      <button @click=${handleMoreClick}>\n        ${showMore ? 'Hide' : 'Show'} details\n      </button>\n      ${showMore && html`<p>${sculpture.description}</p>`}\n      <img\n        src=${sculpture.url}\n        alt=${sculpture.alt}\n      />\n    `;\n  };\n}\nexport default Gallery;\n```\n\n### [State as a snapshot](https://react.dev/learn/adding-interactivity#state-as-a-snapshot)\n\nReact:\n\n```js\nconsole.log(count);  // 0\nsetCount(count + 1); // Request a re-render with 1\nconsole.log(count);  // Still 0!\n```\n\nivi:\n\nLike regular javascript variables, ivi state is updated immediately.\n\n```js\nconsole.log(count());  // 0\nsetCount(count() + 1); // Request a re-render with 1\nconsole.log(count());  // 1\n```\n\n### [Updating objects in state](https://react.dev/learn/adding-interactivity#updating-objects-in-state)\n\nReact:\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [person, setPerson] = useState({\n    name: 'Niki de Saint Phalle',\n    artwork: {\n      title: 'Blue Nana',\n      city: 'Hamburg',\n      image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n    }\n  });\n\n  function handleNameChange(e) {\n    setPerson({\n      ...person,\n      name: e.target.value\n    });\n  }\n\n  function handleTitleChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        title: e.target.value\n      }\n    });\n  }\n\n  function handleCityChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        city: e.target.value\n      }\n    });\n  }\n\n  function handleImageChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        image: e.target.value\n      }\n    });\n  }\n\n  return (\n    <>\n      <label>\n        Name:\n        <input\n          value={person.name}\n          onChange={handleNameChange}\n        />\n      </label>\n      <label>\n        Title:\n        <input\n          value={person.artwork.title}\n          onChange={handleTitleChange}\n        />\n      </label>\n      <label>\n        City:\n        <input\n          value={person.artwork.city}\n          onChange={handleCityChange}\n        />\n      </label>\n      <label>\n        Image:\n        <input\n          value={person.artwork.image}\n          onChange={handleImageChange}\n        />\n      </label>\n      <p>\n        <i>{person.artwork.title}</i>\n        {' by '}\n        {person.name}\n        <br />\n        (located in {person.artwork.city})\n      </p>\n      <img\n        src={person.artwork.image}\n        alt={person.artwork.title}\n      />\n    </>\n  );\n}\n```\n\nivi:\n\n```js\nimport { component, useState } from 'ivi';\nimport { html } from 'ivi';\n\nconst Form = component((c) => {\n  const [person, setPerson] = useState(c, {\n    name: 'Niki de Saint Phalle',\n    artwork: {\n      title: 'Blue Nana',\n      city: 'Hamburg',\n      image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n    }\n  });\n\n  function handleNameChange(e) {\n    setPerson({\n      ...person(),\n      name: e.target.value\n    });\n  }\n\n  function handleTitleChange(e) {\n    const p = person();\n    setPerson({\n      ...p,\n      artwork: {\n        ...p.artwork,\n        title: e.target.value\n      }\n    });\n  }\n\n  function handleCityChange(e) {\n    const p = person();\n    setPerson({\n      ...p,\n      artwork: {\n        ...p.artwork,\n        city: e.target.value\n      }\n    });\n  }\n\n  function handleImageChange(e) {\n    const p = person();\n    setPerson({\n      ...p,\n      artwork: {\n        ...p.artwork,\n        image: e.target.value\n      }\n    });\n  }\n\n  return () => {\n    const p = person();\n    return html`\n      <label>\n        Name:\n        <input\n          *value=${p.name}\n          @input=${handleNameChange}\n        />\n      </label>\n      <label>\n        Title:\n        <input\n          *value=${p.artwork.title}\n          @input=${handleTitleChange}\n        />\n      </label>\n      <label>\n        City:\n        <input\n          *value=${p.artwork.city}\n          @input=${handleCityChange}\n        />\n      </label>\n      <label>\n        Image:\n        <input\n          *value=${p.artwork.image}\n          @input=${handleImageChange}\n        />\n      </label>\n      <p>\n        <i>${p.artwork.title}</i>\n        \\v by ${p.name}\n        <br>\n        (located in ${p.artwork.city} )\n      </p>\n      <img\n        src=${p.artwork.image}\n        alt=${p.artwork.title}\n      />\n    `;\n  };\n});\nexport default Form;\n```\n\n## [Managing State](https://react.dev/learn/managing-state)\n\n### [Reacting to input with state](https://react.dev/learn/managing-state#reacting-to-input-with-state)\n\nReact:\n\n```js\n\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [answer, setAnswer] = useState('');\n  const [error, setError] = useState(null);\n  const [status, setStatus] = useState('typing');\n\n  if (status === 'success') {\n    return <h1>That's right!</h1>\n  }\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    setStatus('submitting');\n    try {\n      await submitForm(answer);\n      setStatus('success');\n    } catch (err) {\n      setStatus('typing');\n      setError(err);\n    }\n  }\n\n  function handleTextareaChange(e) {\n    setAnswer(e.target.value);\n  }\n\n  return (\n    <>\n      <h2>City quiz</h2>\n      <p>\n        In which city is there a billboard that turns air into drinkable water?\n      </p>\n      <form onSubmit={handleSubmit}>\n        <textarea\n          value={answer}\n          onChange={handleTextareaChange}\n          disabled={status === 'submitting'}\n        />\n        <br />\n        <button disabled={\n          answer.length === 0 ||\n          status === 'submitting'\n        }>\n          Submit\n        </button>\n        {error !== null &&\n          <p className=\"Error\">\n            {error.message}\n          </p>\n        }\n      </form>\n    </>\n  );\n}\n\nfunction submitForm(answer) {\n  // Pretend it's hitting the network.\n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      let shouldError = answer.toLowerCase() !== 'lima'\n      if (shouldError) {\n        reject(new Error('Good guess but a wrong answer. Try again!'));\n      } else {\n        resolve();\n      }\n    }, 1500);\n  });\n}\n```\n\nivi:\n\n```js\nimport { component, useState } from 'ivi';\nimport { html } from 'ivi';\n\nconst Form = component((c) => {\n  const [answer, setAnswer] = useState('');\n  const [error, setError] = useState(null);\n  const [status, setStatus] = useState('typing');\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    const answer = answer();\n    setStatus('submitting');\n    try {\n      await submitForm(answer);\n      setStatus('success');\n    } catch (err) {\n      setStatus('typing');\n      setError(err);\n    }\n  }\n\n  function handleTextareaChange(e) {\n    setAnswer(e.target.value);\n  }\n\n  return () => {\n    if (status() === 'success') {\n      return html`h1 'That's right!`;\n    }\n    return html`\n      <h2>City quiz</h2>\n      <p>\n        In which city is there a billboard that turns air into drinkable water?\n      </p>\n      <form @submit=${handleSubmit}>\n        <textarea\n          *value=${answer()}\n          @input=${handleTextareaChange}\n          disabled=${status() === 'submitting'}\n        />\n        <br>\n        <button\n          disabled=${\n            answer().length === 0 ||\n            status() === 'submitting'\n          }\n          Submit\n        />\n        ${error() && html`<p class=\"Error\">${error().message}</p>`}\n      </form>\n    `;\n  };\n});\n\nfunction submitForm(answer) {\n  // Pretend it's hitting the network.\n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      let shouldError = answer.toLowerCase() !== 'lima'\n      if (shouldError) {\n        reject(new Error('Good guess but a wrong answer. Try again!'));\n      } else {\n        resolve();\n      }\n    }, 1500);\n  });\n}\n```\n\n### [Choosing the state structure](https://react.dev/learn/managing-state#choosing-the-state-structure)\n\nReact:\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n\n  const fullName = firstName + ' ' + lastName;\n\n  function handleFirstNameChange(e) {\n    setFirstName(e.target.value);\n  }\n\n  function handleLastNameChange(e) {\n    setLastName(e.target.value);\n  }\n\n  return (\n    <>\n      <h2>Let’s check you in</h2>\n      <label>\n        First name:{' '}\n        <input\n          value={firstName}\n          onChange={handleFirstNameChange}\n        />\n      </label>\n      <label>\n        Last name:{' '}\n        <input\n          value={lastName}\n          onChange={handleLastNameChange}\n        />\n      </label>\n      <p>\n        Your ticket will be issued to: <b>{fullName}</b>\n      </p>\n    </>\n  );\n}\n```\n\nivi:\n\n```js\nimport { component, useState } from 'ivi';\nimport { html } from 'ivi';\n\nconst Form = component((c) => {\n  const [firstName, setFirstName] = useState(c, '');\n  const [lastName, setLastName] = useState(c, '');\n\n  function handleFirstNameChange(e) {\n    setFirstName(e.target.value);\n  }\n\n  function handleLastNameChange(e) {\n    setLastName(e.target.value);\n  }\n\n  return () => {\n    const fullName = firstName + ' ' + lastName;\n\n    return html`\n      <h2>Let's check you in</h2>\n      <label>\n        First Name: \\v\n        <input *value=${firstName()} @input=${handleFirstNameChange} />\n      </label>\n      <label>\n        Last name: \\v\n        <input *value=${lastName()} @input=${handleLastNameChange} />\n      <p>\n        Your ticket will be issued to: \\v\n        <b>${fullName}</b>\n      </p>\n    `;\n  };\n});\n```\n\n### [Preserving and resetting state](https://react.dev/learn/managing-state#preserving-and-resetting-state)\n\nReact:\n\n```js\nimport { useState } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\n\nexport default function Messenger() {\n  const [to, setTo] = useState(contacts[0]);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedContact={to}\n        onSelect={contact => setTo(contact)}\n      />\n      <Chat key={to.email} contact={to} />\n    </div>\n  )\n}\n\nconst contacts = [\n  { name: 'Taylor', email: 'taylor@mail.com' },\n  { name: 'Alice', email: 'alice@mail.com' },\n  { name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\nivi:\n\n```js\nimport { component, useState } from 'ivi';\nimport { html } from 'ivi';\nimport { Identity } from '@ivi/identity';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\n\nconst Messenger = component((c) => {\n  const [_to, setTo] = useState(contacts[0]);\n  const onSelect = (contact) => { setTo(contact); };\n  return () => {\n    const to = _to();\n    return html`\n      <div>\n        ${ContactList({\n            contacts,\n            selectedContact: to,\n            onSelect,\n        })}\n        ${Identity({ key: to.email, contact: to })}\n      </div>\n    `;\n  }\n});\nexport default Messenger;\n\nconst contacts = [\n  { name: 'Taylor', email: 'taylor@mail.com' },\n  { name: 'Alice', email: 'alice@mail.com' },\n  { name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\n### [Extracting state logic into a reducer](https://react.dev/learn/managing-state#extracting-state-logic-into-a-reducer)\n\nReact:\n\n```js\nimport { useReducer } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId\n    });\n  }\n\n  return (\n    <>\n      <h1>Prague itinerary</h1>\n      <AddTask\n        onAddTask={handleAddTask}\n      />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  { id: 0, text: 'Visit Kafka Museum', done: true },\n  { id: 1, text: 'Watch a puppet show', done: false },\n  { id: 2, text: 'Lennon Wall pic', done: false }\n];\n```\n\nivi:\n\n```js\nimport { component, useReducer } from 'ivi';\nimport { html } from 'ivi';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nconst TaskApp = component((c) => {\n  const [tasks, dispatch] = useReducer(c, initialTasks, tasksReducer);\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId\n    });\n  }\n\n  return () => html`\n    <h1>Prague itinerary</h1>\n    ${AddTask({ onAddTask })}\n    ${TaskList({\n      tasks,\n      onChangeTask: handleChangeTask,\n      onDeleteTask: handleDeleteTask,\n    })}\n  `;\n});\nexport default TaskApp;\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  { id: 0, text: 'Visit Kafka Museum', done: true },\n  { id: 1, text: 'Watch a puppet show', done: false },\n  { id: 2, text: 'Lennon Wall pic', done: false }\n];\n```\n\n### [Passing data deeply with context](https://react.dev/learn/managing-state#passing-data-deeply-with-context)\n\nReact:\n\n```js\nimport { createContext, useContext } from 'react';\nconst LevelContext = createContext(0);\n\nfunction Heading({ children }) {\n  const level = useContext(LevelContext);\n  switch (level) {\n    case 0:\n      throw Error('Heading must be inside a Section!');\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n\nfunction Section({ children }) {\n  const level = useContext(LevelContext);\n  return (\n    <section className=\"section\">\n      <LevelContext.Provider value={level + 1}>\n        {children}\n      </LevelContext.Provider>\n    </section>\n  );\n}\n\nfunction Page() {\n  return (\n    <Section>\n      <Heading>Title</Heading>\n      <Section>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Section>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Section>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n          </Section>\n        </Section>\n      </Section>\n    </Section>\n  );\n}\n```\n\nivi:\n\n```js\nimport { component, context } from 'ivi';\nimport { html } from 'ivi';\n\nconst [getLevelContext, LevelContext] = context();\n\nconst Heading = (c) => {\n  const level = getLevelContext(c);\n  return (children) => {\n    switch (level) {\n      case 0:\n        throw Error('Heading must be inside a Section!');\n      case 1:\n        return html`<h1>${children}</h1>`;\n      case 2:\n        return html`<h2>${children}</h2>`;\n      case 3:\n        return html`<h3>${children}</h3>`;\n      case 4:\n        return html`<h4>${children}</h4>`;\n      case 5:\n        return html`<h5>${children}</h5>`;\n      case 6:\n        return html`<h6>${children}</h6>`;\n      default:\n        throw Error('Unknown level: ' + level);\n    }\n  }\n}\n\nconst Section = component(c) => {\n  const level = useContext(LevelContext);\n  return (children) => html`\n    <section class=\"section\">\n      ${LevelContext(level + 1, children)}\n    </section>\n  `;\n}\n\nconst Page = () => (\n  Section([\n    Heading(\"Title\"),\n    Section([\n      Heading(\"Heading\"),\n      Heading(\"Heading\"),\n      Heading(\"Heading\"),\n    ]),\n    Section([\n      Heading(\"Sub-heading\"),\n      Heading(\"Sub-heading\"),\n      Heading(\"Sub-heading\"),\n      Section([\n        Heading(\"Sub-sub-heading\"),\n        Heading(\"Sub-sub-heading\"),\n        Heading(\"Sub-sub-heading\"),\n      ]),\n    ]),\n  ])\n);\n```\n\n## [Escape Hatches](https://react.dev/learn/escape-hatches)\n\n### [Referencing values with refs](https://react.dev/learn/escape-hatches#referencing-values-with-refs)\n\nReact:\n\n```js\nimport { useRef } from 'react';\n\nexport default function Counter() {\n  let ref = useRef(0);\n\n  function handleClick() {\n    ref.current = ref.current + 1;\n    alert('You clicked ' + ref.current + ' times!');\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Click me!\n    </button>\n  );\n}\n```\n\nivi:\n\n```js\nimport { component } from 'ivi';\nimport { html } from 'ivi';\n\nconst Counter = component((c) => {\n  let _ref = 0;\n\n  function handleClick() {\n    _ref += 1;\n    alert('You clicked ' + _ref + ' times!');\n  }\n\n  return html`<button @click=${handleClick}>Click me!</button>`;\n});\nexport default Counter;\n```\n\n### [Manipulating the DOM with refs](https://react.dev/learn/escape-hatches#manipulating-the-dom-with-refs)\n\nReact:\n\n```js\nimport { useRef } from 'react';\n\nexport default function Form() {\n  const inputRef = useRef(null);\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return (\n    <>\n      <input ref={inputRef} />\n      <button onClick={handleClick}>\n        Focus the input\n      </button>\n    </>\n  );\n}\n```\n\nivi:\n\n```js\nimport { component } from 'ivi';\nimport { html } from 'ivi';\n\nconst Form = component((c) => {\n  let _inputRef = null;\n  const onInputRef = (e) => _inputRef = e;\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return () => html`\n    <input ${onInputRef}/>\n    <button @click=${handleClick}>Focus the input</button>\n  `;\n});\n```\n\n### [Synchronizing with Effects](https://react.dev/learn/escape-hatches#synchronizing-with-effects)\n\nReact:\n\n```js\nimport { useState, useRef, useEffect } from 'react';\n\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    if (isPlaying) {\n      ref.current.play();\n    } else {\n      ref.current.pause();\n    }\n  }, [isPlaying]);\n\n  return <video ref={ref} src={src} loop playsInline />;\n}\n\nexport default function App() {\n  const [isPlaying, setIsPlaying] = useState(false);\n  return (\n    <>\n      <button onClick={() => setIsPlaying(!isPlaying)}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <VideoPlayer\n        isPlaying={isPlaying}\n        src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n      />\n    </>\n  );\n}\n```\n\nivi:\n\n```js\nimport { component, useEffect } from 'ivi';\nimport { html } from 'ivi';\n\nconst VideoPlayer = component((c) => {\n  let _ref = null;\n  const getRef = (e) => _ref = e;\n  const update = useEffect(c, (isPlaying) => {\n    if (isPlaying) {\n      _ref.play();\n    } else {\n      _ref.pause();\n    }\n  }, strictEq);\n\n  return ({ src, isPlaying }) => (\n    update(isPlaying),\n    html`<video ${getRef} src=${src} loop playsInline />`);\n});\n\nconst App = component((c) => {\n  const [isPlaying, setIsPlaying] = useState(c, false);\n  return html`\n    <button @click=${onClick}>${isPlaying() ? 'Pause' : 'Play'}</button>\n    ${VideoPlayer({\n      isPlaying: isPlaying(),\n      src: 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4',\n    })}\n  `;\n});\n\nexport default App;\n```\n\n### [Removing Effect dependencies](https://react.dev/learn/escape-hatches#removing-effect-dependencies)\n\nReact:\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input value={message} onChange={e => setMessage(e.target.value)} />\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\nivi:\n\n```js\nimport { component, useState, useEffect } from 'ivi';\nimport { html } from 'ivi';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nconst ChatRoom = component((c) => {\n  const [message, setMessage] = useState(c, '');\n\n  const updateRoomConnection = useEffect(c, (roomId) => {\n    const options = {\n      serverUrl,\n      roomId,\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, strictEq);\n\n  const onInput = (ev) => { setMessage(ev.target.value); };\n\n  return ({ roomId }) => (\n    updateRoomConnection(roomId),\n    html`\n      <h1>Welcome to the ${roomId} room!</h1>\n      <input *value=${message()} @input=${onInput} />\n    `,\n  );\n});\n\nconst App = component((c) => {\n  const [roomId, setRoomId] = useState('general');\n  const onChange = (ev) => setRoomId(ev.target.value);\n  return html`\n    <label>\n      Choose the chat room: \\v\n      <select\n        *value=${roomId}\n        @change=${onChange}\n      >\n        <option value=\"general\">general</option>\n        <option value=\"travel\">travel</option>\n        <option value=\"music\">music</option>\n      </select>\n    <hr>\n    ${ChatRoom({ roomId: roomId() })}\n  `;\n});\nexport default App;\n```\n\n### [Reusing logic with custom Hooks](https://react.dev/learn/escape-hatches#reusing-logic-with-custom-hooks)\n\nReact:\n\n```jsx\nimport { useState, useEffect } from 'react';\n\nfunction useDelayedValue(value, delay) {\n  const [delayedValue, setDelayedValue] = useState(value);\n\n  useEffect(() => {\n    setTimeout(() => {\n      setDelayedValue(value);\n    }, delay);\n  }, [value, delay]);\n\n  return delayedValue;\n}\n\nfunction usePointerPosition() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  useEffect(() => {\n    function handleMove(e) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n  }, []);\n  return position;\n}\n\nexport default function Canvas() {\n  const pos1 = usePointerPosition();\n  const pos2 = useDelayedValue(pos1, 100);\n  const pos3 = useDelayedValue(pos2, 200);\n  const pos4 = useDelayedValue(pos3, 100);\n  const pos5 = useDelayedValue(pos3, 50);\n  return (\n    <>\n      <Dot position={pos1} opacity={1} />\n      <Dot position={pos2} opacity={0.8} />\n      <Dot position={pos3} opacity={0.6} />\n      <Dot position={pos4} opacity={0.4} />\n      <Dot position={pos5} opacity={0.2} />\n    </>\n  );\n}\n\nfunction Dot({ position, opacity }) {\n  const style = {\n    position: 'absolute',\n    backgroundColor: 'pink',\n    borderRadius: '50%',\n    opacity,\n    transform: `translate(${position.x}px, ${position.y}px)`,\n    pointerEvents: 'none',\n    left: -20,\n    top: -20,\n    width: 40,\n    height: 40,\n  };\n\n  return (\n    <div style={style} />\n  );\n}\n```\n\nivi:\n\n```js\nimport {\n  createRoot, update, component, useState, useEffect, shallowEqArray, html,\n} from \"ivi\";\n\nconst ZERO: Point = { x: 0, y: 0 };\n\nfunction useDelayedValue(c) {\n  const [delayedValue, setDelayedValue] = useState(c, ZERO);\n\n  return [\n    delayedValue,\n    useEffect(c, ([value, delay]) => {\n      setTimeout(() => {\n        setDelayedValue(value);\n      }, delay);\n    }, shallowEqArray)\n  ];\n}\n\nfunction usePointerPosition(c) {\n  const [position, setPosition] = useState(c, ZERO);\n  useEffect(c, () => {\n    const onMove = (e) => {\n      setPosition({ x: e.clientX, y: e.clientY });\n    };\n    window.addEventListener('pointermove', onMove);\n    return () => window.removeEventListener('pointermove', onMove);\n  })();\n  return position;\n}\n\nconst Canvas = component((c) => {\n  const _pos1 = usePointerPosition(c);\n  const [_pos2, updatePos2] = useDelayedValue(c);\n  const [_pos3, updatePos3] = useDelayedValue(c);\n  const [_pos4, updatePos4] = useDelayedValue(c);\n  const [_pos5, updatePos5] = useDelayedValue(c);\n  return () => {\n    const pos1 = _pos1();\n    const pos2 = _pos2();\n    const pos3 = _pos3();\n    const pos4 = _pos4();\n    const pos5 = _pos5();\n    updatePos2([pos1, 100]);\n    updatePos3([pos2, 200]);\n    updatePos4([pos3, 100]);\n    updatePos5([pos3, 50]);\n    return [\n      Dot({ position: pos1, opacity: 1 }),\n      Dot({ position: pos2, opacity: 0.8 }),\n      Dot({ position: pos3, opacity: 0.6 }),\n      Dot({ position: pos4, opacity: 0.4 }),\n      Dot({ position: pos5, opacity: 0.2 }),\n    ];\n  };\n});\n\nconst Dot = ({ position, opacity }) => html`\n  <div\n    ~position=\"absolute\"\n    ~background-color=\"pink\"\n    ~border-radius=\"50%\"\n    ~pointer-events=\"none\"\n    ~left=\"-20px\"\n    ~top=\"-20px\"\n    ~width=\"40px\"\n    ~height=\"40px\"\n    ~opacity=${opacity}\n    ~transform=${`translate(${position.x}px,${position.y}px)`}\n  />\n`;\n\nupdate(\n  createRoot(document.getElementById(\"app\")!),\n  Canvas(),\n);\n```\n"
  },
  {
    "path": "justfile",
    "content": "#!/usr/bin/env -S just --justfile\n\nexport PATH := justfile_directory() + \"/node_modules/.bin/:\" + env_var('PATH')\nset shell := [\"bash\", \"-cu\"]\n\n_default:\n  @just --list -u\n\nmod napi\n\ninit:\n  bun install\n\ntest:\n  bun test tests/runtime/\n\ntsc *FLAGS:\n  tsc -b {{FLAGS}}\n\npublish *NPM_FLAGS:\n  just _pkg-publish ./packages/@ivi/rolldown/ {{NPM_FLAGS}}\n  just _pkg-publish ./packages/@ivi/rollup-plugin/ {{NPM_FLAGS}}\n  just _pkg-publish ./packages/@ivi/vite-plugin/ {{NPM_FLAGS}}\n  just _pkg-publish ./packages/ivi/ {{NPM_FLAGS}}\n\n_pkg-set-version dir ver:\n  echo \"$(jq --arg v \"{{ver}}\" '.version = $v' {{dir}}/package.json)\" > {{dir}}/package.json\n\n_pkg-publish dir *NPM_FLAGS:\n  #!/usr/bin/env bash\n  set -euo pipefail;\n  cd {{dir}}\n  if [ \"$(npm --no-workspaces view $(jq -r .name package.json) version)\" != \"$(jq -r .version package.json)\" ]; then\n    filename=\"${PWD}/archive.tgz\"\n    bun pm pack --filename ${filename}\n    npm --no-workspaces publish ${filename} {{NPM_FLAGS}}\n    rm -rf ${filename}\n  fi\n"
  },
  {
    "path": "napi.just",
    "content": "PKG_DIR := \"./packages/@ivi/compiler\"\n\nbuild *FLAGS:\n  napi build --platform --esm --manifest-path {{PKG_DIR}}/Cargo.toml --package-json-path {{PKG_DIR}}/package.json --output-dir {{PKG_DIR}} {{FLAGS}}\n\ntest:\n  bun test ./tests/compiler/\n\ncreate-npm-dirs:\n  napi create-npm-dirs --npm-dir {{PKG_DIR}}/packages --package-json-path {{PKG_DIR}}/package.json\n\nartifacts:\n  napi artifacts --npm-dir {{PKG_DIR}}/packages --package-json-path {{PKG_DIR}}/package.json --output-dir ./napi-artifacts\n\nupdate-versions version:\n  just _pkg-set-version {{PKG_DIR}}/packages/darwin-arm64 {{version}}\n  just _pkg-set-version {{PKG_DIR}}/packages/darwin-x64 {{version}}\n  just _pkg-set-version {{PKG_DIR}}/packages/linux-arm64-gnu {{version}}\n  just _pkg-set-version {{PKG_DIR}}/packages/linux-x64-gnu {{version}}\n  just _pkg-set-version {{PKG_DIR}}/packages/win32-arm64-msvc {{version}}\n  just _pkg-set-version {{PKG_DIR}}/packages/win32-x64-msvc {{version}}\n\nincrement-versions increment:\n  cd {{PKG_DIR}} && bun pm version {{increment}} --no-git-tag-version\n  just napi update-versions $(jq -r .version {{PKG_DIR}}/package.json)\n\npublish *NPM_FLAGS:\n  just _pkg-publish {{PKG_DIR}}/packages/darwin-arm64 {{NPM_FLAGS}}\n  just _pkg-publish {{PKG_DIR}}/packages/darwin-x64 {{NPM_FLAGS}}\n  just _pkg-publish {{PKG_DIR}}/packages/linux-arm64-gnu {{NPM_FLAGS}}\n  just _pkg-publish {{PKG_DIR}}/packages/linux-x64-gnu {{NPM_FLAGS}}\n  just _pkg-publish {{PKG_DIR}}/packages/win32-arm64-msvc {{NPM_FLAGS}}\n  just _pkg-publish {{PKG_DIR}}/packages/win32-x64-msvc {{NPM_FLAGS}}\n  just _pkg-publish {{PKG_DIR}}/ {{NPM_FLAGS}}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": true,\n  \"workspaces\": {\n    \"packages\": [\n      \"packages/@ivi/*\",\n      \"packages/@ivi/compiler/packages/*\",\n      \"packages/ivi\",\n      \"tests\"\n    ],\n    \"catalog\": {\n      \"rollup\": \"^4.52.5\",\n      \"rolldown\": \"^1.0.0-beta.43\"\n    }\n  },\n  \"scripts\": {\n    \"clean\": \"rm -rf ./node_modules ./tsconfig.tsbuildinfo\",\n    \"build\": \"tsc -b\",\n    \"build:watch\": \"tsc -b -w --pretty\",\n    \"build:force\": \"tsc -b --force --pretty\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^25.6.0\",\n    \"@types/bun\": \"^1.3.12\",\n    \"@napi-rs/cli\": \"^3.6.1\",\n    \"typescript\": \"^6.0.2\"\n  }\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/.gitignore",
    "content": "ivi-compiler.*.node"
  },
  {
    "path": "packages/@ivi/compiler/Cargo.toml",
    "content": "\n[package]\npublish = false\nname = \"ivi-compiler-napi\"\nversion = \"0.0.1\"\nauthors = [\"Boris Kaul <localvoid@gmail.com>\"]\nedition = \"2024\"\nlicense = \"MIT\"\nhomepage = \"https://github.com/localvoid/ivi\"\nrepository = \"https://github.com/localvoid/ivi\"\ndescription = \"ivi compiler NAPI bindings\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nrustc-hash.workspace = true\nnapi.workspace = true\nnapi-derive.workspace = true\nivi_compiler.workspace = true\n\n[build-dependencies]\nnapi-build.workspace = true\n\n[lints]\nworkspace = true"
  },
  {
    "path": "packages/@ivi/compiler/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2025 Boris Kaul <localvoid@gmail.com>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE."
  },
  {
    "path": "packages/@ivi/compiler/README.md",
    "content": "NAPI bindings for the [ivi](https://github.com/localvoid/ivi) template compiler.\n"
  },
  {
    "path": "packages/@ivi/compiler/build.rs",
    "content": "fn main() {\n  napi_build::setup();\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/index.d.ts",
    "content": "/* auto-generated by NAPI-RS */\n/* eslint-disable */\nexport declare class CompilerOutput {\n  code: string\n  map: string\n}\n\nexport declare class TemplateCompiler {\n  constructor(options?: CompilerOptions | undefined | null)\n  transform(sourceText: string, moduleType: string): Promise<CompilerOutput>\n  renderStart(): void\n  renderChunk(sourceText: string): Promise<CompilerOutput>\n}\n\nexport interface CompilerOptions {\n  dedupeStrings?: boolean\n  oveo?: boolean\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/index.js",
    "content": "// prettier-ignore\n/* eslint-disable */\n// @ts-nocheck\n/* auto-generated by NAPI-RS */\n\nimport { createRequire } from 'node:module'\nconst require = createRequire(import.meta.url)\nconst __dirname = new URL('.', import.meta.url).pathname\n\nconst { readFileSync } = require('node:fs')\nlet nativeBinding = null\nconst loadErrors = []\n\nconst isMusl = () => {\n  let musl = false\n  if (process.platform === 'linux') {\n    musl = isMuslFromFilesystem()\n    if (musl === null) {\n      musl = isMuslFromReport()\n    }\n    if (musl === null) {\n      musl = isMuslFromChildProcess()\n    }\n  }\n  return musl\n}\n\nconst isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')\n\nconst isMuslFromFilesystem = () => {\n  try {\n    return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')\n  } catch {\n    return null\n  }\n}\n\nconst isMuslFromReport = () => {\n  let report = null\n  if (typeof process.report?.getReport === 'function') {\n    process.report.excludeNetwork = true\n    report = process.report.getReport()\n  }\n  if (!report) {\n    return null\n  }\n  if (report.header && report.header.glibcVersionRuntime) {\n    return false\n  }\n  if (Array.isArray(report.sharedObjects)) {\n    if (report.sharedObjects.some(isFileMusl)) {\n      return true\n    }\n  }\n  return false\n}\n\nconst isMuslFromChildProcess = () => {\n  try {\n    return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')\n  } catch (e) {\n    // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false\n    return false\n  }\n}\n\nfunction requireNative() {\n  if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {\n    try {\n      return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);\n    } catch (err) {\n      loadErrors.push(err)\n    }\n  } else if (process.platform === 'android') {\n    if (process.arch === 'arm64') {\n      try {\n        return require('./ivi-compiler.android-arm64.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-android-arm64')\n        const bindingPackageVersion = require('@ivi/compiler-android-arm64/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else if (process.arch === 'arm') {\n      try {\n        return require('./ivi-compiler.android-arm-eabi.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-android-arm-eabi')\n        const bindingPackageVersion = require('@ivi/compiler-android-arm-eabi/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else {\n      loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))\n    }\n  } else if (process.platform === 'win32') {\n    if (process.arch === 'x64') {\n      if (process.report?.getReport?.()?.header?.osName?.startsWith?.('MINGW')) {\n        try {\n        return require('./ivi-compiler.win32-x64-gnu.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-win32-x64-gnu')\n        const bindingPackageVersion = require('@ivi/compiler-win32-x64-gnu/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      } else {\n        try {\n        return require('./ivi-compiler.win32-x64-msvc.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-win32-x64-msvc')\n        const bindingPackageVersion = require('@ivi/compiler-win32-x64-msvc/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      }\n    } else if (process.arch === 'ia32') {\n      try {\n        return require('./ivi-compiler.win32-ia32-msvc.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-win32-ia32-msvc')\n        const bindingPackageVersion = require('@ivi/compiler-win32-ia32-msvc/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else if (process.arch === 'arm64') {\n      try {\n        return require('./ivi-compiler.win32-arm64-msvc.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-win32-arm64-msvc')\n        const bindingPackageVersion = require('@ivi/compiler-win32-arm64-msvc/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else {\n      loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))\n    }\n  } else if (process.platform === 'darwin') {\n    try {\n      return require('./ivi-compiler.darwin-universal.node')\n    } catch (e) {\n      loadErrors.push(e)\n    }\n    try {\n      const binding = require('@ivi/compiler-darwin-universal')\n      const bindingPackageVersion = require('@ivi/compiler-darwin-universal/package.json').version\n      if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n        throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n      }\n      return binding\n    } catch (e) {\n      loadErrors.push(e)\n    }\n    if (process.arch === 'x64') {\n      try {\n        return require('./ivi-compiler.darwin-x64.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-darwin-x64')\n        const bindingPackageVersion = require('@ivi/compiler-darwin-x64/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else if (process.arch === 'arm64') {\n      try {\n        return require('./ivi-compiler.darwin-arm64.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-darwin-arm64')\n        const bindingPackageVersion = require('@ivi/compiler-darwin-arm64/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else {\n      loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))\n    }\n  } else if (process.platform === 'freebsd') {\n    if (process.arch === 'x64') {\n      try {\n        return require('./ivi-compiler.freebsd-x64.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-freebsd-x64')\n        const bindingPackageVersion = require('@ivi/compiler-freebsd-x64/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else if (process.arch === 'arm64') {\n      try {\n        return require('./ivi-compiler.freebsd-arm64.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-freebsd-arm64')\n        const bindingPackageVersion = require('@ivi/compiler-freebsd-arm64/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else {\n      loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))\n    }\n  } else if (process.platform === 'linux') {\n    if (process.arch === 'x64') {\n      if (isMusl()) {\n        try {\n          return require('./ivi-compiler.linux-x64-musl.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-x64-musl')\n          const bindingPackageVersion = require('@ivi/compiler-linux-x64-musl/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      } else {\n        try {\n          return require('./ivi-compiler.linux-x64-gnu.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-x64-gnu')\n          const bindingPackageVersion = require('@ivi/compiler-linux-x64-gnu/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      }\n    } else if (process.arch === 'arm64') {\n      if (isMusl()) {\n        try {\n          return require('./ivi-compiler.linux-arm64-musl.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-arm64-musl')\n          const bindingPackageVersion = require('@ivi/compiler-linux-arm64-musl/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      } else {\n        try {\n          return require('./ivi-compiler.linux-arm64-gnu.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-arm64-gnu')\n          const bindingPackageVersion = require('@ivi/compiler-linux-arm64-gnu/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      }\n    } else if (process.arch === 'arm') {\n      if (isMusl()) {\n        try {\n          return require('./ivi-compiler.linux-arm-musleabihf.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-arm-musleabihf')\n          const bindingPackageVersion = require('@ivi/compiler-linux-arm-musleabihf/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      } else {\n        try {\n          return require('./ivi-compiler.linux-arm-gnueabihf.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-arm-gnueabihf')\n          const bindingPackageVersion = require('@ivi/compiler-linux-arm-gnueabihf/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      }\n    } else if (process.arch === 'loong64') {\n      if (isMusl()) {\n        try {\n          return require('./ivi-compiler.linux-loong64-musl.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-loong64-musl')\n          const bindingPackageVersion = require('@ivi/compiler-linux-loong64-musl/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      } else {\n        try {\n          return require('./ivi-compiler.linux-loong64-gnu.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-loong64-gnu')\n          const bindingPackageVersion = require('@ivi/compiler-linux-loong64-gnu/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      }\n    } else if (process.arch === 'riscv64') {\n      if (isMusl()) {\n        try {\n          return require('./ivi-compiler.linux-riscv64-musl.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-riscv64-musl')\n          const bindingPackageVersion = require('@ivi/compiler-linux-riscv64-musl/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      } else {\n        try {\n          return require('./ivi-compiler.linux-riscv64-gnu.node')\n        } catch (e) {\n          loadErrors.push(e)\n        }\n        try {\n          const binding = require('@ivi/compiler-linux-riscv64-gnu')\n          const bindingPackageVersion = require('@ivi/compiler-linux-riscv64-gnu/package.json').version\n          if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n            throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n          }\n          return binding\n        } catch (e) {\n          loadErrors.push(e)\n        }\n      }\n    } else if (process.arch === 'ppc64') {\n      try {\n        return require('./ivi-compiler.linux-ppc64-gnu.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-linux-ppc64-gnu')\n        const bindingPackageVersion = require('@ivi/compiler-linux-ppc64-gnu/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else if (process.arch === 's390x') {\n      try {\n        return require('./ivi-compiler.linux-s390x-gnu.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-linux-s390x-gnu')\n        const bindingPackageVersion = require('@ivi/compiler-linux-s390x-gnu/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else {\n      loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))\n    }\n  } else if (process.platform === 'openharmony') {\n    if (process.arch === 'arm64') {\n      try {\n        return require('./ivi-compiler.openharmony-arm64.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-openharmony-arm64')\n        const bindingPackageVersion = require('@ivi/compiler-openharmony-arm64/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else if (process.arch === 'x64') {\n      try {\n        return require('./ivi-compiler.openharmony-x64.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-openharmony-x64')\n        const bindingPackageVersion = require('@ivi/compiler-openharmony-x64/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else if (process.arch === 'arm') {\n      try {\n        return require('./ivi-compiler.openharmony-arm.node')\n      } catch (e) {\n        loadErrors.push(e)\n      }\n      try {\n        const binding = require('@ivi/compiler-openharmony-arm')\n        const bindingPackageVersion = require('@ivi/compiler-openharmony-arm/package.json').version\n        if (bindingPackageVersion !== '0.1.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {\n          throw new Error(`Native binding package version mismatch, expected 0.1.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)\n        }\n        return binding\n      } catch (e) {\n        loadErrors.push(e)\n      }\n    } else {\n      loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))\n    }\n  } else {\n    loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))\n  }\n}\n\nnativeBinding = requireNative()\n\nif (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {\n  let wasiBinding = null\n  let wasiBindingError = null\n  try {\n    wasiBinding = require('./ivi-compiler.wasi.cjs')\n    nativeBinding = wasiBinding\n  } catch (err) {\n    if (process.env.NAPI_RS_FORCE_WASI) {\n      wasiBindingError = err\n    }\n  }\n  if (!nativeBinding) {\n    try {\n      wasiBinding = require('@ivi/compiler-wasm32-wasi')\n      nativeBinding = wasiBinding\n    } catch (err) {\n      if (process.env.NAPI_RS_FORCE_WASI) {\n        wasiBindingError.cause = err\n        loadErrors.push(err)\n      }\n    }\n  }\n  if (process.env.NAPI_RS_FORCE_WASI === 'error' && !wasiBinding) {\n    const error = new Error('WASI binding not found and NAPI_RS_FORCE_WASI is set to error')\n    error.cause = wasiBindingError\n    throw error\n  }\n}\n\nif (!nativeBinding) {\n  if (loadErrors.length > 0) {\n    throw new Error(\n      `Cannot find native binding. ` +\n        `npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +\n        'Please try `npm i` again after removing both package-lock.json and node_modules directory.',\n      {\n        cause: loadErrors.reduce((err, cur) => {\n          cur.cause = err\n          return cur\n        }),\n      },\n    )\n  }\n  throw new Error(`Failed to load native binding`)\n}\n\nconst { CompilerOutput, TemplateCompiler } = nativeBinding\nexport { CompilerOutput }\nexport { TemplateCompiler }\n"
  },
  {
    "path": "packages/@ivi/compiler/package.json",
    "content": "{\n  \"name\": \"@ivi/compiler\",\n  \"version\": \"0.1.14\",\n  \"type\": \"module\",\n  \"sideEffects\": false,\n  \"exports\": {\n    \".\": \"./index.js\"\n  },\n  \"files\": [\n    \"index.d.ts\",\n    \"index.js\"\n  ],\n  \"optionalDependencies\": {\n    \"@ivi/compiler-darwin-arm64\": \"workspace:*\",\n    \"@ivi/compiler-darwin-x64\": \"workspace:*\",\n    \"@ivi/compiler-linux-arm64-gnu\": \"workspace:*\",\n    \"@ivi/compiler-linux-x64-gnu\": \"workspace:*\",\n    \"@ivi/compiler-win32-arm64-msvc\": \"workspace:*\",\n    \"@ivi/compiler-win32-x64-msvc\": \"workspace:*\"\n  },\n  \"napi\": {\n    \"binaryName\": \"ivi-compiler\",\n    \"targets\": [\n      \"x86_64-pc-windows-msvc\",\n      \"x86_64-apple-darwin\",\n      \"x86_64-unknown-linux-gnu\",\n      \"aarch64-unknown-linux-gnu\",\n      \"aarch64-apple-darwin\",\n      \"aarch64-pc-windows-msvc\"\n    ]\n  },\n  \"engines\": {\n    \"node\": \">= 20.0.0\"\n  },\n  \"scripts\": {\n    \"clean\": \"rm -rf ivi-compiler-*.node\"\n  },\n  \"description\": \"ivi compiler NAPI\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/localvoid/ivi.git\"\n  }\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/darwin-arm64/README.md",
    "content": "# `@ivi/compiler-darwin-arm64`\n\nThis is the **aarch64-apple-darwin** binary for `@ivi/compiler`\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/darwin-arm64/package.json",
    "content": "{\n  \"name\": \"@ivi/compiler-darwin-arm64\",\n  \"version\": \"0.1.14\",\n  \"cpu\": [\n    \"arm64\"\n  ],\n  \"main\": \"ivi-compiler.darwin-arm64.node\",\n  \"files\": [\n    \"ivi-compiler.darwin-arm64.node\"\n  ],\n  \"description\": \"ivi compiler NAPI\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">= 20.0.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/localvoid/ivi.git\"\n  },\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"os\": [\n    \"darwin\"\n  ]\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/darwin-x64/README.md",
    "content": "# `@ivi/compiler-darwin-x64`\n\nThis is the **x86_64-apple-darwin** binary for `@ivi/compiler`\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/darwin-x64/package.json",
    "content": "{\n  \"name\": \"@ivi/compiler-darwin-x64\",\n  \"version\": \"0.1.14\",\n  \"cpu\": [\n    \"x64\"\n  ],\n  \"main\": \"ivi-compiler.darwin-x64.node\",\n  \"files\": [\n    \"ivi-compiler.darwin-x64.node\"\n  ],\n  \"description\": \"ivi compiler NAPI\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">= 20.0.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/localvoid/ivi.git\"\n  },\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"os\": [\n    \"darwin\"\n  ]\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/linux-arm64-gnu/README.md",
    "content": "# `@ivi/compiler-linux-arm64-gnu`\n\nThis is the **aarch64-unknown-linux-gnu** binary for `@ivi/compiler`\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/linux-arm64-gnu/package.json",
    "content": "{\n  \"name\": \"@ivi/compiler-linux-arm64-gnu\",\n  \"version\": \"0.1.14\",\n  \"cpu\": [\n    \"arm64\"\n  ],\n  \"main\": \"ivi-compiler.linux-arm64-gnu.node\",\n  \"files\": [\n    \"ivi-compiler.linux-arm64-gnu.node\"\n  ],\n  \"description\": \"ivi compiler NAPI\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">= 20.0.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/localvoid/ivi.git\"\n  },\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"os\": [\n    \"linux\"\n  ],\n  \"libc\": [\n    \"glibc\"\n  ]\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/linux-x64-gnu/README.md",
    "content": "# `@ivi/compiler-linux-x64-gnu`\n\nThis is the **x86_64-unknown-linux-gnu** binary for `@ivi/compiler`\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/linux-x64-gnu/package.json",
    "content": "{\n  \"name\": \"@ivi/compiler-linux-x64-gnu\",\n  \"version\": \"0.1.14\",\n  \"cpu\": [\n    \"x64\"\n  ],\n  \"main\": \"ivi-compiler.linux-x64-gnu.node\",\n  \"files\": [\n    \"ivi-compiler.linux-x64-gnu.node\"\n  ],\n  \"description\": \"ivi compiler NAPI\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">= 20.0.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/localvoid/ivi.git\"\n  },\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"os\": [\n    \"linux\"\n  ],\n  \"libc\": [\n    \"glibc\"\n  ]\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/win32-arm64-msvc/README.md",
    "content": "# `@ivi/compiler-win32-arm64-msvc`\n\nThis is the **aarch64-pc-windows-msvc** binary for `@ivi/compiler`\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/win32-arm64-msvc/package.json",
    "content": "{\n  \"name\": \"@ivi/compiler-win32-arm64-msvc\",\n  \"version\": \"0.1.14\",\n  \"cpu\": [\n    \"arm64\"\n  ],\n  \"main\": \"ivi-compiler.win32-arm64-msvc.node\",\n  \"files\": [\n    \"ivi-compiler.win32-arm64-msvc.node\"\n  ],\n  \"description\": \"ivi compiler NAPI\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">= 20.0.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/localvoid/ivi.git\"\n  },\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"os\": [\n    \"win32\"\n  ]\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/win32-x64-msvc/README.md",
    "content": "# `@ivi/compiler-win32-x64-msvc`\n\nThis is the **x86_64-pc-windows-msvc** binary for `@ivi/compiler`\n"
  },
  {
    "path": "packages/@ivi/compiler/packages/win32-x64-msvc/package.json",
    "content": "{\n  \"name\": \"@ivi/compiler-win32-x64-msvc\",\n  \"version\": \"0.1.14\",\n  \"cpu\": [\n    \"x64\"\n  ],\n  \"main\": \"ivi-compiler.win32-x64-msvc.node\",\n  \"files\": [\n    \"ivi-compiler.win32-x64-msvc.node\"\n  ],\n  \"description\": \"ivi compiler NAPI\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">= 20.0.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/localvoid/ivi.git\"\n  },\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"os\": [\n    \"win32\"\n  ]\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/src/lib.rs",
    "content": "use ivi_compiler::{compile_chunk, compile_module};\nuse napi::{Env, bindgen_prelude::*};\nuse napi_derive::napi;\nuse rustc_hash::{FxHashMap, FxHashSet};\n\nuse std::sync::{Arc, Mutex, RwLock};\n\n#[napi]\npub struct CompilerOutput {\n    pub code: String,\n    pub map: String,\n}\n\n#[napi(object)]\npub struct CompilerOptions {\n    pub dedupe_strings: Option<bool>,\n    pub oveo: Option<bool>,\n}\n\n#[napi]\npub struct TemplateCompiler {\n    inner: Arc<CompilerState>,\n}\n\nstruct CompilerState {\n    options: ivi_compiler::CompilerOptions,\n    unique_strings: Mutex<FxHashSet<String>>,\n    indexed_strings: RwLock<FxHashMap<String, u8>>,\n}\n\n#[napi]\nimpl TemplateCompiler {\n    #[napi(constructor)]\n    pub fn new(options: Option<CompilerOptions>) -> Result<Self> {\n        let options = if let Some(options) = options {\n            ivi_compiler::CompilerOptions {\n                oveo: options.oveo.unwrap_or(false),\n                dedupe_strings: options.dedupe_strings.unwrap_or(false),\n            }\n        } else {\n            ivi_compiler::CompilerOptions::default()\n        };\n\n        Ok(Self {\n            inner: Arc::new(CompilerState {\n                options,\n                unique_strings: Mutex::default(),\n                indexed_strings: RwLock::default(),\n            }),\n        })\n    }\n\n    #[napi(ts_return_type = \"Promise<CompilerOutput>\")]\n    pub fn transform(&self, source_text: String, module_type: String) -> AsyncTask<TransformTask> {\n        AsyncTask::new(TransformTask {\n            compiler: Arc::clone(&self.inner),\n            source_text,\n            module_type,\n            dedupe_strings: self.inner.options.dedupe_strings,\n        })\n    }\n\n    #[napi]\n    pub fn render_start(&self) {\n        let unique_lock = self.inner.unique_strings.lock().unwrap();\n        let mut unique: Vec<_> = unique_lock.iter().collect();\n        unique.sort();\n        let mut strings = self.inner.indexed_strings.write().unwrap();\n        strings.clear();\n        for (i, s) in unique.iter().enumerate() {\n            strings.insert(s.to_string(), i as u8);\n        }\n        unique.clear();\n    }\n\n    #[napi(ts_return_type = \"Promise<CompilerOutput>\")]\n    pub fn render_chunk(&self, source_text: String) -> AsyncTask<RenderChunkTask> {\n        AsyncTask::new(RenderChunkTask { compiler: Arc::clone(&self.inner), source_text })\n    }\n}\n\npub struct TransformTask {\n    compiler: Arc<CompilerState>,\n    source_text: String,\n    module_type: String,\n    dedupe_strings: bool,\n}\n\nimpl Task for TransformTask {\n    type Output = CompilerOutput;\n    type JsValue = CompilerOutput;\n\n    fn compute(&mut self) -> Result<Self::Output> {\n        let mut strings = FxHashSet::default();\n        let result = compile_module(\n            &self.source_text,\n            &self.module_type,\n            &self.compiler.options,\n            &mut strings,\n        )\n        .map(|v| CompilerOutput { code: v.code, map: v.map })\n        .map_err(|err| Error::from_reason(err.to_string()))?;\n\n        if self.dedupe_strings && !strings.is_empty() {\n            let mut unique = self.compiler.unique_strings.lock().unwrap();\n            unique.extend(strings.drain());\n        }\n\n        Ok(result)\n    }\n\n    fn resolve(&mut self, _env: Env, output: CompilerOutput) -> Result<Self::JsValue> {\n        Ok(output)\n    }\n}\n\npub struct RenderChunkTask {\n    compiler: Arc<CompilerState>,\n    source_text: String,\n}\n\nimpl Task for RenderChunkTask {\n    type Output = CompilerOutput;\n    type JsValue = CompilerOutput;\n\n    fn compute(&mut self) -> Result<Self::Output> {\n        let strings = self.compiler.indexed_strings.read().unwrap();\n\n        compile_chunk(&self.source_text, &strings)\n            .map(|v| CompilerOutput { code: v.code, map: v.map })\n            .map_err(|err| Error::from_reason(err.to_string()))\n    }\n\n    fn resolve(&mut self, _env: Env, output: CompilerOutput) -> Result<Self::JsValue> {\n        Ok(output)\n    }\n}\n"
  },
  {
    "path": "packages/@ivi/compiler/tsconfig.json",
    "content": "{\n  \"extends\": \"../../../tsconfig.composite.json\",\n  \"files\": [\"./index.d.ts\"]\n}\n"
  },
  {
    "path": "packages/@ivi/identity/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-2024 Boris Kaul <localvoid@gmail.com>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "packages/@ivi/identity/README.md",
    "content": "# [ivi](https://github.com/localvoid/ivi) Identity\n\nIdentity component is used to reset internal state.\n\n## Example\n\n```ts\nimport { createRoot, update, component, useState, useEffect, html } from \"ivi\";\nimport { Identity } from \"@ivi/identity\";\n\nconst Timer = component((c) => {\n  const [time, setTime] = useState(c, 0);\n\n  useEffect(c, () => {\n    const t = setInterval(() => {\n      setTime(time() + 100);\n    }, 100);\n    return () => { clearInterval(t); };\n  })();\n\n  return () => time();\n});\n\nconst App = component((c) => {\n  const [key, setKey] = useState(c, 0);\n\n  const onClick = () => { setKey(key() + 1); };\n\n  return () => (\n    html`\n    <div class=\"App\">\n      <button @click=${onClick}>Next</button>\n      ${Identity({ key: key(), children: Timer() })}\n    </div>\n    `\n  );\n});\n\nupdate(\n  createRoot(document.body),\n  App(),\n);\n```\n"
  },
  {
    "path": "packages/@ivi/identity/package.json",
    "content": "{\n  \"name\": \"@ivi/identity\",\n  \"version\": \"0.4.0\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./dist/index.js\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\",\n    \"!dist/**/*.tsbuildinfo\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"peerDependencies\": {\n    \"ivi\": \"workspace:^\"\n  },\n  \"devDependencies\": {\n    \"ivi\": \"workspace:*\"\n  },\n  \"scripts\": {\n    \"clean\": \"rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo\"\n  },\n  \"description\": \"ivi identity component.\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"repository\": \"github:localvoid/ivi\"\n}"
  },
  {
    "path": "packages/@ivi/identity/src/index.ts",
    "content": "import { type VAny, type VComponent, component } from \"ivi\";\n\nexport interface PortalEntry {\n  v: VAny;\n}\n\nconst _renderChildren = (children: VAny) => children;\nconst _identityComponent = () => _renderChildren;\nconst A = component<VAny>(_identityComponent);\nconst B = component<VAny>(_identityComponent);\n\nexport interface IdentityProps {\n  readonly key: any;\n  readonly children: VAny;\n}\n\nexport const Identity: (props: IdentityProps) => VComponent<IdentityProps> = component<IdentityProps>(() => {\n  var _prevKey: any;\n  var _prevType = A;\n  return ({ key, children }) => {\n    if (_prevKey !== key) {\n      _prevKey = key;\n      _prevType = (_prevType === A) ? B : A;\n    }\n    return _prevType(children);\n  };\n});\n"
  },
  {
    "path": "packages/@ivi/identity/tsconfig.json",
    "content": "{\n  \"extends\": \"../../../tsconfig.composite.json\",\n  \"references\": [\n    { \"path\": \"../../ivi\" },\n  ],\n}\n"
  },
  {
    "path": "packages/@ivi/mock-dom/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-2024 Boris Kaul <localvoid@gmail.com>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "packages/@ivi/mock-dom/README.md",
    "content": "# [ivi](https://github.com/localvoid/ivi) DOM Mocking Tools\n\nThis package is designed for internal tests of the ivi library.\n"
  },
  {
    "path": "packages/@ivi/mock-dom/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"@ivi/mock-dom\",\n  \"version\": \"0.2.0\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./dist/index.js\",\n    \"./global\": \"./dist/global.js\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\",\n    \"!dist/**/*.tsbuildinfo\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"scripts\": {\n    \"clean\": \"rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo\"\n  },\n  \"description\": \"ivi DOM mocking tools.\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"repository\": \"github:localvoid/ivi\"\n}"
  },
  {
    "path": "packages/@ivi/mock-dom/src/global.ts",
    "content": "export {\n  toSnapshot,\n} from \"./index.js\";\nimport {\n  // DOMException as _DOMException,\n  Node as _Node,\n  Element as _Element,\n  HTMLElement as _HTMLElement,\n  SVGElement as _SVGElement,\n  Template as _Template,\n  DocumentFragment as _DocumentFragment,\n  Document as _Document,\n  CSSStyleDeclaration as _CSSStyleDeclaration\n} from \"./index.js\";\n\ndeclare global {\n  let requestAnimationFrame: (cb: (t: number) => void) => void;\n  let requestIdleCallback: (cb: () => void) => void;\n  // let DOMException: typeof _DOMException;\n  let Node: typeof _Node;\n  let Element: typeof _Element;\n  let HTMLElement: typeof _HTMLElement;\n  let SVGElement: typeof _SVGElement;\n  let Template: typeof _Template;\n  let DocumentFragment: typeof _DocumentFragment;\n  let Document: typeof _Document;\n  let CSSStyleDeclaration: typeof _CSSStyleDeclaration;\n  let document: _Document;\n}\n\nlet _animationFrameQueue: ((t: number) => void)[] = [];\nlet _idleCallbackQueue: (() => void)[] = [];\n\nconst requestAnimationFrame = (cb: (t: number) => void) => {\n  _animationFrameQueue.push(cb);\n};\n\nconst requestIdleCallback = (cb: () => void) => {\n  _idleCallbackQueue.push(cb);\n};\n\nexport const flushAnimationFrames = (t: number) => {\n  const queue = _animationFrameQueue;\n  if (queue.length > 0) {\n    _animationFrameQueue = [];\n    for (let i = 0; i < queue.length; i++) {\n      queue[i](t);\n    }\n  }\n};\n\nexport const flushIdleCallbacks = () => {\n  const queue = _idleCallbackQueue;\n  if (queue.length > 0) {\n    _idleCallbackQueue = [];\n    for (let i = 0; i < queue.length; i++) {\n      queue[i]();\n    }\n  }\n};\n\n\n// TODO: How to avoid (global as any). It seems that extending NodeJ.Global\n// doesn't work.\n(global as any).requestAnimationFrame = requestAnimationFrame;\n(global as any).requestIdleCallback = requestIdleCallback;\n// (global as any).DOMException = _DOMException;\n(global as any).Node = _Node;\n(global as any).Element = _Element;\n(global as any).HTMLElement = _HTMLElement;\n(global as any).SVGElement = _SVGElement;\n(global as any).Template = _Template;\n(global as any).DocumentFragment = _DocumentFragment;\n(global as any).Document = _Document;\n(global as any).CSSStyleDeclaration = _CSSStyleDeclaration;\n\n(global as any).document = new _Document();\n\nexport const trace = (fn: () => void): string[] => {\n  let log;\n  try {\n    document._startTracing();\n    fn();\n  } finally {\n    log = document._log;\n    document._stopTracing();\n  }\n  return log!;\n};\n\nexport const reset = () => {\n  document._reset();\n  _animationFrameQueue = [];\n  _idleCallbackQueue = [];\n};\n\nexport const emit = (node: any, type: string) => {\n  let target: _Node | null = node;\n  while (target !== null) {\n    if (target._eventHandlers !== null) {\n      const handlers = target._eventHandlers.get(type);\n      if (handlers !== void 0) {\n        for (const h of handlers) {\n          h();\n        }\n      }\n    }\n    target = target._parentNode;\n  }\n};\n"
  },
  {
    "path": "packages/@ivi/mock-dom/src/index.ts",
    "content": "export enum NodeType {\n  Element = 1,\n  Attribute = 2,\n  Text = 3,\n  CData = 4,\n  EntityReference = 5,\n  ProcessingInstruction = 7,\n  Comment = 8,\n  Document = 9,\n  DocumentFragment = 11,\n}\n\n// export class DOMException extends Error {\n//   constructor(msg: string) {\n//     super(msg);\n//   }\n// }\n\nexport abstract class Node {\n  readonly uid: number;\n  _document: Document;\n  _nodeType: NodeType;\n  _nodeName: string;\n  _nodeValue: any;\n  _parentNode: Node | null;\n  _previousSibling: Node | null;\n  _nextSibling: Node | null;\n  _firstChild: Node | null;\n  _lastChild: Node | null;\n  _eventHandlers: Map<string, EventHandler[]> | null;\n\n  constructor(\n    doc: Document,\n    uid: number,\n    nodeType: NodeType,\n    nodeName: string,\n    eventHandlers: Map<string, EventHandler[]> | null = null,\n  ) {\n    this.uid = uid;\n    this._document = doc;\n    this._nodeType = nodeType;\n    this._nodeName = nodeName;\n    this._nodeValue = null;\n    this._parentNode = null;\n    this._previousSibling = null;\n    this._nextSibling = null;\n    this._firstChild = null;\n    this._lastChild = null;\n    this._eventHandlers = eventHandlers;\n  }\n\n  get nodeType(): number {\n    this._trace(`Node.nodeType => ${this._nodeType}`);\n    return this._nodeType;\n  }\n\n  get nodeName(): string {\n    this._trace(`Node.nodeName => \"${this._nodeName}\"`);\n    return this._nodeName;\n  }\n\n  get nodeValue(): any {\n    this._trace(`Node.nodeValue => \"${this._nodeValue}\"`);\n    return this._nodeValue;\n  }\n\n  set nodeValue(s: any) {\n    this._trace(`Node.nodeValue = ${JSON.stringify(s)}`);\n    this._nodeValue = s.toString();\n  }\n\n  get parentNode(): Node | null {\n    this._trace(`Node.parentNode => ${this._parentNode === null ? \"null\" : this._parentNode.uid}`);\n    return this._parentNode;\n  }\n\n  get previousSibling(): Node | null {\n    this._trace(`Node.previousSibling => ${this._previousSibling === null ? \"null\" : this._previousSibling.uid}`);\n    return this._previousSibling;\n  }\n\n  get nextSibling(): Node | null {\n    this._trace(`Node.nextSibling => ${this._nextSibling === null ? \"null\" : this._nextSibling.uid}`);\n    return this._nextSibling;\n  }\n\n  get firstChild(): Node | null {\n    this._trace(`Node.firstChild => ${this._firstChild === null ? \"null\" : this._firstChild.uid}`);\n    return this._firstChild;\n  }\n\n  get lastChild(): Node | null {\n    this._trace(`Node.lastChild => ${this._lastChild === null ? \"null\" : this._lastChild.uid}`);\n    return this._lastChild;\n  }\n\n  set textContent(s: string) {\n    this._trace(`Node.textContent = ${JSON.stringify(s)}`);\n    this._setTextContent(s);\n  }\n\n  _setTextContent(s: string) {\n    let node = this._firstChild;\n    while (node !== null) {\n      const next = node._nextSibling;\n      this._removeChild(node);\n      node = next;\n    }\n    if (s !== \"\") {\n      this._appendChild(document._createTextNode(s));\n    }\n  }\n\n  appendChild(child: Node) {\n    this._trace(`Node.appendChild(${child.uid})`);\n    return this._appendChild(child);\n  }\n\n  _appendChild(child: Node) {\n    this._insertBefore(child, null);\n    return child;\n  }\n\n  insertBefore(child: Node, ref: Node | null) {\n    this._trace(\n      `Node.insertBefore(${child.uid}, ${ref === null ? \"null\" : ref.uid})`\n    );\n    this._insertBefore(child, ref);\n  }\n\n  _insertBefore(child: Node, ref: Node | null = null) {\n    child._remove();\n    child._parentNode = this;\n    if (ref === null) {\n      const last = this._lastChild;\n      this._lastChild = child;\n      child._previousSibling = last;\n      if (last !== null) {\n        last._nextSibling = child;\n      } else {\n        this._firstChild = child;\n      }\n    } else {\n      const prev = ref._previousSibling;\n      if (prev !== null) {\n        prev._nextSibling = child;\n        child._previousSibling = prev;\n      } else {\n        this._firstChild = child;\n      }\n      ref._previousSibling = child;\n      child._nextSibling = ref;\n    }\n    return child;\n  }\n\n  removeChild(child: Node) {\n    this._trace(`Node.removeChild(${child.uid})`);\n    return this._removeChild(child);\n  }\n\n  _removeChild(child: Node) {\n    if (child._parentNode?.uid !== this.uid) {\n      throw new Error(\"Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.\");\n    }\n    const prev = child._previousSibling;\n    const next = child._nextSibling;\n    if (prev !== null) {\n      prev._nextSibling = next;\n    } else {\n      this._firstChild = next;\n    }\n    if (next !== null) {\n      next._previousSibling = prev;\n    } else {\n      this._lastChild = prev;\n    }\n    child._parentNode = null;\n    child._previousSibling = null;\n    child._nextSibling = null;\n    return child;\n  }\n\n  remove() {\n    this._trace(\"Node.remove()\");\n    this._remove();\n  }\n\n  _remove() {\n    if (this._parentNode !== null) {\n      this._parentNode._removeChild(this);\n    }\n  }\n\n  _trace(s: string) {\n    this._document._trace(`[${this.uid}] ${s}`);\n  }\n\n  cloneNode(deep: boolean): Node {\n    const n = this._cloneNode(deep);\n    this._trace(`Node.cloneNode(${deep}) => ${n.uid}`);\n    return n;\n  }\n\n  _cloneNode(deep: boolean): Node {\n    return cloneNode(this, deep);\n  }\n\n  contains(node: Node | null): boolean {\n    const r = this._contains(node);\n    this._trace(`Node.contains(${node?.uid ?? \"null\"}) => ${r}`);\n    return r;\n  }\n\n  _contains(node: Node | null): boolean {\n    while (node !== null) {\n      if (this.uid === node.uid) {\n        return true;\n      }\n      node = node._parentNode;\n    }\n    return false;\n  }\n}\n\nexport class Text extends Node {\n  constructor(document: Document, uid: number, text: string = \"\") {\n    super(document, uid, NodeType.Text, \"\");\n    this._nodeValue = text;\n  }\n}\n\nexport class Comment extends Node {\n  constructor(document: Document, uid: number) {\n    super(document, uid, NodeType.Comment, \"\");\n  }\n}\n\nexport class Element extends Node {\n  _namespaceURI: string;\n  _attributes: Map<string, string>;\n  _properties: Map<string | symbol, any>;\n  _styles: Map<string, string>;\n\n  constructor(\n    document: Document,\n    uid: number,\n    nodeType: number,\n    tagName: string,\n    namespaceURI: string = \"http://www.w3.org/1999/xhtml\",\n    attributes: Map<string, string> = new Map(),\n    properties: Map<string | symbol, any> = new Map(),\n    styles: Map<string, string> = new Map(),\n    eventHandlers: Map<string, EventHandler[]> | null = null,\n  ) {\n    super(document, uid, nodeType, tagName, eventHandlers);\n    this._namespaceURI = namespaceURI;\n    this._attributes = attributes;\n    this._properties = properties;\n    this._styles = styles;\n    this._eventHandlers = eventHandlers;\n  }\n\n  get namespaceURI() {\n    this._trace(`Element.namespaceURI => \"${this._namespaceURI}\"`);\n    return this._namespaceURI;\n  }\n\n  get className(): string {\n    const r = this._getAttribute(\"class\") ?? \"\";\n    this._trace(`Element.className => \"${r}\"`);\n    return r;\n  }\n\n  set className(value: string) {\n    this._trace(`Element.className = ${JSON.stringify(value)}`);\n    this._setAttribute(\"class\", value);\n  }\n\n  setAttribute(key: string, value: string) {\n    this._trace(`Element.setAttribute(\"${key}\", ${JSON.stringify(value)})`);\n    this._setAttribute(key, \"\" + value);\n  }\n\n  _setAttribute(key: string, value: string) {\n    this._attributes.set(key, value);\n  }\n\n  getAttribute(key: string): string | null {\n    const v = this._getAttribute(key);\n    this._trace(`Element.getAttribute(\"${key}\") => ${JSON.stringify(v)}`);\n    return v;\n  }\n\n  _getAttribute(key: string): string | null {\n    return this._attributes.get(key) ?? null;\n  }\n\n  setProperty(key: string | symbol, value: any) {\n    this._trace(`Element.setProperty(\"${key.toString()}\", ${JSON.stringify(value)})`);\n    this._setProperty(key, value);\n  }\n\n  _setProperty(key: string | symbol, value: any) {\n    this._properties.set(key, value);\n  }\n\n  getProperty(key: string) {\n    const v = this._getProperty(key);\n    this._trace(`Element.getProperty(\"${key.toString()}\") => ${JSON.stringify(v)}`);\n    return v;\n  }\n\n  _getProperty(key: string): any {\n    const prop = this._properties.get(key);\n    if (prop !== void 0) {\n      return prop;\n    }\n    return this._attributes.get(key);\n  }\n\n  removeAttribute(key: string) {\n    this._trace(`Element.removeAttribute(\"${key}\")`);\n    this._removeAttribute(key);\n  }\n\n  _removeAttribute(key: string) {\n    this._attributes.delete(key);\n  }\n\n  addEventListener(type: string, handler: EventHandler) {\n    this._trace(`Element.addEventListener(\"${type}\", ${handler.name})`);\n    this._addEventListener(type, handler);\n  }\n\n  _addEventListener(type: string, handler: EventHandler) {\n    if (this._eventHandlers === null) {\n      this._eventHandlers = new Map();\n      this._eventHandlers.set(type, [handler]);\n    } else {\n      let handlers = this._eventHandlers.get(type);\n      if (handlers === void 0) {\n        this._eventHandlers.set(type, [handler]);\n      } else {\n        handlers.push(handler);\n      }\n    }\n  }\n\n  removeEventListener(type: string, handler: EventHandler) {\n    this._trace(`Element.removeEventListener(\"${type}\", ${handler.name})`);\n    this._removeEventListener(type, handler);\n  }\n\n  _removeEventListener(type: string, handler: EventHandler) {\n    if (this._eventHandlers !== null) {\n      const handlers = this._eventHandlers.get(type);\n      if (handlers !== void 0) {\n        const idx = handlers.findIndex((h) => h === handler);\n        if (idx !== -1) {\n          handlers.splice(idx, 1);\n        }\n      }\n    }\n  }\n\n  set innerHTML(html: string) {\n    this._trace(`Element.innerHTML = ${JSON.stringify(html)}`);\n    this._setInnerHTML(html);\n  }\n\n  _setInnerHTML(html: string) {\n    const fragment = parseHTML(this._document, html, this._namespaceURI);\n    this._firstChild = null;\n    this._lastChild = null;\n    let node = fragment._firstChild;\n    while (node !== null) {\n      this._appendChild(node);\n      node = node._nextSibling;\n    }\n  }\n}\n\nconst ELEMENT_PROXY_HANDLER: ProxyHandler<Element> = {\n  get(target, p) {\n    if (p in target) {\n      return (target as any)[p];\n    }\n    return target.getProperty(p as string) ?? void 0;\n  },\n  set(target, p, newValue) {\n    if (p in target) {\n      (target as any)[p] = newValue;\n    } else {\n      target.setProperty(p, newValue);\n    }\n    return true;\n  },\n};\n\nexport class HTMLElement extends Element {\n  constructor(\n    document: Document,\n    uid: number,\n    tagName: string,\n    attributes: Map<string, string> = new Map(),\n    properties: Map<string | symbol, any> = new Map(),\n    styles: Map<string, string> = new Map(),\n    eventHandlers: Map<string, EventHandler[]> | null = null,\n  ) {\n    super(\n      document,\n      uid,\n      NodeType.Element,\n      tagName,\n      \"http://www.w3.org/1999/xhtml\",\n      attributes,\n      properties,\n      styles,\n      eventHandlers,\n    );\n  }\n\n  get style() {\n    this._trace(`HTMLElement.style`);\n    return this._getStyle();\n  }\n\n  _getStyle() {\n    return new CSSStyleDeclaration(this, this._styles);\n  }\n}\nexport class SVGElement extends Element {\n  constructor(\n    document: Document,\n    uid: number,\n    tagName: string,\n    attributes: Map<string, string> = new Map(),\n    properties: Map<string | symbol, any> = new Map(),\n    styles: Map<string, string> = new Map(),\n    eventHandlers: Map<string, EventHandler[]> | null = null,\n  ) {\n    super(\n      document,\n      uid,\n      NodeType.Element,\n      tagName,\n      \"http://www.w3.org/2000/svg\",\n      attributes,\n      properties,\n      styles,\n      eventHandlers,\n    );\n  }\n\n  get style() {\n    this._trace(`SVGElement.style`);\n    return this._getStyle();\n  }\n\n  _getStyle() {\n    return new CSSStyleDeclaration(this, this._styles);\n  }\n}\n\nexport class DocumentFragment extends Node {\n  constructor(document: Document, uid: number) {\n    super(document, uid, NodeType.DocumentFragment, \"\");\n  }\n}\n\nexport class Template extends Element {\n  _content: DocumentFragment;\n\n  constructor(document: Document, uid: number) {\n    super(document, uid, NodeType.Element, \"TEMPLATE\");\n    this._content = document._createDocumentFragment();\n  }\n\n  get content() {\n    this._trace(`Template.content`);\n    return this._content;\n  }\n\n  override set innerHTML(html: string) {\n    this._trace(`Template.innerHTML = \"${html}\"`);\n    this._setInnerHTML(html);\n  }\n\n  override _setInnerHTML(html: string) {\n    const frag = parseHTML(this._document, html, this._namespaceURI);\n    let n = this._content._firstChild;\n    while (n !== null) {\n      n._remove();\n      n = n._nextSibling;\n    }\n\n    n = frag._firstChild;\n    while (n !== null) {\n      this._content._appendChild(n);\n      n = n._nextSibling;\n    }\n  }\n}\n\nexport class Document extends Element {\n  _log: string[] | null;\n  _nextUid: number;\n  _body: Element;\n\n  constructor() {\n    super(null!, -9, NodeType.Document, \"#document\");\n    this._log = null;\n    this._nextUid = -8;\n    this._body = this._createElement(\"body\");\n  }\n\n  get body() {\n    return this._body;\n  }\n\n  createElement(tagName: string) {\n    const n = this._createElement(tagName);\n    this._trace(`createElement(\"${tagName}\") => ${n.uid}`);\n    return n;\n  }\n\n  _createElement(tagName: string) {\n    if (tagName === \"template\") {\n      return new Template(this, this._nextUid++);\n    }\n    return new Proxy(\n      new HTMLElement(this, this._nextUid++, tagName.toUpperCase()),\n      ELEMENT_PROXY_HANDLER,\n    );\n  }\n\n  createElementNS(namespace: string, tagName: string) {\n    const n = this._createElementNS(namespace, tagName);\n    this._trace(`createElementNS(\"${namespace}\", \"${tagName}\") => ${n.uid}`);\n    return n;\n  }\n\n  _createElementNS(namespace: string, tagName: string) {\n    if (namespace === \"http://www.w3.org/2000/svg\") {\n      return new Proxy(\n        new SVGElement(this, this._nextUid++, tagName.toUpperCase()),\n        ELEMENT_PROXY_HANDLER,\n      );\n    }\n    return new Proxy(\n      new Element(\n        this,\n        this._nextUid++,\n        NodeType.Element,\n        tagName.toUpperCase(),\n        namespace,\n      ),\n      ELEMENT_PROXY_HANDLER,\n    );\n  }\n\n  createTextNode(text: string) {\n    const n = this._createTextNode(text);\n    this._trace(`createTextNode(${JSON.stringify(text)}) => ${n.uid}`);\n    return n;\n  }\n\n  _createTextNode(text: string) {\n    return new Text(this, this._nextUid++, text);\n  }\n\n  createDocumentFragment() {\n    const n = this._createDocumentFragment();\n    this._trace(`createDocumentFragment() => ${n.uid}`);\n    return n;\n  }\n\n  _createDocumentFragment() {\n    return new DocumentFragment(this, this._nextUid++);\n  }\n\n  createComment() {\n    const n = this._createComment();\n    this._trace(`createComment() => ${n.uid}`);\n    return n;\n  }\n\n  _createComment() {\n    return new Comment(this, this._nextUid++);\n  }\n\n  _reset() {\n    this._log = null;\n    this._nextUid = 1;\n    this._body = this._createElement(\"body\");\n  }\n\n  override _trace(s: string) {\n    if (this._log !== null) {\n      this._log.push(s);\n    }\n  }\n\n  _startTracing() {\n    this._log = [];\n  }\n\n  _stopTracing() {\n    this._log = null;\n  }\n}\n\nexport type EventHandler = () => void;\n\nexport class CSSStyleDeclaration {\n  _element: Element;\n  _styles: Map<string, string>;\n\n  constructor(element: Element, styles: Map<string, string>) {\n    this._element = element;\n    this._styles = styles;\n  }\n\n  setProperty(key: string, value: string) {\n    this._element._trace(`style.setProperty(\"${key}\", \"${value}\")`);\n    this._setProperty(key, \"\" + value);\n  }\n\n  _setProperty(key: string, value: string) {\n    this._styles.set(key, value);\n  }\n\n  removeProperty(key: string) {\n    this._element._trace(`style.removeProperty(\"${key}\")`);\n    this._removeProperty(key);\n  }\n\n  _removeProperty(key: string) {\n    this._styles.delete(key);\n  }\n\n  getPropertyValue(key: string) {\n    const v = this._getPropertyValue(key);\n    this._element._trace(`style.getPropertyValue(${key}) => \"${v}\"`);\n    return v;\n  }\n\n  _getPropertyValue(key: string): string {\n    return this._styles.get(key) ?? \"\";\n  }\n}\n\nconst cloneNode = (node: Node, deep: boolean) => {\n  const uid = node._document._nextUid++;\n  if (node instanceof Text) {\n    const n = new Text(node._document, uid, node._nodeValue);\n    return n;\n  }\n\n  if (node instanceof HTMLElement) {\n    const n = new HTMLElement(\n      node._document,\n      uid,\n      node._nodeName,\n      new Map(node._attributes.entries()),\n      new Map(node._properties.entries()),\n      new Map(node._styles.entries()),\n      node._eventHandlers === null\n        ? new Map()\n        : new Map(node._eventHandlers.entries()),\n    );\n    if (deep) {\n      _cloneChildren(node, n);\n    }\n    return n;\n  }\n\n  if (node instanceof SVGElement) {\n    const n = new SVGElement(\n      node._document,\n      uid,\n      node._nodeName,\n      new Map(node._attributes.entries()),\n      new Map(node._properties.entries()),\n      new Map(node._styles.entries()),\n      node._eventHandlers === null\n        ? new Map()\n        : new Map(node._eventHandlers.entries()),\n    );\n    if (deep) {\n      _cloneChildren(node, n);\n    }\n    return n;\n  }\n\n  if (node instanceof Element) {\n    const n = new Element(\n      node._document,\n      uid,\n      node._nodeType,\n      node._nodeName,\n      node._namespaceURI,\n      new Map(node._attributes.entries()),\n      new Map(node._properties.entries()),\n      new Map(node._styles.entries()),\n      node._eventHandlers === null\n        ? new Map()\n        : new Map(node._eventHandlers.entries()),\n    );\n    if (deep) {\n      _cloneChildren(node, n);\n    }\n    return n;\n  }\n\n  if (node instanceof Comment) {\n    const n = new Comment(node._document, uid);\n    return n;\n  }\n\n  throw Error(\"This node type doesn't support cloning\");\n};\n\n\nconst _cloneChildren = (from: Element, to: Element) => {\n  let child = from._firstChild;\n  while (child !== null) {\n    to._appendChild(cloneNode(child, true));\n    child = child._nextSibling;\n  }\n};\n\nexport const toSnapshot = (node: any): string => (\n  _toSnapshot(node as Node, 0).trimEnd()\n);\n\nconst _toSnapshot = (node: Node, depth: number) => {\n  const uid = node.uid;\n  if (node instanceof Text) {\n    return (\n      indent(depth, `<TEXT#${uid}>`) +\n      (node._nodeValue as string) +\n      `</TEXT#${uid}>\\n`\n    );\n  }\n\n  if (node instanceof Element) {\n    let s = indent(depth, `<${node._nodeName}#${uid}`);\n    let propsString = \"\";\n    const innerDepth = depth + 2;\n    node._attributes.forEach((v, k) => {\n      propsString += indent(innerDepth, `${k}=\"${v}\"\\n`);\n    });\n    node._properties.forEach((v, k) => {\n      propsString += indent(innerDepth, `.${k.toString()}=${JSON.stringify(v)}\\n`);\n    });\n    node._styles.forEach((v, k) => {\n      propsString += indent(innerDepth, `~${k}=\"${v}\"\\n`);\n    });\n    if (node._eventHandlers !== null) {\n      node._eventHandlers.forEach((v, k) => {\n        propsString += indent(innerDepth, `@${k}=[${v.map((v) => v.name).join(\", \")}]\\n`);\n      });\n    }\n    let child = node._firstChild;\n    let childrenString = \"\";\n    while (child !== null) {\n      childrenString += _toSnapshot(child, depth + 2);\n      child = child._nextSibling;\n    }\n\n    if (propsString === \"\") {\n      if (childrenString === \"\") {\n        s += \"/>\\n\";\n      } else {\n        s += \">\\n\";\n        s += childrenString;\n        s += indent(depth, `</${node.nodeName}#${uid}>\\n`);\n      }\n    } else {\n      s += \"\\n\";\n      s += propsString;\n      if (childrenString === \"\") {\n        s += indent(depth, \"/>\\n\");\n      } else {\n        s += indent(depth, \">\\n\");\n        s += childrenString;\n        s += indent(depth, `</${node.nodeName}#${uid}>\\n`);\n      }\n    }\n    return s;\n  }\n\n  if (node instanceof Comment) {\n    return indent(depth, `<!>\\n`);\n  }\n\n  let s = indent(depth, `<${node._nodeName}#${uid}>\\n`);\n  let child = node._firstChild;\n  while (child !== null) {\n    s += _toSnapshot(child, depth + 2);\n    child = child._nextSibling;\n  }\n  return s + indent(depth, `</${node._nodeName}#${uid}>\\n`);\n};\n\ninterface HTMLParserContext {\n  doc: Document;\n  s: string;\n  i: number;\n}\n\n// It is not a valid HTML parser, and it was designed this way intentionally,\n// so that we could catch extra whitespaces, etc.\nfunction parseHTML(doc: Document, html: string, namespaceURI: string): DocumentFragment {\n  const ctx: HTMLParserContext = {\n    doc,\n    s: html,\n    i: 0,\n  };\n  const fragment = doc._createDocumentFragment();\n  parseChildren(ctx, fragment, namespaceURI);\n  return fragment;\n}\n\nfunction parseChildren(\n  ctx: HTMLParserContext,\n  parent: Node,\n  namespaceURI: string,\n) {\n  const s = ctx.s;\n  while (ctx.i < s.length) {\n    const c = s.charCodeAt(ctx.i);\n    if (c === CharCode.LessThan) {\n      const i2 = ctx.i + 1;\n      if (i2 < s.length) {\n        const c2 = s.charCodeAt(i2);\n        if (c2 === CharCode.Slash) {\n          break;\n        } else if (c2 === CharCode.ExclamationMark) {\n          parent._appendChild(parseComment(ctx));\n          continue;\n        }\n      }\n      parent._appendChild(parseElement(ctx, namespaceURI));\n    } else {\n      parent._appendChild(parseText(ctx));\n    }\n  }\n}\n\nconst IDENTIFIER = /[a-zA-Z_][\\w-]*/y;\nconst ATTR_IDENTIFIER = /[&a-zA-Z_][\\w-]*/y;\n\nfunction parseElement(ctx: HTMLParserContext, namespaceURI: string): Element {\n  if (!parseCharCode(ctx, CharCode.LessThan)) {\n    throw new Error(`Invalid HTML [${ctx.i}]: expected a '<' character\\n${ctx.s}`);\n  }\n  const tagName = parseRegExp(ctx, IDENTIFIER);\n  if (tagName === void 0) {\n    throw new Error(`Invalid HTML [${ctx.i}]: expected a valid tag name\\n${ctx.s}`);\n  }\n  const element = new Proxy(\n    document._createElementNS(namespaceURI, tagName),\n    ELEMENT_PROXY_HANDLER,\n  );\n  parseSkipWhitespace(ctx);\n  parseAttributes(ctx, element);\n  if (!parseCharCode(ctx, CharCode.MoreThan)) {\n    throw new Error(`Invalid HTML [${ctx.i}]: expected a '>' character\\n${ctx.s}`);\n  }\n  if (!VOID_ELEMENTS.test(tagName)) {\n    if (tagName === \"textarea\") {\n      let start = ctx.i;\n      while (ctx.i < ctx.s.length) {\n        const c = ctx.s.charCodeAt(ctx.i);\n        if (c === CharCode.LessThan) {\n          break;\n        }\n        ctx.i++;\n      }\n      element._setProperty(\"value\", ctx.s.substring(start, ctx.i));\n    } else {\n      parseChildren(ctx, element, namespaceURI);\n    }\n    if (!parseCharCode(ctx, CharCode.LessThan)) {\n      throw new Error(`Invalid HTML [${ctx.i}]: expected a '<' character\\n${ctx.s}`);\n    }\n    if (!parseCharCode(ctx, CharCode.Slash)) {\n      throw new Error(`Invalid HTML [${ctx.i}]: expected a '/' character\\n${ctx.s}`);\n    }\n    if (!ctx.s.substring(ctx.i).startsWith(tagName)) {\n      throw new Error(`Invalid HTML [${ctx.i}]: expected a tag name '${tagName}'\\n${ctx.s}`);\n    }\n    ctx.i += tagName.length;\n    if (!parseCharCode(ctx, CharCode.MoreThan)) {\n      throw new Error(`Invalid HTML [${ctx.i}]: expected a '>' character\\n${ctx.s}`);\n    }\n  }\n\n  return element;\n}\n\nfunction parseAttributes(ctx: HTMLParserContext, element: Element) {\n  const s = ctx.s;\n\n  while (ctx.i < s.length) {\n    const c = s.charCodeAt(ctx.i);\n    if (c === CharCode.MoreThan) {\n      return;\n    }\n    const key = parseRegExp(ctx, ATTR_IDENTIFIER);\n    if (key === void 0) {\n      throw new Error(`Invalid HTML [${ctx.i}]: invalid attribute name\\n${ctx.s}`);\n    }\n    if (parseCharCode(ctx, CharCode.EqualsTo)) {\n      const value = parseAttributeString(ctx);\n      element._setAttribute(key, value);\n    } else {\n      element._setAttribute(key, \"\");\n    }\n    parseSkipWhitespace(ctx);\n  }\n  throw new Error(`Invalid HTML [${ctx.i}]: unexpected end\\n${ctx.s}`);\n}\n\nfunction parseAttributeString(ctx: HTMLParserContext): string {\n  if (!parseCharCode(ctx, CharCode.DoubleQuote)) {\n    throw new Error(`Invalid HTML [${ctx.i}]: invalid attribute value, expected a '\"' character)\\n${ctx.s}`);\n  }\n  const s = ctx.s;\n  let start = ctx.i;\n  while (ctx.i < s.length) {\n    const c = s.charCodeAt(ctx.i);\n    if (c === CharCode.DoubleQuote) {\n      return s.substring(start, ctx.i++);\n    }\n    ctx.i++;\n  }\n  throw new Error(`Invalid HTML [${ctx.i}]: invalid attribute value, expected a '\"' character\\n${ctx.s}`);\n}\n\n// Supports only <!> syntax.\nfunction parseComment(ctx: HTMLParserContext): Comment {\n  if (!parseCharCode(ctx, CharCode.LessThan)) {\n    throw new Error(`Invalid HTML [${ctx.i}]: expected a '<' character\\n${ctx.s}`);\n  }\n  if (!parseCharCode(ctx, CharCode.ExclamationMark)) {\n    throw new Error(`Invalid HTML [${ctx.i}]: expected a '!' character\\n${ctx.s}`);\n  }\n  if (!parseCharCode(ctx, CharCode.MoreThan)) {\n    throw new Error(`Invalid HTML [${ctx.i}]: expected a '>' character\\n${ctx.s}`);\n  }\n  return document._createComment();\n}\n\nfunction parseText(ctx: HTMLParserContext): Text {\n  const s = ctx.s;\n  let start = ctx.i;\n  while (ctx.i < s.length) {\n    const c = s.charCodeAt(ctx.i);\n    if (c === CharCode.LessThan) {\n      break;\n    }\n    ctx.i++;\n  }\n  return document._createTextNode(s.substring(start, ctx.i));\n}\n\nfunction parseSkipWhitespace(ctx: HTMLParserContext): void {\n  const s = ctx.s;\n  let i = ctx.i;\n  while (i < s.length) {\n    const c = s.charCodeAt(i);\n    if (\n      c === CharCode.Space ||\n      c === CharCode.Tab ||\n      c === CharCode.Newline ||\n      c === CharCode.CarriageReturn\n    ) {\n      i++;\n    } else {\n      break;\n    }\n  }\n  ctx.i = i;\n}\n\nfunction parseCharCode(ctx: HTMLParserContext, charCode: number): boolean {\n  if (ctx.i < ctx.s.length && ctx.s.charCodeAt(ctx.i) === charCode) {\n    ctx.i++;\n    return true;\n  }\n  return false;\n}\n\nfunction parseRegExp(ctx: HTMLParserContext, re: RegExp): string | undefined {\n  re.lastIndex = ctx.i;\n  const match = re.exec(ctx.s);\n  if (match !== null) {\n    const s = match[0];\n    ctx.i += s.length;\n    return s;\n  }\n}\n\nconst VOID_ELEMENTS = /^(audio|video|embed|input|param|source|textarea|track|area|base|link|meta|br|col|hr|img|wbr)$/;\n\nconst indent = (i: number, s: string) => \" \".repeat(i) + s;\n\nconst enum CharCode {\n  /** \"\\\\t\" */Tab = 9,\n  /** \"\\\\n\" */Newline = 10,\n  /** \"\\\\v\" */VerticalTab = 11,\n  /** \"\\\\r\" */CarriageReturn = 13,\n  /** [space] */Space = 32,\n  /** \"!\" */ExclamationMark = 33,\n  /** \"\\\\\"\" */DoubleQuote = 34,\n  /** \"&\" */Ampersand = 38,\n  /** \"/\" */Slash = 47,\n  /** \"<\" */LessThan = 60,\n  /** \"=\" */EqualsTo = 61,\n  /** \">\" */MoreThan = 62,\n}\n"
  },
  {
    "path": "packages/@ivi/mock-dom/tsconfig.json",
    "content": "{\n  \"extends\": \"../../../tsconfig.composite.json\",\n  \"compilerOptions\": {\n    \"lib\": [\"ESNext\"],\n  },\n}\n"
  },
  {
    "path": "packages/@ivi/portal/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-2024 Boris Kaul <localvoid@gmail.com>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "packages/@ivi/portal/README.md",
    "content": "# [ivi](https://github.com/localvoid/ivi) Portals\n\n**EXPERIMENTAL API**\n\n## Example\n\n```ts\nimport { createRoot, update, component, useState, html } from \"ivi\";\nimport { createPortal } from \"@ivi/portal\";\n\nconst [portalContainer, portal] = createPortal();\n\nupdate(\n  createRoot(document.getElementById(\"overlay\")!),\n  portalContainer,\n);\n\nconst App = component((c) => {\n  const [visible, setVisible] = useState(c, false);\n\n  const onMouseEnter = () => { setVisible(true); };\n  const onMouseLeave = () => { setVisible(false); };\n\n  return () => (\n    html`\n    <div class=\"App\">\n      <span\n        @mouseenter=${onMouseEnter}\n        @mouseleave=${onMouseLeave}\n      >\n        Portal Example\n      </span>\n      ${visible() && portal(html`<span>rendered inside of a portal</span>`)}\n    </div>\n    `\n  );\n});\n\nupdate(\n  createRoot(document.getElementById(\"root\")!),\n  App(),\n);\n```\n"
  },
  {
    "path": "packages/@ivi/portal/package.json",
    "content": "{\n  \"name\": \"@ivi/portal\",\n  \"version\": \"0.4.0\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./dist/index.js\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\",\n    \"!dist/**/*.tsbuildinfo\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"peerDependencies\": {\n    \"ivi\": \"workspace:^\"\n  },\n  \"devDependencies\": {\n    \"ivi\": \"workspace:*\"\n  },\n  \"scripts\": {\n    \"clean\": \"rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo\"\n  },\n  \"description\": \"ivi portal.\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ivi\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"repository\": \"github:localvoid/ivi\"\n}"
  },
  {
    "path": "packages/@ivi/portal/src/index.ts",
    "content": "import {\n  type Component, type VAny,\n  component, useUnmount, invalidate, List\n} from \"ivi\";\n\nexport interface PortalEntry {\n  v: VAny;\n}\n\nconst invalidatePortalContainers = (containers: Component[]) => {\n  for (let i = 0; i < containers.length; i++) {\n    invalidate(containers[i]);\n  }\n};\n\nconst portalEntryKey = (entry: PortalEntry) => entry;\nconst renderPortalEntry = (entry: PortalEntry) => entry.v;\n\n/**\n * Creates a stateless node for a portal container and component factory that\n * renders into a portal container.\n */\nexport const createPortal = (): [\n  VAny,\n  (v: VAny) => VAny,\n] => {\n  // Can't imagine any use case when there are many entries rendered into a\n  // portal, so we can use an array, and remove from it with an O(n) operation.\n  var _entries: PortalEntry[] = [];\n  // Completely useless feature, but to make the current API sound, it is\n  // necessary to allow attaching multiple containers into different places.\n  // It is going to duplicate everything that is rendered in this portal into\n  // multiple places.\n  var _containers: Component[] = [];\n\n  return [\n    // Portal container.\n    component((c) => {\n      // Add container to a containers registry.\n      _containers.push(c);\n      useUnmount(c, () => {\n        // Remove container from a containers registry.\n        _containers.splice(_containers.indexOf(c), 1);\n      });\n      // Manually create stateless node for dynamic lists, so that we can use\n      // object identity as keys without creating additional arrays.\n      return () => List(_entries, portalEntryKey, renderPortalEntry);\n    })(),\n    // Portal component factory.\n    component<VAny>((c) => {\n      // Object that contains stateless node that is rendered into a portal.\n      // It is wrapped, so that we can use it to uniquely identify it among\n      // different entries.\n      var entry: PortalEntry = { v: null };\n      _entries.push(entry);\n      useUnmount(c, () => {\n        _entries.splice(_entries.indexOf(entry), 1);\n        invalidatePortalContainers(_containers);\n      });\n      return (v) => {\n        entry.v = v;\n        invalidatePortalContainers(_containers);\n        return null;\n      };\n    }),\n  ];\n};\n"
  },
  {
    "path": "packages/@ivi/portal/tsconfig.json",
    "content": "{\n  \"extends\": \"../../../tsconfig.composite.json\",\n  \"references\": [\n    { \"path\": \"../../ivi\" },\n  ],\n}\n"
  },
  {
    "path": "packages/@ivi/rolldown/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-2024 Boris Kaul <localvoid@gmail.com>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "packages/@ivi/rolldown/README.md",
    "content": "# [ivi](https://github.com/localvoid/ivi) Rolldown Plugin\n\n## Example Configuration\n\n`rolldown.config.js`\n\n```js\nimport { ivi } from \"@ivi/rolldown\";\n\nexport default {\n  // ..\n  plugins: [\n    ivi(),\n  ],\n};\n```\n\n## Plugin Options\n\n- `filter` - Optional `HookFilter` for input modules.\n"
  },
  {
    "path": "packages/@ivi/rolldown/package.json",
    "content": "{\n  \"name\": \"@ivi/rolldown\",\n  \"version\": \"5.0.0\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./dist/index.js\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\",\n    \"!dist/**/*.tsbuildinfo\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"dependencies\": {\n    \"@ivi/compiler\": \"workspace:^\"\n  },\n  \"devDependencies\": {\n    \"rolldown\": \"catalog:\"\n  },\n  \"scripts\": {\n    \"clean\": \"rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo\"\n  },\n  \"description\": \"Rolldown plugin for ivi library.\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ivi\",\n    \"rolldown-plugin\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"repository\": \"github:localvoid/ivi\"\n}\n"
  },
  {
    "path": "packages/@ivi/rolldown/src/index.ts",
    "content": "import { TemplateCompiler, type CompilerOptions } from \"@ivi/compiler\";\nimport type { HookFilter, RolldownPlugin } from \"rolldown\";\n\nexport interface IviOptions extends CompilerOptions {\n  readonly filter?: HookFilter,\n}\n\nexport function ivi(options: IviOptions = {}): RolldownPlugin {\n  const compiler = new TemplateCompiler({\n    dedupeStrings: options.dedupeStrings ?? true,\n    oveo: options.oveo ?? false,\n  });\n  return {\n    name: \"ivi\",\n\n    transform: {\n      filter: {\n        code: \"ivi\",\n        moduleType: [\"js\", \"jsx\", \"ts\", \"tsx\"],\n        ...options.filter,\n      },\n      async handler(code: string, _id: string, { moduleType }) {\n        try {\n          const result = await compiler.transform(code, moduleType);\n          const map = result.map;\n          code = result.code;\n          return map ? { code, map } : { code };\n        } catch (err) {\n          this.error(`Failed to transform: ${err}`);\n        }\n      },\n    },\n\n    renderStart() {\n      compiler.renderStart();\n    },\n\n    renderChunk: {\n      filter: {\n        code: \"IVI\",\n      },\n      async handler(code, _chunk) {\n        try {\n          const result = await compiler.renderChunk(code);\n          const map = result.map;\n          code = result.code;\n          return map ? { code, map } : { code };\n        } catch (err) {\n          this.error(`Failed to render chunk: ${err}`);\n        }\n      },\n    },\n  };\n}\n"
  },
  {
    "path": "packages/@ivi/rolldown/tsconfig.json",
    "content": "{\n  \"extends\": \"../../../tsconfig.composite.json\",\n  \"references\": [\n    { \"path\": \"../../ivi\" },\n  ],\n}\n"
  },
  {
    "path": "packages/@ivi/rollup-plugin/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-2024 Boris Kaul <localvoid@gmail.com>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "packages/@ivi/rollup-plugin/README.md",
    "content": "# [ivi](https://github.com/localvoid/ivi) Rollup Plugin\n\n## Example Configuration\n\n`rollup.config.mjs`\n\n```js\nimport { ivi } from \"@ivi/rollup-plugin\";\n\nexport default {\n  // ..\n  plugins: [\n    ivi(),\n  ],\n};\n```\n\n## Plugin Options\n\n- `include` - Optional include filter.\n- `exclude` - Optional exclude filter.\n"
  },
  {
    "path": "packages/@ivi/rollup-plugin/package.json",
    "content": "{\n  \"name\": \"@ivi/rollup-plugin\",\n  \"version\": \"6.0.0\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./dist/index.js\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\",\n    \"!dist/**/*.tsbuildinfo\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"dependencies\": {\n    \"@ivi/compiler\": \"workspace:^\",\n    \"rollup\": \"catalog:\"\n  },\n  \"scripts\": {\n    \"clean\": \"rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo\"\n  },\n  \"description\": \"Rollup plugin for ivi library.\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ivi\",\n    \"rollup-plugin\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"repository\": \"github:localvoid/ivi\"\n}\n"
  },
  {
    "path": "packages/@ivi/rollup-plugin/src/index.ts",
    "content": "import { TemplateCompiler, type CompilerOptions } from \"@ivi/compiler\";\nimport type { HookFilter, Plugin } from \"rollup\";\n\nexport interface IviOptions extends CompilerOptions {\n  readonly filter?: HookFilter,\n}\n\nexport function ivi(options: IviOptions = {}): Plugin {\n  const compiler = new TemplateCompiler({\n    dedupeStrings: options?.dedupeStrings ?? true,\n    oveo: options?.oveo ?? false,\n  });\n  return {\n    name: \"ivi\",\n\n    transform: {\n      filter: {\n        code: \"ivi\",\n        ...options.filter,\n      },\n      async handler(code: string, id: string) {\n        try {\n          const result = await compiler.transform(code, \"tsx\");\n          const map = result.map;\n          code = result.code;\n          return map ? { code, map } : { code };\n        } catch (err) {\n          this.error(`Failed to transform: ${err}`);\n        }\n      }\n    },\n\n    renderStart() {\n      compiler.renderStart();\n    },\n\n    async renderChunk(code, _chunk) {\n      // Fast-path for chunks that doesn't have any ivi code.\n      if (!code.includes(\"IVI\")) {\n        return;\n      }\n\n      try {\n        const result = await compiler.renderChunk(code);\n        const map = result.map;\n        code = result.code;\n        return map ? { code, map } : { code };\n      } catch (err) {\n        this.error(`Failed to render chunk: ${err}`);\n      }\n    },\n  };\n}\n"
  },
  {
    "path": "packages/@ivi/rollup-plugin/tsconfig.json",
    "content": "{\n  \"extends\": \"../../../tsconfig.composite.json\",\n  \"references\": [\n    { \"path\": \"../../ivi\" },\n  ],\n}\n"
  },
  {
    "path": "packages/@ivi/vite-plugin/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-2024 Boris Kaul <localvoid@gmail.com>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "packages/@ivi/vite-plugin/README.md",
    "content": "# [ivi](https://github.com/localvoid/ivi) Vite Plugin\n\n`\"@ivi/vite-plugin\"` package provides [Vite](https://vitejs.dev/) plugin.\n\n```ts\n// vite.config.mjs\nimport { defineConfig } from \"vite\";\nimport { ivi } from \"@ivi/vite-plugin\";\n\nexport default defineConfig({\n  plugins: [ivi()],\n});\n```\n"
  },
  {
    "path": "packages/@ivi/vite-plugin/package.json",
    "content": "{\n  \"name\": \"@ivi/vite-plugin\",\n  \"version\": \"6.0.0\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./dist/index.js\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\",\n    \"!dist/**/*.tsbuildinfo\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"dependencies\": {\n    \"@ivi/compiler\": \"workspace:^\",\n    \"rollup\": \"catalog:\"\n  },\n  \"scripts\": {\n    \"clean\": \"rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo\"\n  },\n  \"description\": \"Vite plugin for ivi library.\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ivi\",\n    \"rollup-plugin\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"repository\": \"github:localvoid/ivi\"\n}\n"
  },
  {
    "path": "packages/@ivi/vite-plugin/src/index.ts",
    "content": "import { TemplateCompiler, type CompilerOptions } from \"@ivi/compiler\";\nimport type { HookFilter, Plugin } from \"rollup\";\n\nexport interface IviOptions extends CompilerOptions {\n  readonly filter?: HookFilter,\n}\n\nexport function ivi(options: IviOptions = {}): Plugin & { config(options: any, env: { mode: string; command: string; }): void; } {\n  let compiler!: TemplateCompiler;\n  return {\n    name: \"ivi\",\n\n    config(_options, { command }) {\n      let dedupeStrings = options?.dedupeStrings ?? true;\n      let oveo = options?.oveo ?? false;\n      if (command !== \"build\") {\n        dedupeStrings = false;\n        oveo = false;\n      }\n      compiler = new TemplateCompiler({\n        dedupeStrings,\n        oveo,\n      });\n    },\n\n    transform: {\n      filter: {\n        code: \"ivi\",\n        ...options.filter,\n      },\n      async handler(code: string, id: string) {\n        try {\n          const result = await compiler.transform(code, \"tsx\");\n          const map = result.map;\n          code = result.code;\n          return map ? { code, map } : { code };\n        } catch (err) {\n          this.error(`Failed to transform: ${err}`);\n        }\n      },\n    },\n\n    renderStart() {\n      compiler.renderStart();\n    },\n\n    async renderChunk(code, _chunk) {\n      // Fast-path for chunks that doesn't have any ivi code.\n      if (!code.includes(\"IVI\")) {\n        return;\n      }\n\n      try {\n        const result = await compiler.renderChunk(code);\n        const map = result.map;\n        code = result.code;\n        return map ? { code, map } : { code };\n      } catch (err) {\n        this.error(`Failed to render chunk: ${err}`);\n      }\n    },\n  };\n}\n"
  },
  {
    "path": "packages/@ivi/vite-plugin/tsconfig.json",
    "content": "{\n  \"extends\": \"../../../tsconfig.composite.json\",\n  \"references\": [\n    { \"path\": \"../rollup-plugin\" },\n  ],\n}\n"
  },
  {
    "path": "packages/ivi/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-2024 Boris Kaul <localvoid@gmail.com>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "packages/ivi/README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://github.com/localvoid/ivi\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <picture>\n      <source media=\"(prefers-color-scheme: dark)\" srcset=\".https://localvoid.github.io/ivi/images/ivi-logo-dark.svg\">\n      <source media=\"(prefers-color-scheme: light)\" srcset=\"https://localvoid.github.io/ivi/images/ivi-logo-light.svg\">\n      <img width=\"120\" height=\"120\" src=\"https://localvoid.github.io/ivi/images/ivi-logo-light.svg\" alt=\"ivi logo\">\n    </picture>\n  </a>\n</p>\n\nivi is a lightweight embeddable declarative Web UI library.\n\n- `f(state) => UI`\n- The [basic example](#examples) is just 2.7KB.\n- [Vite](#vite), [Rollup](#rollup) and [Rolldown](https://rolldown.rs/) plugins.\n- [Precompiled](#template-optimizations) templates optimized for size and performance.\n- Blazingly fast template compiler written in Rust with [oxc](https://oxc.rs/) library for javascript parsing.\n- [Small memory footprint](#internal-data-structures).\n- [Embeddable](#custom-scheduler).\n\n## Examples\n\n```js\nimport { createRoot, update, component, useState, html } from \"ivi\";\n\nconst Example = component((c) => {\n  const [count, setCount] = useState(c, 0);\n  const inc = () => {\n    setCount(count() + 1);\n  };\n\n  return () => html`\n    <div class=\"app\">\n      <div>${count()}</div>\n      <button @click=${inc}>Increment</button>\n    </div>\n  `;\n});\n\nupdate(\n  createRoot(document.body),\n  Example(),\n);\n```\n\nThe size of the precompiled example above is just 2.7KB (minified+brotli). It includes entire runtime for declarative UI rendering. Precompiled templates are [optimized](#template-optimizations) for code size and cold-start performance.\n\n- [TodoMVC](https://github.com/localvoid/ivi-examples/tree/master/apps/todomvc-htm)\n- [Examples from the https://react.dev/learn rewritten with ivi API](https://github.com/localvoid/ivi/blob/master/docs/misc/migrating-from-react.md)\n\n## Table of Contents\n\n- [Setup](#setup)\n  - [Vite](#vite)\n  - [Rollup](#rollup)\n- [Template Language](#template-language)\n  - [Multiple Root Nodes](#multiple-root-nodes)\n  - [Childless Elements](#childless-elements)\n  - [Whitespace Rules](#whitespace-rules)\n  - [Expressions](#expressions)\n  - [Element Properties](#element-properties)\n    - [Attributes](#attributes)\n    - [Properties](#properties)\n    - [Styles](#styles)\n    - [Events](#events)\n    - [Directives](#directives)\n  - [Conditionals](#conditionals)\n  - [Arrays](#arrays)\n  - [Dynamic Lists](#dynamic-lists)\n- [Components](#components)\n  - [Stateful Components](#stateful-components)\n  - [Stateless Components](#stateless-components)\n- [API](#api)\n  - [Opaque Types](#opaque-types)\n  - [Stateful Tree](#stateful-tree)\n  - [Stateless Tree](#stateless-tree)\n  - [Root](#root)\n    - [`createRoot(parentElement, nextNode)`](#createroot)\n    - [`dirtyCheck(root, forceUpdate)`](#dirtycheck)\n    - [`update(root, v, forceUpdate)`](#update)\n    - [`unmount(root, detach)`](#unmount)\n    - [`defineRoot(onInvalidate)`](#defineroot)\n  - [Components](#components)\n    - [`component(factory, areEqual)`](#component)\n    - [`getProps(component)`](#getprops)\n    - [`invalidate(component)`](#invalidate)\n    - [`useUnmount(component, hook)`](#useunmount)\n  - [Component State](#component-state)\n    - [`useMemo(areEqual, fn)`](#usememo)\n    - [`useState(component, value)`](#usestate)\n    - [`useReducer(component, value, reducer)`](#usereducer)\n  - [Side Effects](#side-effects)\n    - [`useEffect(component, effect, areEqual)`](#useeffect)\n    - [`useAnimationFrameEffect(component, effect, areEqual)`](#useanimationframeeffect)\n    - [`useIdleEffect(component, effect, areEqual)`](#useidleeffect)\n  - [List](#list)\n    - [`List(entries, getKey, render)`](#list-1)\n  - [Context](#context)\n    - [`context()`](#context-1)\n  - [Element Directive](#element-directive)\n  - [DOM Utilities](#dom-utilities)\n    - [`eventDispatcher(eventType, options)`](#eventdispatcher)\n    - [`findDOMNode(node)`](#finddomnode)\n    - [`containsDOMElement(node, element)`](#containsdomelement)\n    - [`hasDOMElement(node, element)`](#hasdomelement)\n  - [Equality Functions](#equality-functions)\n    - [`preventUpdates(a, b)`](#preventupdates)\n    - [`strictEq(a, b)`](#stricteq)\n    - [`shallowEq(a, b)`](#shalloweq)\n    - [`shallowEqArray(a, b)`](#shalloweqarray)\n- [CheatSheet](#cheatsheet)\n  - [Passive Event Listener](#passive-event-listener)\n  - [Dynamic Argument Name](#dynamic-argument-name)\n  - [Integrating External/Imperative Libraries](#integrating-externalimperative-libraries)\n- [Advanced](#advanced)\n  - [Component Invalidation and Dirty Checking](#component-invalidation-and-dirty-checking)\n  - [Right-to-Left Updates](#right-to-left-updates)\n  - [Template Call-Site Unique Identity](#template-call-site-unique-identity)\n  - [Forcing Component Updates](#forcing-component-updates)\n  - [Event Handlers Hoisting](#event-handlers-hoisting)\n  - [Internal Data Structures](#internal-data-structures)\n    - [UI Tree](#ui-tree-data-structures)\n    - [Templates](#template-data-structures)\n  - [Template Optimizations](#template-optimizations)\n  - [Custom Scheduler](#custom-scheduler)\n- [External Dependencies](#external-dependencies)\n\n## Setup\n\nivi templates will work without any precompilation, but it is highly recommended to use precompilation to improve performance and reduce code size.\n\nHoist and dedupe optimizations are implemented by the [oveo](https://github.com/localvoid/oveo) javascript optimizer.\n\n### Vite\n\n`\"@ivi/vite-plugin\"` package provides [Vite](https://vitejs.dev/) plugin.\n\n```ts\n// vite.config.mjs\nimport { defineConfig } from \"vite\";\nimport { oveo } from \"@oveo/vite\";\nimport { ivi } from \"@ivi/vite-plugin\";\n\nexport default defineConfig({\n  plugins: [\n    ivi({\n      oveo: true,\n    }),\n    oveo({\n      hoist: true,\n      dedupe: true,\n      externs: {\n        import: [\"ivi/oveo.json\"],\n      },\n    }),\n  ],\n});\n```\n\n> Vite setup with rolldown integration should use the `@ivi/rolldown` plugin.\n\n### Rollup\n\n`\"@ivi/rollup-plugin\"` package provides [Rollup](https://rollupjs.org/) plugin.\n\n```js\n// rollup.config.mjs\nimport { oveo } from \"@oveo/rollup\";\nimport { ivi } from \"@ivi/rollup-plugin\";\n\nexport default {\n  input: \"src/main.js\",\n  output: {\n    file: \"bundle.js\",\n  },\n  plugins: [\n    ivi({\n      oveo: true,\n    }),\n    oveo({\n      hoist: true,\n      dedupe: true,\n      externs: {\n        import: [\"ivi/oveo.json\"],\n      },\n    }),\n  ],\n};\n```\n\n## Template Language\n\nivi template language has an HTML-like syntax with additional syntax for DOM properties, events and whitespace removal.\n\n- `html` creates a template with [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) nodes.\n- `svg` creates a template with [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement) nodes.\n\n```js\nimport { html } from \"ivi\";\nconst Example = component((c) => {\n  // ...\n  return () => html`\n    <div class=\"app\">\n      <div>${count()}</div>\n      <button @click=${inc}>Increment</button>\n    </div>\n  `;\n});\n```\n\n### Multiple Root Nodes\n\nTemplates can have multiple root nodes.\n\n```js\nhtml`\n  <div></div>\n  ${expr} text\n  <div></div>\n`;\n```\n\n### Childless Elements\n\nChildless elements can be self closed with a `/>` syntax.\n\n```js\nhtml` <div class=\"a\" /> `;\n```\n\n### Whitespace Rules\n\n1. Whitespaces around newlines are removed:\n\n```htm\n<div>\n  <p></p>\n  ab\n  <p></p>\n</div>\n```\n\n```htm\n<div>\n  <p></p>\n  ab\n  <p></p>\n</div>\n```\n\n2. Inline whitespaces are collapsed into one whitespace:\n\n```htm\n<div>  <span>  a  b  </span>  </div>\n```\n\n```htm\n<div> <span> a b </span> </div>\n```\n\n3. Whitespaces around newlines in text nodes are collapsed into one whitespace:\n\n```htm\n<div>\n  ab\n  cd\n</div>\n```\n\n```htm\n<div>ab cd</div>\n```\n\n4. Vertical tab `\\v` character prevents from removing all whitespaces around newlines:\n\n```htm\n<div>\n  <b>1</b>\n  \\v item left\n</div>\n```\n\n```htm\n<div><b>1</b> item left</div>\n```\n\n### Expressions\n\nIn ivi templates, you can include dynamic content called expressions. An expression is just a piece of JavaScript code that gets evaluated when template is rendered. Whatever value an expression produces at that time will be included in the final rendered template.\n\n```js\nhtml` <div attr=${attributeValueExpr}>${childExpr}</div>`;\n```\n\n### Element Properties\n\nivi template language supports additional syntax to work with DOM properties, events, etc.\n\n- [`<div name=\"value\" />`](#attributes) - Static attribute.\n- [`<div name />`](#attributes) - Static attribute.\n- [`<div name=${expr} />`](#attributes) - Dynamic attribute `element.setAttribute(name, expr)`.\n- [`<div .name=${expr} />`](#properties) - Property `element[name] = expr`.\n- [`<div *name=${expr} />`](#properties) - Property `element[name] = expr`, diffs against a DOM value.\n- [`<div ~name=\"value\" />`](#styles) - Static style `<div style=\"name:value;\">`.\n- [`<div ~name=${expr} />`](#styles) - Dynamic style `element.style.setProperty(name, expr)`.\n- [`<div @name=${expr} />`](#events) - Event `element.addEventListener(name, expr)`.\n- [`<div ${directive} />`](#directives) - Client-Side Element Directive `directive(element)`.\n- [`<div .textContent=${expr} />`](#text-content) - Text content.\n\n#### Attributes\n\n- `<div name=\"value\" />` - Static attribute with a value `<div name=\"value\">`.\n- `<div name />` - Static attribute without a value `<div name>`.\n- `<div name=${expr} />` - Dynamic attribute `element.setAttribute(name, expr)`.\n\nDOM attributes are assigned with `Element.setAttribute(..)`.\n\nWhen dynamic attribute has an `undefined`, `null` or `false` value, it will be removed from the DOM element with `Element.removeAttribute(..)` method.\n\n#### Properties\n\n- `<div .name=${expr} />` - Property `element[name] = expr`.\n- `<div *name=${expr} />` - Property `element[name] = expr`, diffs against a DOM value.\n\nProperties are assigned with an assignment operator `Element.name = value`.\n\nDiffing with a DOM value is useful in use cases when we use `<input>` values to avoid triggering unnecessary `input` events.\n\n#### Styles\n\n- `<div ~name=\"value\" />` - Static style `<div style=\"value\">`.\n- `<div ~name=${expr} />` - Dynamic style `element.style.setProperty(name, expr)`.\n\nStatic styles are automatically merged with `:style=\"value\"` attribute.\n\nDynamic styles are assigned with a `CSSStyleDeclaration.setProperty(..)` method.\n\nWhen style has an `undefined`, `null` or `false` value, it will be removed with `CSSStyleDeclaration.removeProperty(..)` method.\n\n#### Events\n\n- `<div @name=${expr} />` - Event `element.addEventListener(name, expr)`.\n\nEvents are assigned with an `EventTarget.addEventListener(..)` method.\n\nWhen event has an `undefined`, `null` or `false` value, it will be removed with `EventTarget.removeEventListener(..)` method.\n\n#### Text Content\n\n- `<div .textContent=${expr} />` - Text Content `element.textContent = expr`.\n\nText content property can be used as an optimization that slightly reduces memory consumption for elements with a text child. It will create a text node with a [`Node.textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) property and won't have any stateful nodes associated with a text node.\n\nText content value should have an `undefined`, `null`, `false`, `string` or a `number` type.\n\n#### Directives\n\n- `<div ${directive} />` - Element Directive `directive(element)`.\n\nDirective is a function that is invoked each time template is updated and\nreceives a DOM element associated with a directive:\n\n```ts\ntype ElementDirective = <E extends Element>(element: E) => void;\n```\n\nDirective function is invoked only when template is created with a\ndifferent function, so if we are going to reuse the same function, it can be\nused as a DOM element created callback:\n\n```js\nconst Example = component((c) => {\n  const onCreated = (innerElement) => {\n    // ..\n  };\n  return () => html`\n    <div>\n      <div class=\"Inner\" ${onCreated} />\n    </div>\n  `;\n});\n```\n\nDirectives can be used not just as a simple DOM created callbacks, but also as stateful directives. E.g.\n\n```js\nfunction createStatefulDirective() {\n  // Internal state that stores previous value.\n  let prev;\n  // Returns a factory that creates directive functions.\n  return (next) => (element) => {\n    // Check if previous value has been changed.\n    if (prev !== next) {\n      prev = next;\n      // Updates textContent only when input value is changed.\n      element.textContent = next;\n    }\n  };\n}\n\nconst Example = component((c) => {\n  const directive = createStatefulDirective();\n  return (i) => htm`\n    <div ${directive(i)} />\n  `;\n});\n```\n\n### Conditionals\n\nYou can use regular JavaScript expressions in your templates, which means you can use any javascript control flow constructs like conditional operators, function calls, and if or switch statements to generate dynamic content based on runtime conditions.\n\nThis means you can create templates with complex logic that conditionally renders different content based on what's happening in your application. You can nest template expressions inside one another to build up more complex templates, and you can store the results of templates in variables to use them later in your code.\n\n```js\nconst Example = component((c) => {\n  // ...\n  return (show) => html` <div>${show && html`<span>Show</span>`}</div> `;\n});\n```\n\n### Arrays\n\nIf an expression is used in the child position of an HTML element and it returns an array, ivi will render all of the items in that array as separate nodes.\n\n```js\nconst Example = () => html` <div>${[\"Text Node 1\", \"Text Node 2\"]}</div> `;\n```\n\nivi allows components to return arrays of elements as their root nodes. This means that a component can return multiple top-level elements instead of just one.\n\nFor example, a component could return an array of `<li>` elements that make up a list. When this component is rendered, ivi will treat the array of `<li>` elements as a set of top-level elements, just like it would with a single root element.\n\nThis feature provides more flexibility when building complex UI components, as it allows you to create components that generate a dynamic number of top-level elements depending on their input.\n\n```js\nconst Example = component((c) => {\n  return (entries) => entries.map((e) => html`\n    <li>${e}</li>\n  `);\n);\n// Example([1, 2, 3])\n```\n\nWhen arrays are updated, stateless tree nodes are mapped onto their stateful nodes by their position in the array.\n\nWhen array contains a conditional expression that returns a \"hole\" value (`null`, `undefined` or `false`), the hole will occupy a slot in a stateful tree, so that all nodes will be correclty mapped onto their stateful nodes.\n\n```js\n[conditional ? \"text\" : null, StatefulComponent()];\n```\n\nIn the example above, when `conditional` expression goes from a text to a \"hole\" and vice versa, `StatefulComponent` will preserve its internal state.\n\nWhen array grows or shrinks in size, stateful nodes will be created or removed at the end of an array.\n\n### Dynamic Lists\n\nIn ivi, you can render lists of items using the `List()` function that loops through an array of data and returns a list of elements. However, when list is updated, it is important to correctly map rendered items onto their stateful views. This means that if an item is rendered as a component that has internal state that could change as a result of user actions or external events, it should be mapped onto the same component instance.\n\nTo render dynamic lists, ivi provides the [`List()`](#list-1) function.\n\n```ts\nfunction List<E, K>(\n  // Input Entries.\n  entries: E[],\n  // Function that retrieves unique key from an entry.\n  getKey: (entry: E, index: number) => K,\n  // Function that renders an entry.\n  render: (entry: E, index: number) => VAny,\n): VList;\n```\n\nIt creates a dynamic list with an array of keys that uniquely identify each item in the list. When list is updated, ivi uses keys to map items onto their stateful nodes.\n\nIt's important to note that when rendering a dynamic list, you should always use a unique identifier as a key. This helps ivi identify each element in a list and avoid rendering errors. If you use an index or a random value as a key, ivi may not be able to identify correct elements in a list, which can cause errors.\n\n```ts\ninterface DataEntry {\n  key: number;\n  text: string;\n}\nconst getEntryKey = (entry: DataEntry) => entry.key;\nconst EntryView = (entry: DataEntry) => html`<li>${entry.text}</li>`;\n\nconst ListView = (data: DataEntry[]) => html`\n  <ul>\n    ${List(data, getEntryKey, EntryView)}\n  </ul>\n`;\n```\n\nivi is using an optimal algorithm for dynamic lists that uses the minimum number of `Node.insertBefore()` operations to rearrange DOM nodes.\n\nReducing `Node.insertBefore()` operations is important not just because it invalidates internal DOM state, but also because each time one of the DOM nodes attached to the document is moved, it may produce a [MutationObserver notification](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver). And a lot of popular extensions are using Mutation Observers to observe entire document subtree, so each `insertBefore` operation can become quite costly when it is used outside of benchmarking sandboxes.\n\n## Components\n\nComponents can be either stateful or stateless. Stateful components are used when you need to manage state that changes over time, such as user input, network requests, or animations.\n\n### Stateful Components\n\nStateful components are declared with [`component()`](#component) function. It creates a factory function that produces component nodes.\n\n```js\n// `component()` function creates a factory function for component\n// nodes of this type.\nconst Example = component((c) => {\n  // When component state is initialized, it should return a render\n  // function.\n  return (props) => html`<div>${props.value}</div>`;\n});\n\nupdate(document.body, Example({ value: \"Hello World\" }));\n```\n\nStateful components are using JavaScript closures to store internal state.\n\n```js\nconst Example = component((c) => {\n  // Internal state.\n  let _counter = 0;\n\n  // Event handler.\n  const increment = () => {\n    // Mutate internal state.\n    _counter++;\n    // Invalidate component and schedule an update.\n    invalidate(c);\n  };\n\n  // Render function.\n  return () => html`\n    <div>\n      <p>Count: ${_counter}</p>\n      <button @click=${increment}>Increment</button>\n    </div>\n  `;\n});\n```\n\nWhen internal state is mutated, it doesn't trigger component updates automatically and it should be manually invalidated with [`invalidate()`](#invalidate) function.\n\nThere are high-level APIs like [`useState()`](#usestate) or\n[`useReducer()`](#usereducer) that use the low-level [`invalidate()`](#invalidate) function behind the scenes to automatically invalidate components when internal state is mutated.\n\n```js\nconst Example = component((c) => {\n  // Internal state.\n  const [counter, setCounter] = useState(c, 0);\n\n  const increment = () => {\n    // Automatically invalidates component when counter value is mutated.\n    setCounter(counter() + 1);\n  };\n\n  // Render function.\n  return () =>\n    html` <div>\n      <p>Count: ${counter()}</p>\n      <button @click=${increment}>Increment</button>\n    </div>`;\n});\n```\n\n### Stateless Components\n\nStateless components in ivi are just basic JavaScript functions. They are faster and more lightweight than stateful components, which makes them a good choice for simple and reusable components that doesn't have any internal state.\n\n```js\nconst Button = (text, onClick) => html` <button @click=${onClick}>${text}</button> `;\n```\n\n## API\n\n### Stateful Tree\n\n```ts\ntype SNode = Opaque;\ntype Root<State> = Opaque<State>;\ntype Component<Props> = Opaque<Props>;\n```\n\n### Stateless Tree\n\n```ts\ntype VAny =\n  | null // Hole\n  | undefined // Hole\n  | false // Hole\n  | string // Text\n  | number // Text\n  | VRoot // Root\n  | VTemplate // Template\n  | VComponent // Component\n  | VContext // Context Provider\n  | VList // Dynamic List with track by key algo\n  | VAny[]; // Dynamic List with track by index algo\n\ntype VRoot = Opaque;\ntype VTemplate = Opaque;\ntype VComponent = Opaque;\ntype VContext = Opaque;\ntype VList = Opaque;\n```\n\n### Root Nodes\n\nA root node is the topmost node in a stateful tree, from which all other nodes are rendered. It represents an entry point for the ivi rendering algorithm and stores a position in the DOM tree.\n\n#### **`createRoot()`**\n\n`createRoot` creates a root node that uses microtask queue for scheduling updates.\n\n```ts\nfunction createRoot(parentElement: Element, nextNode: Node | null = null): Root;\n```\n\n- `parentElement` - Parent DOM Element.\n- `nextNode` - Next DOM Node.\n\n#### **`dirtyCheck()`**\n\n`dirtyCheck` performs the dirty checking algorithm in a root subtree and updates all dirty components.\n\n```ts\nfunction dirtyCheck(root: Root, forceUpdate: boolean = false): void;\n```\n\n- `root` - Root node.\n- `forceUpdate` - Force all components to update, even when they are using optimization hints to reduce updates.\n\n#### **`update()`**\n\n`update` updates a root subtree with a new representation.\n\n```ts\nfunction update(root: Root, v: VAny, forceUpdate: boolean = false): void;\n```\n\n- `root` - Root node.\n- `v` - New representation.\n- `forceUpdate` - Force all components to update, even when they are using optimization hints to reduce updates.\n\n#### **`unmount()`**\n\n`unmount` unmounts a root subtree from the DOM and triggers unmount hooks in components.\n\n```ts\nfunction unmount(root: Root, detach: boolean): void;\n```\n\n- `root` - Root node.\n- `detach` - Detach the topmost DOM nodes from the DOM subtree.\n\n#### **`defineRoot()`**\n\n`defineRoot` creates a root node factory that uses a custom `OnRootInvalidated` hook.\n\n```ts\nfunction defineRoot(\n  onInvalidate: (root: Root<undefined>) => void,\n): (parentElement: Element, nextNode: Node | null) => Root<undefined>;\n\nfunction defineRoot<S>(\n  onInvalidate: (root: Root<S>, state: S) => void,\n): (parentElement: Element, nextNode: Node | null, state: S) => Root<S>;\n```\n\n- `onInvalidate` - `OnRootInvalidated` hook that receives a root node and custom state associated with that root node.\n\n### Components\n\n#### **`component()`**\n\n`component` creates a factory that produces component nodes.\n\n```ts\nfunction component(\n  factory: (c: Component) => () => VComponent<undefined>,\n  areEqual?: () => boolean,\n): () => VComponent<undefined>;\nfunction component<P>(\n  factory: (c: Component) => (props: P) => VAny,\n  areEqual?: (prev: P, next: P) => boolean,\n): (props: P) => VComponent<P>;\n```\n\n- `factory` - Function that produces stateful component render functions.\n- `areEqual` - Optional function that checks input properties for changes and is used as an optimization hint to reduce unnecessary updates when properties didn't change.\n\n_When root subtree is updated with `forceUpdate` option, `areEqual` hint is ignored and all components are updated._\n\n#### **`getProps()`**\n\n`getProps` gets current component props from component instance.\n\n```ts\nfunction getProps = <P>(component: Component<P>): P;\n```\n\n- `component` - Component instance.\n\n#### **`invalidate()`**\n\n`invalidate` invalidates component and schedules an update.\n\n```ts\nfunction invalidate(component: Component): void;\n```\n\n- `component` - Component instance.\n\n#### **`useUnmount()`**\n\nAdds an unmount hook.\n\n```ts\nfunction useUnmount(component: Component, hook: () => void): void;\n```\n\n- `component` - Component instance.\n- `hook` - Unmount hook.\n\n### Component State\n\n#### **`useMemo()`**\n\n`useMemo` creates a memoized function.\n\n```ts\nfunction useMemo<T, U>(areEqual: (prev: T, next: T) => boolean, fn: (props: T) => U): (props: T) => U;\n```\n\n- `areEqual` - Checks input properties for changes to avoid recomputations.\n- `fn` - Function to memoize.\n\n#### **`useState()`**\n\n`useState` creates a reactive component state.\n\n```ts\nfunction useState<S>(component: Component, state: S): [get: () => S, set: (s: S) => void];\n```\n\n- `component` - Component instance.\n- `state` - Initial state.\n\nReturns state getter and state setter functions.\n\n#### **`useReducer()`**\n\n`useReducer` creates a reactive component state reducer.\n\n```ts\ntype Dispatch<A> = (action: A) => void;\n\nfunction useReducer<S, A>(\n  component: Component,\n  state: S,\n  reducer: (state: S, action: A) => S,\n): [get: () => S, dispatch: Dispatch<A>];\n```\n\n- `component` - Component instance.\n- `state` - Initial state.\n- `reducer` - State reducer function.\n\nReturns state getter and action dispatcher functions.\n\n### Side Effects\n\nSide effects lets you specify how your components should behave with external systems such as imperative API calls, timer manipulations, or direct DOM interactions.\n\nYou can think of it as a combination of `mount`, `update` and `unmount` lifecycles hooks.\n\n#### **`useEffect()`**\n\n`useEffect` creates a side effect that is executed immediately after root node finishes an update.\n\n```ts\nfunction useEffect(component: Component, effect: () => (() => void) | void): () => void;\nfunction useEffect<P>(\n  component: Component,\n  effect: (props: P) => (() => void) | void,\n  areEqual?: (prev: P, next: P) => boolean,\n): (props: P) => void;\n```\n\n- `component` - Component instance.\n- `effect` - Effect hook.\n- `areEqual` - Optional function that checks input properties for changes and is used to control when an effect should be updated.\n\nReturns a side effect function that should be invoked in a render function.\n\n#### **`useAnimationFrameEffect()`**\n\n`useAnimationFrameEffect` creates a side effect that is executed before [animation frame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).\n\n```ts\nfunction useAnimationFrameEffect(component: Component, effect: () => (() => void) | void): () => void;\nfunction useAnimationFrameEffect<P>(\n  component: Component,\n  effect: (props: P) => (() => void) | void,\n  areEqual?: (prev: P, next: P) => boolean,\n): (props: P) => void;\n```\n\n- `component` - Component instance.\n- `effect` - Effect hook.\n- `areEqual` - Optional function that checks input properties for changes and is used to control when an effect should be updated.\n\nReturns a side effect function that should be invoked in a render function.\n\n#### **`useIdleEffect()`**\n\n`useIdleEffect` creates a side effect that is executed when browser is [idle](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback).\n\n```ts\nfunction useIdleEffect(ccomponent: Component, effect: () => (() => void) | void): () => void;\nfunction useIdleEffect<P>(\n  ccomponent: Component,\n  effect: (props: P) => (() => void) | void,\n  areEqual?: (prev: P, next: P) => boolean,\n): (props: P) => void;\n```\n\n- `component` - Component instance.\n- `effect` - Effect hook.\n- `areEqual` - Optional function that checks input properties for changes and is used to control when an effect should be updated.\n\nReturns a side effect function that should be invoked in a render function.\n\n### List\n\n#### **`List()`**\n\n`List` creates a dynamic lists.\n\n```ts\nfunction List<E, K>(entries: E[], getKey: (entry: E, index: number) => K, render: (entry: E) => VAny): VList;\n```\n\n- `entries` - Input data.\n- `getKey` - Function that should return an unique key for each data entry.\n- `render` - Function that renders an entry.\n\n### Context\n\n### **`context()`**\n\n`context` creates context getter and context provider functions.\n\n```ts\nfunction context = <T>(): [\n  get: (component: Component) => T | undefined,\n  provider: (value: T, children: VAny) => VContext<T>,\n]\n```\n\nReturns a `get` function that finds the closest context value, and a `provider` function that creates context nodes.\n\n```js\n// Creates a getter and provider functions.\nconst [getContextValue, contextValueProvider] = context();\n\nconst Example = component((c) => {\n  return () => html` <h1>Hello ${getContextValue(c)}</h1> `;\n});\n\nupdate(createRoot(document.body), contextValueProvider(\"World\", Example()));\n```\n\n### Element Directive\n\n`ElementDirective` is an escape hatch that allows extending ivi rendering\nalgorithm.\n\n```ts\ntype ElementDirective = <E extends Element>(element: E) => void;\n```\n\n### DOM Utilities\n\n#### **`eventDispatcher()`**\n\n`eventDispatcher` creates an event dispatcher that finds the closest child DOM\nnode and emits a [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) with [`EventTarget.dispatchEvent()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent) method.\n\n```ts\ninterface DispatchEventOptions {\n  // Option indicating whether the event bubbles. The default\n  // is `true`.\n  bubbles?: boolean;\n  // Option indicating whether the event can be cancelled. The\n  // default is `false`.\n  cancelable?: boolean;\n  // Option indicating whether the event will trigger listeners\n  // outside of a shadow root. The default is `false`.\n  composed?: boolean;\n}\n\ntype EventDispatcher = {\n  (component: Component): boolean;\n  <T>(component: Component, value: T): boolean;\n};\n\nfunction eventDispatcher = <T>(\n  eventType: string,\n  options?: DispatchEventOptions,\n): EventDispatcher;\n```\n\n- `eventType` - Event type.\n- `options` - Event options that will be used when event is dispatched.\n\nEvent dispatcher invokes event handlers synchronously. All event handlers are invoked before event dispatcher returns.\n\n#### **`findDOMNode()`**\n\n`findDOMNode` finds the closest DOM node child that belongs to a stateful node subtree.\n\n```ts\nfunction findDOMNode<T extends Node | Text>(node: SNode | null): T | null;\n```\n\n- `node` - Stateful node.\n\n#### **`containsDOMElement()`**\n\n`containsDOMElement` checks if a stateful node contains a DOM elements in its subtree.\n\n```ts\nfunction containsDOMElement(node: SNode, element: Element): boolean;\n```\n\n- `node` - Stateful node.\n- `element` - DOM Element.\n\n#### **`hasDOMElement()`**\n\n`hasDOMElement` checks if a stateful node has a DOM element as its child.\n\n```ts\nfunction hasDOMElement(node: SNode, child: Element): boolean;\n```\n\n- `node` - Stateful node.\n- `child` - DOM Element.\n\n### Equality Functions\n\n### **`preventUpdates()`**\n\n`preventUpdates` is a noop function that always returns `true` value.\n\n```ts\nfunction preventUpdates<T>(a: T, b: T): true;\n```\n\n#### **`strictEq()`**\n\n`strictEq` checks values for equality with strict equality operator `===`.\n\n```ts\nfunction strictEq<T>(a: T, b: T): boolean;\n```\n\n#### **`shallowEq()`**\n\n`shallowEq` checks objects with shallow equality algorithm and uses strict equality operator to check individual values for equality.\n\n```ts\nfunction shallowEq<T extends object>(a: T, b: T): boolean;\n```\n\n#### **`shallowEqArray()`**\n\n`shallowEqArray` checks arrays with shallow equality algorithm and uses strict equality operator to check individual values for equality.\n\n```ts\nfunction shallowEqArray<T>(a: T[], b: T[]): boolean;\n```\n\n## CheatSheet\n\n### Passive Event Listener\n\n```js\nconst Example = component(() => {\n  const _onTouchDown = (ev) => {};\n\n  const addPassiveTouchDown = (element) => {\n    element.addEventListener(\"touchdown\", _onTouchDown, { passive: true });\n  };\n\n  return () => html` <div ${addPassiveTouchDown}></div> `;\n});\n```\n\n### Dynamic Argument Name\n\n```js\nconst useDynamicArg = () => {\n  let prevKey;\n  let prevValue;\n  return (key, value) => (element) => {\n    if (prevKey !== key) {\n      if (prevKey) {\n        element.removeAttribute(prevKey);\n      }\n      element.setAttribute(key, value);\n    } else if (prevValue !== value) {\n      element.setAttribute(key, value);\n    }\n  };\n};\n\nconst Example = component(() => {\n  const arg = useDynamicArg();\n\n  return ([key, value]) => html` <div ${arg(key, value)}></div> `;\n});\n```\n\n### Integrating External/Imperative Libraries\n\n```js\nimport { createRoot, update, component, findDOMNode, useEffect, html } from \"ivi\";\nimport { EditorView, basicSetup } from \"codemirror\";\nimport { javascript } from \"@codemirror/lang-javascript\";\n\nconst CodeMirror = component((c) => {\n  let _editor;\n\n  useEffect(c, () => {\n    _editor = new EditorView({\n      extensions: [basicSetup, javascript()],\n      // findDOMNode finds the closest child DOM node.\n      parent: findDOMNode(c),\n    });\n\n    // Reset function will be invoked when component is unmounted.\n    return () => {\n      _editor.destroy();\n    };\n  })();\n  // ^ When effect doesn't have any dependencies, it can be executed just\n  // once in the outer scope. Effect will run when its DOM tree is mounted.\n\n  return () => html` <div class=\"CodeMirror\"></div> `;\n});\n\nupdate(createRoot(document.body), CodeMirror());\n```\n\n## Advanced\n\n### Component Invalidation and Dirty Checking\n\nComponent invalidation algorithm is implemented by marking component as dirty and marking all its parent nodes with a flag that they have a dirty subtree. When marking algorithm reaches a root node, it invokes `OnRootInvalidated()` hook that can be used to implement a [custom scheduler](#custom-scheduler).\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://localvoid.github.io/ivi/images/component-invalidated-1-dark.png\">\n  <source media=\"(prefers-color-scheme: light)\" srcset=\"https://localvoid.github.io/ivi/images/component-invalidated-1-light.png\">\n  <img src=\"https://localvoid.github.io/ivi/images/component-invalidated-1-light.png\">\n</picture>\n\n1. Component invalidated and marked with `Dirty` flag.\n2. Node marked with `DirtySubtree` flag.\n3. Root Node marked with `DirtySubtree` flag, `OnRootInvalidated()` hook invoked.\n4. Component invalidated and marked with `Dirty` flag, parents already marked with `DirtySubtree` flag.\n\nWhen scheduler decides to update a root node with a dirty subtree, it starts a dirty checking algorithm. This algorithm goes top-down in a right-to-left order, visiting all nodes with a dirty subtree flag until it reaches a dirty component and updates it.\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://localvoid.github.io/ivi/images/dirty-checking-1-dark.png\">\n  <source media=\"(prefers-color-scheme: light)\" srcset=\"https://localvoid.github.io/ivi/images/dirty-checking-1-light.png\">\n  <img src=\"https://localvoid.github.io/ivi/images/dirty-checking-1-light.png\">\n</picture>\n\n1. Starts dirty checking from the root node.\n2. Clean node, skips visiting its subtree.\n3. Node with `DirtySubtree` flag, starts checking its children.\n4. Component with `Dirty` flag, triggers an update.\n5. Component with `Dirty` flag, triggers an update.\n6. Clean node, skips visiting its subtree.\n\n### Right-to-Left Updates\n\nOne of the reasons why the core library is so small is because update algorithm is implemented in RTL order. Algorithm that performs updates in RTL order simplifies a lot of complex issues with DOM updates. The main issue with DOM updates is that when we start updating a DOM tree structure, we need to have a reference to a parent and a next DOM node, so that we can use `parent.insertBefore(newNode, nextNode)`. In most cases it is easy to retrieve a next DOM node, but there are edge cases like when we have two adjacent conditional expressions and one of their states is that it completely removes a DOM node from the tree, or two adjacent components with conditionals at their roots, etc.\n\nMajority of libraries are dealing with this edge cases by introducing marker DOM nodes (comment or an empty text node). For example, to implement conditional expressions we can add an empty text node when conditional doesn't render any DOM node and when conditional goes into a state when it needs to add a DOM node, it will use a marker node as a next DOM node reference. The RTL update algorithm in ivi doesn't use any marker nodes.\n\nThe RTL algorithm that is used in ivi also makes it way much easier to implement node displacements without introducing any additional code paths, fragments and pretty much everything that involves updating a DOM structure.\n\n### Template Call-Site Unique Identity\n\nEach call-site that creates a template has unique identity, so even identical templates created from different call-sites won't be able to diff against each other.\n\n```js\nfunction TemplateUniqueIdentity(condition, text) {\n  return condition ? html`<div>${text}</div>` : html`<div>${text}</div>`;\n}\n```\n\nIn the example above, when `condition` is changed, instead of updating text node, update algorithm will replace entire div element with a new one.\n\n### Forcing Component Updates\n\nThere are some use cases that require a lot of frequent reads from a reactive variable. And whenever this variable changes, it affects a lot of UI nodes, like switching between light/dark themes.\n\nInstead of creating a lot of subscriptions to this variables, it is recommended to use simple javascript values and rerender entire UI subtree with `dirtyCheck(root, true)` when this values are changed.\n\n```js\nconst root = createRoot(document.getElementById(\"app\"));\n\nlet theme = \"Light\";\nfunction setTheme(t) {\n  if (theme !== t) {\n    theme = t;\n    dirtyCheck(root, true);\n  }\n}\n\nconst App = component((c) => {\n  const toggleTheme = () => {\n    setTheme(theme === \"Light\" ? \"Dark\" : \"Light\");\n  };\n  return () => html`\n    <div>\n      <div>${theme}</div>\n      <button @click=${toggleTheme}>Toggle Theme</button>\n    </div>\n  `;\n});\n\nupdate(root, App());\n```\n\n### Event Handlers Hoisting\n\nWhen oveo optimizations are enabled, event handlers are automatically hoisted to the outermost scope.\n\n```js\nconst Example = component((c) => {\n  const [count, setCount] = useState(c, 0);\n\n  return () => html`\n    <div\n      @click=${() => {\n        setCount(count() + 1);\n      }}\n    >\n      ${count()}\n    </div>\n  `;\n});\n```\n\nAfter event handler hoisting, it will be transformed into:\n\n```js\nconst Example = component((c) => {\n  const [count, setCount] = useState(c, 0);\n  const __ivi_hoist_1 = () => {\n    setCount(count() + 1);\n  };\n\n  return () => html` <div @click=${__ivi_hoist_1}>${count()}</div> `;\n});\n```\n\nTo disable event handlers hoisting, it should be wrapped in parenthesis. E.g.\n\n```js\nconst Example = component((c) => {\n  const [count, setCount] = useState(c, 0);\n\n  return () => html`\n    <div\n      @click=${() => {\n        setCount(count() + 1);\n      }}\n    >\n      ${count()}\n    </div>\n  `;\n});\n```\n\n### Internal Data Structures\n\nTo get a rough estimate of memory usage it is important to understand internal data structures.\n\nIn the description below we are going to calculate memory usage in a Chromium-based engines with [Pointer Compression in V8](https://v8.dev/blog/pointer-compression).\n\n#### UI Tree\n\nUI Tree is implemented with a stateful tree `SNode` and immutable stateless tree `VAny`.\n\nStateless Tree has a simple data structure:\n\n```ts\n// 20 bytes\ninterface VNode<D extends VDescriptor, P> {\n  // Descriptors are reused for all VNodes with the same type and its memory\n  // usage can be ignored during estimation.\n  readonly d: D;\n  // Prop value is used for storing the results of template expressions in an\n  // array, prop value for Components, or VRoot and VList props.\n  readonly p: P;\n}\n\ntype VArray = VAny[];\ntype VAny =\n  | null // empty slot\n  | undefined // empty slot\n  | false // empty slot\n  | string // text\n  | number // text\n  | VRoot // VNode<RootDescriptor, RootProps>\n  | VTemplate // VNode<TemplateDescriptor, P>\n  | VComponent // VNode<ComponentDescriptor, P>\n  | VContext // VNode<ContextDescriptor, ContextProps<T>>\n  | VList // VNode<ListDescriptor, ListProps<K>>\n  | VArray; // VAny[]\n\n// 20 bytes\n// Root Props stores a location where its children should be rendered.\ninterface RootProps {\n  // Parent Element\n  p: Element;\n  // Next Node\n  n: Node | null;\n}\n\n// 20 bytes\n// Context Props stores a context value and stateless child node.\ninterface ContextProps<T> {\n  // Context value\n  v: T;\n  // Stateless child\n  c: VAny;\n}\n\n// 20 bytes\ninterface ListProps<K> {\n  // Keys that uniquely identify each stateless node in a dynamic list.\n  k: K[];\n  // Stateless nodes\n  v: VAny[];\n}\n```\n\nFor each stateless node `VAny` there is a stateful node `SNode` that has an\ninterface:\n\n```ts\n// 32 bytes\ninterface SNode1<V extends VAny, S1> {\n  // Stateless node associated with the current state.\n  v: V;\n  // Bitflags\n  f: Flags; // SMI value - Small Integer\n  // Children nodes.\n  c: SNode | (SNode | null)[] | null;\n  // Parent node.\n  p: SNode | null;\n  // State Slot #1.\n  s1: S1;\n}\n\n// 36 bytes\ninterface SNode2<V = VAny, S1 = any, S2 = any> extends SNode1<V, S1> {\n  // State slot #2.\n  s2: S2;\n}\n\n// Stateful Nodes are using two different shapes. Call-sites that accessing its\n// flags to determine node type will be in a polymorphic state. In this case it\n// is perfectly fine to use polymorphic call-sites to reduce memory usage.\ntype SNode<V = VAny> = SNode1<V> | SNode2<V>;\n\n// Additional state size of the root nodes depends on the implementation of\n// root nodes. Default root implementation doesn't use any additional state and\n// stores `null` value in the additional state slot.\ntype SRoot<S> = SNode1<VRoot, S>;\n// Text nodes are storing a reference to a Text DOM node.\ntype SText = SNode1<string | number, Text>;\n// Template nodes are storing a reference to a root DOM node, DOM nodes with\n// dynamic properties and DOM nodes that will be used as a reference for\n// `parent.insertBefore(node, nextNode)` operations. Slots for DOM nodes with\n// dynamic properties that also used as a reference for insertBefore operation\n// will share the same slots, there won't be any duplicated references.\ntype STemplate = SNode1<VTemplate, Node[]>;\n// Dynamic lists doesn't have any additional state.\ntype SList = SNode1<VList, null>;\n// Components are using State Nodes with 2 state slots.\ntype SComponent = SNode2<\n  VComponent,\n  // Render function.\n  //\n  // Stateless components will share the same function.\n  // Stateful components will create closures and its memory usage will depend\n  // on the size of the closure context.\n  null | ((props: any) => VAny),\n  // Unmount hooks.\n  //\n  // Usually components don't have any unmount hooks, or they have just one\n  // unmount hook.\n  //\n  // When there is one hook, it will be stored without any additional arrays.\n  // If we add one more hook, array will be preallocated with exactly two\n  // slots `[firstHook, newHook]`. And when it grows even more, javascript\n  // engine will preallocate internal storage using a growth factor[1][2].\n  //\n  // 1. https://en.wikipedia.org/wiki/Dynamic_array#Growth_factor\n  // 2. https://github.com/v8/v8/blob/1e6775a539a3b88b25cc0ffdb52529c68aad2be8/src/objects/js-objects.h#L584-L590\n  null | (() => void) | (() => void)[]\n>;\n// Contexts doesn't have any additional state.\ntype SContext = SNode1<null, null>;\n```\n\nThis data structures were carefully designed to have small memory overhead and avoid a lot of polymorphic/megamorphic call-sites that access this data structures.\n\nTo understand why monomorphic call-sites are important for performance, it is recommended to read a great article on this topic: [\"What's up with monomorphism?\"](https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html).\n\n#### Template\n\nTemplates are precompiled into a static part that is stored in a `TemplateDescriptor` object and an array of dynamic expressions.\n\n```js\nconst Example = (attr, child) => html`<div attr=${attr}><span>${child}</span></div>`;\n```\n\nGets compiled into:\n\n```js\n// _T() creates TemplateDescriptor\nconst _tpl_1 = _T(\n  // _h() creates a template factory that uses Node.cloneNode(true) to\n  // instantiate static template structure.\n  _hN(\"<div><span></span></div>\"),\n  // SMI (Small Integer) value that packs several values:\n  // struct Data {\n  //   stateSize:6;    // The number of state slots\n  //   childrenSize:6; // The number of children slots\n  //   svg:1;          // Template with SVG elements\n  // }\n  // stateSize and childrenSize are used for preallocating arrays with\n  // exact number to avoid dynamic growth and reduce memory consumption.\n  1026,\n  // propOpCodes is an array of SMI values that stores opCodes for updating\n  // element properties.\n  [2],\n  // childOpCodes is an array of SMI values that stores opCodes for updating\n  // children nodes.\n  [7, 4],\n  // stateOpCodes is an array of SMI values that stores opCodes for traversing\n  // DOM nodes and saving references to DOM nodes into internal state when\n  // template is instantiated.\n  [4],\n  // An array of string values that stores attribute name, event names, etc.\n  [\"attr\"],\n);\n// _t() creates stateless tree node VTemplate with shared TemplateDescriptor\n// and an array of dynamic expressions.\nconst Example = (attr, child) => _t(_tpl_1, [attr, child]);\n```\n\n```ts\n// Descriptor with TemplateData and template factory function.\ntype TemplateDescriptor = VDescriptor<TemplateData, () => Element>;\n\ninterface TemplateData {\n  // stateSize / childrenSize / svg flag\n  f: number;\n  // Prop OpCodes\n  p: PropOpCode[];\n  // Child OpCodes\n  c: ChildOpCode[];\n  // State OpCodes\n  s: StateOpCode[];\n  // Strings\n  d: string[];\n}\n\n// Stateless tree node VTemplate.\ntype VTemplate<P = any> = VNode<TemplateDescriptor, P>;\n```\n\n### Template Optimizations\n\nTemplate compiler doesn't just eliminate compilation step during runtime, it also hoists static attributes and event listeners, deduplicates OpCodes, strings and template factory functions. E.g.\n\n```js\nimport { className } from \"styles.css\";\n\nconst a = (id) => html` <div class=${className} id=${id}></div> `;\nconst b = (id) => html` <div class=${className} id=${id}></div> `;\n```\n\nWill generate two different templates with shared data structures:\n\n```js\nimport { className } from \"styles.css\";\nimport { _h, _T, _t } from \"ivi\";\n\nconst EMPTY_ARRAY = [];\nconst __IVI_STRINGS__ = [\"id\"];\nconst ELEMENT_FACTORY_1 = _h('<div class=\"' + className + '\"></div>');\nconst SHARED_OP_CODES_1 = [\n  /*..*/\n];\nconst _tpl_a = _T(\n  /* factory */ ELEMENT_FACTORY_1,\n  /* flags */ 0,\n  /* propOpCodes */ SHARED_OP_CODES_1,\n  /* childOpCodes */ EMPTY_ARRAY,\n  /* stateOpCodes */ EMPTY_ARRAY,\n  /* shared strings */ __IVI_STRINGS__,\n);\nconst _tpl_b = _T(\n  /* factory */ ELEMENT_FACTORY_1,\n  /* flags */ 0,\n  /* propOpCodes */ SHARED_OP_CODES_1,\n  /* childOpCodes */ EMPTY_ARRAY,\n  /* stateOpCodes */ EMPTY_ARRAY,\n  /* shared strings */ __IVI_STRINGS__,\n);\n\nconst a = (id) => _t(_tpl_a, [id]);\nconst b = (id) => _t(_tpl_b, [id]);\n```\n\nQuite often, OpCodes that are used for different purposes (props,child,state) are going to have similar values, so when OpCodes are deduplicated they are treated as simple arrays with integers that can be used for different purposes.\n\nShared strrings (attribute keys, event names, etc) are deduplicated into one array (`__IVI_STRINGS__`) that is shared between all templates.\n\n### Custom Scheduler\n\nivi is designed as an embeddable solution, so that it can be integrated into existing frameworks or web components. The basic root node instantiated with `createRoot()` function is using microtask queue to schedule updates. Root nodes with custom scheduling algorithm can be created by defining new root factories with `defineRoot()` function.\n\n```ts\nfunction defineRoot(\n  onInvalidate: (root: Root<undefined>) => void,\n): (parentElement: Element, nextNode: Node | null) => Root<undefined>;\nfunction defineRoot<S>(\n  onInvalidate: (root: Root<S>) => void,\n): (parentElement: Element, nextNode: Node | null, state: S) => Root<S>;\n```\n\nAs an example, to remove any batching and immediately update root subtree when it is invalidated we can define the following root node:\n\n```ts\nimport { defineRoot } from \"ivi\";\n\nconst createSyncRoot = defineRoot((root) => {\n  // Immediately triggers dirty checking.\n  dirtyCheck(root);\n});\n```\n\n#### Using `requestAnimationFrame()` for Scheduling UI Updates\n\nScheduling algorithm with `rAF` batching has some potential footguns with race conditions.\n\n```js\nfunction formStateReducer(state, action) {\n  switch (action.type) {\n    case \"update\":\n      return {\n        value: action.value,\n        valid: /^[a-z]+$/.test(action.value),\n      };\n  }\n  return state;\n}\n\nconst Form = component((c) => {\n  const [state, dispatch] = useReducer(c,\n    { value: \"\", valid: false },\n    formStateReducer,\n  );\n  const onInput = (ev) => {\n    dispatch({ type: \"update\", value: ev.target.value });\n  };\n  return () => html`\n    <form>\n      <input\n        @input=${onInput}\n        *value=${state().value}\n      />\n      <input\n        type=\"submit\"\n        value=\"Submit\"\n        .disabled=${!state().valid}\n      />\n    </form>\n  `;\n});\n\nupdate(\n  createRoot(document.getElementById(\"app\")!),\n  Form(),\n);\n```\n\nIn this example, if the user types really fast and pushes an `[enter]` button, it is possible to get an execution order like this:\n\n- User types `0` into `<input>`.\n- `onChange()` event handler is triggered, `state.valid` switches into a `false`\n  state.\n- User pushes an `[enter]` button.\n- Browser sends submit request because UI is still in the old state\n  `<input type=\"submit\" .disabled={false} />`\n- `rAF` event is triggered, submit button goes into disabled state.\n\nThe simplest way to avoid issues like this is to use microtasks for batching. But if you really want to add `rAF` scheduling, it is possible to solve issues like this by introducing some synchronization primitives:\n\n```js\nimport { uiReady } from \"my-custom-scheduler\";\n\nconst onSubmit = async (ev) => {\n  await uiReady();\n  submit();\n};\n```\n\n## External Dependencies\n\nivi doesn't depend on any external libraries.\n\n## License\n\n[MIT](http://opensource.org/licenses/MIT)\n"
  },
  {
    "path": "packages/ivi/oveo.json",
    "content": "{\n  \"ivi\": {\n    \"exports\": {\n      \"component\": {\n        \"type\": \"function\",\n        \"arguments\": [\n          {\n            \"scope\": true\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "packages/ivi/package.json",
    "content": "{\n  \"name\": \"ivi\",\n  \"version\": \"5.1.0\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \"./dist/index.js\",\n    \"./test\": \"./dist/test-utils/index.js\",\n    \"./template/compiler\": \"./dist/template/compiler.js\",\n    \"./template/parser\": \"./dist/template/parser.js\",\n    \"./template/ir\": \"./dist/template/ir.js\",\n    \"./html\": \"./dist/html/index.js\",\n    \"./html/parser\": \"./dist/html/parser.js\",\n    \"./package.json\": \"./package.json\",\n    \"./oveo.json\": \"./oveo.json\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\",\n    \"!dist/**/*.tsbuildinfo\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"scripts\": {\n    \"clean\": \"rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo\"\n  },\n  \"description\": \"Lightweight Embeddable Web UI Library.\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ivi\",\n    \"ui\",\n    \"web-components\",\n    \"custom-elements\"\n  ],\n  \"author\": \"Boris Kaul <localvoid@gmail.com> (https://github.com/localvoid)\",\n  \"homepage\": \"https://github.com/localvoid/ivi\",\n  \"bugs\": \"https://github.com/localvoid/ivi/issues\",\n  \"repository\": \"github:localvoid/ivi\"\n}"
  },
  {
    "path": "packages/ivi/src/html/index.ts",
    "content": "import {\n  type TemplateDescriptor, type VAny,\n  _hN, _hE, _sN, _sE, _T, _t\n} from \"../lib/core.js\";\nimport { type TemplateNode, TemplateNodeType, compileTemplate } from \"../template/compiler.js\";\nimport { TEMPLATE_TYPE_HTM, TEMPLATE_TYPE_SVG } from \"../template/ir.js\";\nimport { TemplateParserError, formatError } from \"../template/parser.js\";\nimport { parseTemplate } from \"./parser.js\";\n\nconst DESCRIPTORS = new WeakMap<TemplateStringsArray, (exprs: any[]) => VAny>();\n\n/**\n * Creates template with HTML Elements.\n *\n * ### Element Syntax:\n *\n * - `<div name=\"value\" />` - Static Attribute.\n * - `<div name />` - Static Attribute.\n * - `<div name=${expr} />` - Dynamic attribute `element.setAttribute(name, expr)`.\n * - `<div .name=${expr} />` - Property `element[name] = expr`.\n * - `<div *name=${expr} />` - Property `element[name] = expr`, diffs against a DOM value.\n * - `<div ~name=\"value\" />` - Static style `<div style=\"name:value;\">`\n * - `<div ~name=${expr} />` - Dynamic style `element.style.setProperty(name, expr)`\n * - `<div @name=${expr} />` - Event `element.addEventListener(name, expr)`\n * - `<div ${directive} />` - Client-Side Directive `directive(element)`\n *\n * @example\n *\n *     function MyApp(content) {\n *       return html`\n *         <div id=\"App\">\n *           <h1 class=\"Title\">MyApp</h1>\n *           <div>Content ${content}</div>\n *         </div>\n *       `;\n *     }\n */\nexport const html = (strings: TemplateStringsArray, ...exprs: any[]) => {\n  let fn = DESCRIPTORS.get(strings);\n  if (fn === void 0) {\n    let result;\n    try {\n      const tpl = parseTemplate(strings, TEMPLATE_TYPE_HTM);\n      result = compileTemplate(tpl);\n    } catch (e) {\n      if (e instanceof TemplateParserError) {\n        throw Error(\n          \"Invalid template\" +\n          formatError(strings, e.message, e.staticsOffset, e.textOffset),\n        );\n      }\n      throw e;\n    }\n\n    const roots = result.roots;\n    if (roots.length === 1) {\n      const root = prepareRootNode(roots[0], _hE, _hN);\n      fn = (exprs) => createRootNode(root, exprs);\n    } else {\n      const entries = roots.map((root) => prepareRootNode(root, _hE, _hN));\n      fn = (exprs) => entries.map((root) => createRootNode(root, exprs));\n    }\n    DESCRIPTORS.set(strings, fn);\n  }\n\n  return fn(exprs);\n};\n\n/**\n * Creates template with SVG Elements.\n *\n * ### Element Syntax:\n *\n * - `<div name=\"value\" />` - Static Attribute.\n * - `<div name />` - Static Attribute.\n * - `<div name=${expr} />` - Dynamic attribute `element.setAttribute(name, expr)`.\n * - `<div .name=${expr} />` - Property `element[name] = expr`.\n * - `<div *name=${expr} />` - Property `element[name] = expr`, diffs against a DOM value.\n * - `<div ~name=\"value\" />` - Static style `<div style=\"name:value;\">`\n * - `<div ~name=${expr} />` - Dynamic style `element.style.setProperty(name, expr)`\n * - `<div @name=${expr} />` - Event `element.addEventListener(name, expr)`\n * - `<div ${directive} />` - Client-Side Directive `directive(element)`\n *\n * @example\n *\n *     function Star(content) {\n *       return svg`\n *         <svg height=\"210\" width=\"500\">\n *           <polygon\n *             points=\"100,10 40,198 190,78 10,78 160,198\"\n *             ~fill=\"lime\"\n *             ~stroke=\"purple\"\n *             ~stroke-width=\"5\"\n *             ~fill-rule=\"nonzero\"\n *           />\n *         </svg>\n *       `;\n *     }\n */\nexport const svg = (strings: TemplateStringsArray, ...exprs: any[]) => {\n  let fn = DESCRIPTORS.get(strings);\n  if (fn === void 0) {\n    let result;\n    try {\n      const tpl = parseTemplate(strings, TEMPLATE_TYPE_SVG);\n      result = compileTemplate(tpl);\n    } catch (e) {\n      if (e instanceof TemplateParserError) {\n        throw Error(\n          \"Invalid template\" +\n          formatError(strings, e.message, e.staticsOffset, e.textOffset),\n        );\n      }\n      throw e;\n    }\n\n    const roots = result.roots;\n    if (roots.length === 1) {\n      const root = prepareRootNode(roots[0], _sE, _sN);\n      fn = (exprs) => createRootNode(root, exprs);\n    } else {\n      const entries = roots.map((root) => prepareRootNode(root, _hE, _hN));\n      fn = (exprs) => entries.map((root) => createRootNode(root, exprs));\n    }\n    DESCRIPTORS.set(strings, fn);\n  }\n\n  return fn(exprs);\n};\n\ninterface RootNodeBlock {\n  readonly map: number[];\n  readonly descriptor: TemplateDescriptor;\n}\ntype RootNode = RootNodeBlock | string | number;\n\nconst prepareRootNode = (root: TemplateNode,\n  createElement: (t: string) => () => Element,\n  cloneTemplate: (t: string) => () => Element,\n) => {\n  switch (root.type) {\n    case TemplateNodeType.Block:\n      const template = root.template;\n      return {\n        map: root.exprs,\n        descriptor: _T(\n          (typeof template === \"string\")\n            ? createElement(template)\n            : cloneTemplate(template.join(\"\")),\n          root.flags,\n          root.props,\n          root.child,\n          root.state,\n          root.data,\n        ),\n      };\n    case TemplateNodeType.Text:\n      return root.value;\n    case TemplateNodeType.Expr:\n      return root.value;\n  }\n};\n\nconst createRootNode = (e: RootNode, exprs: any[]) => {\n  if (typeof e === \"object\") {\n    return _t(e.descriptor, e.map.map((i) => exprs[i]));\n  }\n  if (typeof e === \"number\") {\n    return exprs[e];\n  }\n  return e;\n};\n"
  },
  {
    "path": "packages/ivi/src/html/parser.ts",
    "content": "import {\n  type ITemplate, type IProperty, type INode, type INodeElement, type INodeText,\n  type IPropertyType, type ITemplateType,\n  NODE_TYPE_TEXT,\n  NODE_TYPE_EXPR,\n  NODE_TYPE_ELEMENT,\n  PROPERTY_TYPE_DIRECTIVE,\n  PROPERTY_TYPE_VALUE,\n  PROPERTY_TYPE_DOMVALUE,\n  PROPERTY_TYPE_EVENT,\n  PROPERTY_TYPE_STYLE,\n  PROPERTY_TYPE_ATTRIBUTE,\n} from \"../template/ir.js\";\nimport {\n  CharCode, TemplateParserError, TemplateScanner,\n} from \"../template/parser.js\";\nimport { VOID_ELEMENTS } from \"../template/shared.js\";\n\nexport const parseTemplate = (\n  s: string[] | TemplateStringsArray,\n  type: ITemplateType,\n): ITemplate => {\n  const parser = new TemplateParser(s);\n  return {\n    type,\n    children: parser.parse(),\n  };\n};\n\nexport class TemplateParser extends TemplateScanner {\n  constructor(\n    statics: string[] | TemplateStringsArray,\n  ) {\n    super(statics);\n  }\n\n  parse(): INode[] {\n    return this.parseChildrenList();\n  }\n\n  parseChildrenList(): INode[] {\n    const children: INode[] = [];\n    let whitespaceState = this.whitespace();\n    while (!this.isEnd()) {\n      const c = this.peekCharCode();\n      if (c !== -1) {\n        if (c === CharCode.LessThan) {\n          if (whitespaceState & WhitespaceState.Whitespace) {\n            if (\n              !(whitespaceState & WhitespaceState.ContainsNewline) ||\n              (whitespaceState & WhitespaceState.ContainsVerticalTab)\n            ) {\n              children.push(SPACE_TEXT_NODE);\n            }\n          }\n          const c1 = this.peekCharCode(1);\n          if (c1 === CharCode.Slash) {\n            break;\n          } else if (c1 === CharCode.ExclamationMark) {\n            this.parseComment();\n          } else {\n            children.push(this.parseElement());\n          }\n        } else {\n          children.push({\n            type: NODE_TYPE_TEXT,\n            value: this.parseText(whitespaceState),\n          });\n        }\n      } else {\n        const expr = this.expr();\n        if (expr !== -1) {\n          if (whitespaceState & WhitespaceState.Whitespace) {\n            if (\n              !(whitespaceState & WhitespaceState.ContainsNewline) ||\n              (whitespaceState & WhitespaceState.ContainsVerticalTab)\n            ) {\n              children.push(SPACE_TEXT_NODE);\n            }\n          }\n          children.push({\n            type: NODE_TYPE_EXPR,\n            value: expr,\n          });\n        } else {\n          break;\n        }\n      }\n\n      whitespaceState = this.whitespace();\n    }\n    return children;\n  }\n\n  parseComment(): void {\n    if (!this.charCode(CharCode.LessThan)) {\n      throw new TemplateParserError(\"Expected a '<' character.\", this.e, this.i);\n    }\n    if (!this.charCode(CharCode.ExclamationMark)) {\n      throw new TemplateParserError(\"Expected a '!' character.\", this.e, this.i);\n    }\n    const text = this.text;\n    let i = this.i;\n    while (i < text.length) {\n      if (text.charCodeAt(i++) === CharCode.MoreThan) {\n        break;\n      }\n    }\n    this.i = i;\n  }\n\n  parseElement(): INodeElement {\n    if (!this.charCode(CharCode.LessThan)) {\n      throw new TemplateParserError(\"Expected a '<' character.\", this.e, this.i);\n    }\n    const tag = this.regExp(IDENTIFIER);\n    if (tag === void 0) {\n      throw new TemplateParserError(\"Expected a valid tag name.\", this.e, this.i);\n    }\n\n    this.whitespace();\n    const properties = this.parseAttributes();\n\n    let children: INode[];\n    if (this.charCode(CharCode.Slash)) {\n      if (!this.charCode(CharCode.MoreThan)) {\n        throw new TemplateParserError(\"Expected a '>' character.\", this.e, this.i);\n      }\n      children = [];\n    } else {\n      if (!this.charCode(CharCode.MoreThan)) {\n        throw new TemplateParserError(\"Expected a '>' character.\", this.e, this.i);\n      }\n\n      if (!VOID_ELEMENTS.test(tag)) {\n        children = this.parseChildrenList();\n        if (!this.charCode(CharCode.LessThan)) {\n          throw new TemplateParserError(\"Expected a '<' character.\", this.e, this.i);\n        }\n        if (!this.charCode(CharCode.Slash)) {\n          throw new TemplateParserError(\"Expected a '/' character.\", this.e, this.i);\n        }\n        if (!this.string(tag)) {\n          throw new TemplateParserError(`Expected a '${tag}' tag name.`, this.e, this.i);\n        }\n        this.whitespace();\n        if (!this.charCode(CharCode.MoreThan)) {\n          throw new TemplateParserError(\"Expected a '>' character.\", this.e, this.i);\n        }\n      } else {\n        children = [];\n      }\n    }\n\n    return {\n      type: NODE_TYPE_ELEMENT,\n      tag,\n      properties,\n      children,\n    };\n  }\n\n  parseAttributes(): IProperty[] {\n    const properties: IProperty[] = [];\n    while (!this.isEnd()) {\n      const c = this.peekCharCode();\n\n      if (c === -1) { // shorthand syntax for directives\n        properties.push({\n          type: PROPERTY_TYPE_DIRECTIVE,\n          key: null,\n          value: this.expr(),\n          hoist: false,\n        });\n\n        this.whitespace();\n        continue;\n      }\n      if (c === CharCode.Slash || c === CharCode.MoreThan) {\n        return properties;\n      }\n      if (c === CharCode.Dot) { // .property\n        this.i++;\n        const key = this.regExp(JS_PROPERTY);\n        if (key === void 0) {\n          throw new TemplateParserError(\"Expected a valid property name.\", this.e, this.i);\n        }\n        this.dynamicProp(properties, PROPERTY_TYPE_VALUE, key);\n      } else if (c === CharCode.Asterisk) { // *value\n        this.i++;\n        const key = this.regExp(JS_PROPERTY);\n        if (key === void 0) {\n          throw new TemplateParserError(\"Expected a valid property name.\", this.e, this.i);\n        }\n        this.dynamicProp(properties, PROPERTY_TYPE_DOMVALUE, key);\n      } else if (c === CharCode.Tilde) { // ~style\n        this.i++;\n        const key = this.regExp(IDENTIFIER);\n        if (key === void 0) {\n          throw new TemplateParserError(\"Expected a valid style name.\", this.e, this.i);\n        }\n        let value: string | number;\n        if (!this.charCode(CharCode.EqualsTo)) {\n          throw new TemplateParserError(\"Expected a '=' character.\", this.e, this.i);\n        }\n        const c = this.peekCharCode();\n        if (c !== -1) {\n          if (c !== CharCode.DoubleQuote) {\n            throw new TemplateParserError(\"Expected a string or an expression.\", this.e, this.i);\n          }\n          value = this.parseAttributeString();\n        } else {\n          value = this.expr();\n          if (value === -1) {\n            throw new TemplateParserError(\"Expected a string or an expression.\", this.e, this.i);\n          }\n        }\n        properties.push({\n          type: PROPERTY_TYPE_STYLE,\n          key,\n          value,\n          hoist: false,\n        });\n      } else if (c === CharCode.AtSign) { // @event\n        this.i++;\n        const key = this.regExp(IDENTIFIER);\n        if (key === void 0) {\n          throw new TemplateParserError(\"Expected a valid event name.\", this.e, this.i);\n        }\n        this.dynamicProp(properties, PROPERTY_TYPE_EVENT, key);\n      } else {\n        const key = this.regExp(IDENTIFIER);\n        if (key === void 0) {\n          throw new TemplateParserError(\"Expected a valid attribute name.\", this.e, this.i);\n        }\n\n        let value: string | boolean | number = true;\n        if (this.charCode(CharCode.EqualsTo)) { // =\n          const c = this.peekCharCode();\n          if (c !== -1) {\n            if (c !== CharCode.DoubleQuote) {\n              throw new TemplateParserError(\"Expected a string or an expression.\", this.e, this.i);\n            }\n            value = this.parseAttributeString();\n          } else {\n            value = this.expr();\n            if (value === -1) {\n              throw new TemplateParserError(\"Expected a string or an expression.\", this.e, this.i);\n            }\n          }\n        }\n        properties.push({\n          type: PROPERTY_TYPE_ATTRIBUTE,\n          key,\n          value,\n        });\n      }\n      this.whitespace();\n    }\n    throw new TemplateParserError(\"Expected a '>' character\", this.e, this.i);\n  }\n\n  dynamicProp(properties: IProperty[], type: IPropertyType, key: string) {\n    if (!this.charCode(CharCode.EqualsTo)) {\n      throw new TemplateParserError(\"Expected a '=' character.\", this.e, this.i);\n    }\n    const value = this.expr();\n    if (value === -1) {\n      throw new TemplateParserError(\"Expected an expression.\", this.e, this.i);\n    }\n    properties.push({\n      type,\n      key,\n      value,\n      hoist: false,\n    } as IProperty);\n  }\n\n  parseAttributeString(): string {\n    const text = this.text;\n    const textLength = text.length;\n    let i = this.i;\n    let s = \"\";\n    let c = text.charCodeAt(i);\n\n    let delimCharCode: number;\n    if (c === CharCode.SingleQuote) {\n      delimCharCode = CharCode.SingleQuote;\n    } else if (c === CharCode.DoubleQuote) {\n      delimCharCode = CharCode.DoubleQuote;\n    } else {\n      throw new TemplateParserError(\"Expected ' or \\\" character.\", this.e, i);\n    }\n\n    let start = ++i;\n    while (i < textLength) {\n      c = text.charCodeAt(i++);\n      if (c === delimCharCode) {\n        const end = i - 1;\n        this.i = i;\n        return s + text.substring(start, end);\n      }\n    }\n\n    throw new TemplateParserError(\n      `Attribute string should be closed with a '${String.fromCharCode(delimCharCode)}' character`,\n      this.e,\n      i,\n    );\n  }\n\n  parseText(state: number): string {\n    const text = this.text;\n    let chars = [];\n    while (!this.isEnd()) {\n      if (this.i < text.length) {\n        const c = text.charCodeAt(this.i);\n        if (c === CharCode.LessThan) {\n          break;\n        }\n        if (c === CharCode.Space || c === CharCode.Tab) {\n          this.i++;\n          state |= WhitespaceState.Whitespace;\n          continue;\n        }\n        if (c === CharCode.Newline || c === CharCode.CarriageReturn) {\n          this.i++;\n          state |= WhitespaceState.ContainsNewline;\n          continue;\n        }\n        if (c === CharCode.VerticalTab) {\n          this.i++;\n          state |= WhitespaceState.ContainsVerticalTab;\n          continue;\n        }\n        if (state & WhitespaceState.Whitespace) {\n          if (\n            (state & (WhitespaceState.TextContent | WhitespaceState.ContainsVerticalTab)) ||\n            !(state & WhitespaceState.ContainsNewline)\n          ) {\n            chars.push(CharCode.Space);\n          }\n        }\n        state = WhitespaceState.TextContent;\n        chars.push(c);\n        this.i++;\n      }\n\n      if (this.peekExpr() !== -1) {\n        break;\n      }\n    }\n\n    if (state & WhitespaceState.Whitespace) {\n      if (!(state & WhitespaceState.ContainsNewline) || (state & WhitespaceState.ContainsVerticalTab)) {\n        chars.push(CharCode.Space);\n      }\n    }\n    if (chars.length !== 0) {\n      if (chars.length > (1 << 16)) {\n        throw new TemplateParserError(\"Text string is too long (>64k)\", this.e, this.i);\n        // Text nodes are splitted into two nodes when they exceed their length limit (64k).\n        // https://github.com/chromium/chromium/blob/91159249db3086f17b28b7a060f55ec0345c24c7/third_party/blink/renderer/core/dom/text.h#L42\n      }\n      return _String.fromCharCode(...chars);\n    }\n    return \"\";\n  }\n\n  whitespace(): number {\n    const text = this.text;\n    let state = 0;\n    let i = this.i;\n    while (i < text.length) {\n      const c = text.charCodeAt(i);\n      if (c === CharCode.Space || c === CharCode.Tab) {\n        i++;\n        continue;\n      }\n      if (c === CharCode.Newline || c === CharCode.CarriageReturn) {\n        i++;\n        state |= WhitespaceState.ContainsNewline;\n        continue;\n      }\n      if (c === CharCode.VerticalTab) {\n        i++;\n        state |= WhitespaceState.ContainsVerticalTab;\n        continue;\n      }\n      break;\n    }\n\n    if (i !== this.i) {\n      this.i = i;\n      return state | WhitespaceState.Whitespace;\n    }\n    return 0;\n  }\n}\n\nconst enum WhitespaceState {\n  Whitespace = 1,\n  ContainsNewline = 1 << 1,\n  ContainsVerticalTab = 1 << 2,\n  TextContent = 1 << 3,\n}\n\nconst _String = String;\nconst IDENTIFIER = /[a-zA-Z_][\\w-]*/y;\nconst JS_PROPERTY = /[a-zA-Z_$][\\w]*/y;\n\nconst SPACE_TEXT_NODE: INodeText = {\n  type: NODE_TYPE_TEXT,\n  value: \" \",\n};\n"
  },
  {
    "path": "packages/ivi/src/index.ts",
    "content": "export {\n  EMPTY_ARRAY,\n  // Stateful Nodes\n  type SNode,\n  type Root, type Component,\n  // Stateless Nodes\n  type VAny, type VRoot, type VTemplate, type VComponent, type VList,\n  // Template\n  type TemplateDescriptor, type ElementDirective,\n  _hN, _hE, _sN, _sE, _T, _t,\n  // Components\n  type ComponentFactory, type Effect,\n  component, getProps, invalidate,\n  useUnmount, useEffect,\n  createEffectHandler, useAnimationFrameEffect, useIdleEffect,\n  type VContext,\n  context,\n  // Dynamic List\n  List,\n  // Root\n  type RootFactory,\n  defineRoot, dirtyCheck, update, unmount,\n  createRoot,\n} from \"./lib/core.js\";\nexport {\n  eventDispatcher, findDOMNode, containsDOMElement, hasDOMElement,\n} from \"./lib/utils.js\";\nexport {\n  useMemo,\n  useState,\n  type Dispatch, useReducer,\n} from \"./lib/state.js\";\nexport {\n  preventUpdates, strictEq, shallowEq, shallowEqArray,\n} from \"./lib/equal.js\";\nexport { html, svg } from \"./html/index.js\";"
  },
  {
    "path": "packages/ivi/src/lib/core.ts",
    "content": "import {\n  type TemplateData,\n  TemplateFlags, ChildOpCode, PropOpCode, StateOpCode, CommonPropType,\n} from \"./template.js\";\n\nexport const EMPTY_ARRAY: any[] = [];\n\n/**\n * Globally shared strings are automatically generated by template compiler.\n */\nconst __IVI_STRINGS__: string[] = [\"IVI:fa7327d9-0034-492d-bfdf-576548b2d9cc\"];\n\n// Store global variables in a local scope as const variables so that JIT\n// compiler could easily inline functions and eliminate checks in case global\n// variables are overriden.\nconst _Object = Object;\nconst _Array = Array;\nconst _isArray: <T = any>(a: any) => a is T[] = _Array.isArray;\nconst _Map = Map;\nconst _Int32Array = Int32Array;\nconst _queueMicrotask = queueMicrotask;\nconst _requestAnimationFrame = requestAnimationFrame;\nconst _requestIdleCallback = requestIdleCallback;\n\nconst nodeProto = Node.prototype;\nconst elementProto = Element.prototype;\nconst doc = document;\n\n// Template containers are used to create static templates from HTML strings\n// via `innerHTML`.\nconst HTM_TEMPLATE = /**@__PURE__*/doc.createElement(\"template\");\nconst HTM_TEMPLATE_CONTENT = HTM_TEMPLATE.content;\nconst _SVG_TEMPLATE = /**@__PURE__*/doc.createElement(\"template\");\nconst SVG_TEMPLATE = /**@__PURE__*/doc.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n_SVG_TEMPLATE.content.appendChild(SVG_TEMPLATE);\nconst SVG_TEMPLATE_CONTENT = _SVG_TEMPLATE.content.firstChild as Element;\n\n// Store Node/Element methods to avoid going through a long prototype chain and\n// avoid megamorphic call-sites when accessing DOM nodes.\n\n/** `Node.prototype.insertBefore` */\nconst nodeInsertBefore: <T extends Node>(this: Node, node: T, child: Node | null) => T = nodeProto.insertBefore;\n/** `Node.prototype.removeChild`. */\nconst nodeRemoveChild: <T extends Node>(this: Node, node: T) => T = nodeProto.removeChild;\n/** `Node.prototype.cloneNode`. */\nconst nodeCloneNode: (this: Node, deep?: boolean | undefined) => Node = nodeProto.cloneNode;\ndeclare global {\n  interface Element {\n    moveBefore<T extends Element | CharacterData>(node: T, child: Node | null): void;\n  }\n}\n/** `Element.prototype.moveBefore` */\nconst elementMoveBefore: <T extends Element | CharacterData>(this: Element, node: T, child: Node | null) => void = elementProto.moveBefore ?? nodeInsertBefore;\n/** `Element.prototype.setAttribute` */\nconst elementSetAttribute: (this: Element, qualifiedName: string, value: string) => void = elementProto.setAttribute;\n/** `Element.prototype.removeAttribute` */\nconst elementRemoveAttribute: (this: Element, qualifiedName: string) => void = elementProto.removeAttribute;\n/** `EventTarget.prototype.addEventListener` */\nconst elementAddEventListener = elementProto.addEventListener;\n/** `EventTarget.prototype.removeEventListener` */\nconst elementRemoveEventListener = elementProto.removeEventListener;\n\n/** `Object.getOwnPropertyDescriptor(o, p)` */\nconst getDescriptor = (o: any, p: string | number | symbol) => _Object.getOwnPropertyDescriptor(o, p);\n\n/** `get Node.prototype.firstChild` */\nconst nodeGetFirstChild: (this: Node) => ChildNode | null = /*@__PURE__*/getDescriptor(nodeProto, \"firstChild\")!.get!;\n/** `get Node.prototype.nextSibling` */\nconst nodeGetNextSibling: (this: Node) => ChildNode | null = /*@__PURE__*/getDescriptor(nodeProto, \"nextSibling\")!.get!;\n/** `set Node.prototype.textContent` */\nconst nodeSetTextContent: (this: Node, value: string | number) => void = /*@__PURE__*/getDescriptor(nodeProto, \"textContent\")!.set!;\n/** `set Element.prototype.innerHTML` */\nconst elementSetInnerHTML: (this: Element, value: string) => void = /*@__PURE__*/getDescriptor(elementProto, \"innerHTML\")!.set!;\n/** `set Element.prototype.className` */\nconst elementSetClassName: (this: Element, value: string) => void = /*@__PURE__*/getDescriptor(elementProto, \"className\")!.set!;\n/** `get HTMLElement.prototype.style`. */\nconst htmlElementGetStyle: (this: HTMLElement) => CSSStyleDeclaration = /*@__PURE__*/getDescriptor(HTMLElement.prototype, \"style\")!.get!;\n/** `get SVGElement.prototype.style` */\nconst svgElementGetStyle: (this: SVGElement) => CSSStyleDeclaration = /*@__PURE__*/getDescriptor(SVGElement.prototype, \"style\")!.get!;\n\n/**\n * Render Context.\n */\nexport interface RenderContext {\n  /** Parent DOM Element */\n  p: Element;\n  /** Next DOM Node. */\n  n: Node | null;\n  /** Template state index. */\n  si: number;\n  /** DOM Side Effects */\n  e: Array<() => void>;\n}\n\n// When object is sealed and stored in a const variable, JIT compiler can\n// eliminate object map(shape) checks when accessing its properties.\n/**\n * Global Render Context.\n */\nexport const RENDER_CONTEXT: RenderContext = _Object.seal({\n  p: null!,\n  n: null,\n  si: 0,\n  e: [],\n});\n\n// Types are stored as bit flags so that we could perform multiple tests with a\n// single bitwise operation. E.g. `flags & (List | Array)`.\n/**\n * Flags.\n */\nexport const enum Flags {\n  // VNode and SNode flags\n  Template = 1,\n  Component = 1 << 1,\n  List = 1 << 2,\n  Array = 1 << 3,\n  Text = 1 << 4,\n  Root = 1 << 5,\n  Context = 1 << 6,\n  TypeMask = (1 << 7) - 1,\n\n  // Component Dirty Flags\n  Dirty = 1 << 7,\n  DirtySubtree = 1 << 8,\n\n  // Update Flags\n  ForceUpdate = 1 << 9,\n  DisplaceNode = 1 << 10,\n}\n\n/**\n * Stateful Node.\n */\nexport type SAny =\n  | null       // Hole\n  | SRoot      // Root\n  | SText      // Text\n  | STemplate  // Template\n  | SList      // Dynamic List\n  | SComponent // Component\n  | SContext   // Context\n  ;\n\n/**\n * Stateful Node.\n */\nexport type SNode<V = VAny> = SNode1<V> | SNode2<V>;\n\n/**\n * Stateful Node with 1 state slot.\n *\n * @typeparam S1 State slot #1.\n */\nexport interface SNode1<V = VAny, S1 = any> {\n  /** Stateless Node. */\n  v: V;\n  /** See {@link Flags} for details. */\n  f: Flags;\n  /** Children Stateful Nodes. */\n  c: SNode | (SNode | null)[] | null;\n  /** Parent Stateful Node. */\n  p: SNode | null,\n  /** State slot #1. */\n  s1: S1;\n}\n\n/**\n * Stateful Node with 2 state slots.\n *\n * @typeparam S1 State slot #1.\n * @typeparam S2 State slot #2.\n */\nexport interface SNode2<V = VAny, S1 = any, S2 = any> extends SNode1<V, S1> {\n  /** State slot #2. */\n  s2: S2;\n}\n\n/** Stateful Root Node. */\nexport type SRoot<S = any> = SNode1<VRoot, S>;\n/** Stateful Root Node. */\nexport type Root<S = any> = SRoot<S>;\n/** Stateful Text Node. */\nexport type SText = SNode1<string | number, Text>;\n/** Stateful Template Node. */\nexport type STemplate = SNode1<VTemplate, Node[]>;\n/** Stateful List Node. */\nexport type SList = SNode1<VList, null>;\n/** Stateful Component Node. */\nexport type SComponent<P = any> = SNode2<\n  VComponent,\n  /** Render function. */\n  null | ComponentRenderFn<P>,\n  /** Unmount hooks. */\n  null | (() => void) | (() => void)[]\n>;\n/** Stateful Component Node. */\nexport type Component<P = any> = SComponent<P>;\n\nexport type ComponentRenderFn<P = any> = (props: P) => VAny;\n\nexport type SContext = SNode1<VContext, null>;\n\n/**\n * Creates a Stateful Node instance.\n *\n * @param v VNode.\n * @returns {@link SNode} instance.\n */\nexport const createSNode = <V extends VAny, S>(\n  f: Flags,\n  v: V,\n  c: SNode | Array<SNode1 | null> | null,\n  p: SNode | null,\n  s1: S,\n): SNode1<V, S> => ({ f, v, c, p, s1 });\n\n/**\n * Stateless Tree Node.\n */\nexport type VAny =\n  | null       // Hole\n  | undefined  // Hole\n  | false      // Hole\n  | string     // Text\n  | number     // Text\n  | VRoot      // Root\n  | VTemplate  // Template\n  | VComponent // Component\n  | VContext   // Context\n  | VList      // Dynamic List with track by key algo\n  | VAny[]     // Dynamic List with track by index algo\n  ;\n\n/**\n * Stateless Node Descriptor.\n */\nexport interface VDescriptor<P1 = any, P2 = any> {\n  /** See {@link Flags} for details. */\n  readonly f: Flags;\n  /** First property. */\n  readonly p1: P1;\n  /** Second property. */\n  readonly p2: P2;\n}\n\n/** Root Invalidate Hook. */\nexport type OnRootInvalidated<S> = (root: SRoot<S>, state: S) => void;\n/** Root Descriptor. */\nexport type RootDescriptor<S = any> = VDescriptor<OnRootInvalidated<S>, null>;\n\n/** Template Descriptor */\nexport type TemplateDescriptor = VDescriptor<TemplateData, () => Element>;\n\n/** Component Descriptor */\nexport type ComponentDescriptor<P = any> = VDescriptor<\n  // Component factory function.\n  ComponentFactoryFn<P>,\n  // `areEqual()` function.\n  undefined | ((prev: P, next: P) => boolean)\n>;\n\nexport type ComponentFactoryFn<P = any> = (component: Component) => (props: P) => VAny;\n\n/** Context Descriptor */\nexport type ContextDescriptor = VDescriptor<null, null>;\n\n/** List Descriptor */\nexport type ListDescriptor = VDescriptor<null, null>;\n\n/**\n * Stateless Node.\n *\n * @typeparam D Descriptor.\n * @typeparam P Property.\n */\nexport interface VNode<D extends VDescriptor<any, any> = VDescriptor<any, any>, P = any> {\n  /** Descriptor. */\n  readonly d: D;\n  /** Property. */\n  readonly p: P;\n}\n\n/** Stateless Root Node. */\nexport type VRoot = VNode<RootDescriptor, RootProps>;\n/** Stateless Template Node. */\nexport type VTemplate<P = any> = VNode<TemplateDescriptor, P>;\n/** Stateless Component Node. */\nexport type VComponent<P = any> = VNode<ComponentDescriptor, P>;\n/** Stateless Context Node. */\nexport type VContext<T = any> = VNode<ContextDescriptor, ContextProps<T>>;\n/** Stateless List Node. */\nexport type VList<K = any> = VNode<ListDescriptor, ListProps<K>>;\n\n/**\n * Stateless Root Node Props.\n *\n * Contains a DOM position where root children should mounted.\n */\nexport interface RootProps {\n  /** Parent Element */\n  p: Element,\n  /** Next Node */\n  n: Node | null,\n}\n\n/**\n * Stateless Context Node Props.\n */\nexport interface ContextProps<T = any> {\n  /** Context Value. */\n  v: T;\n  /** Stateless Child Node. */\n  c: VAny;\n}\n\n/**\n * Stateless List Node Props.\n *\n * Contains unique keys for stateless nodes and stateless nodes.\n */\nexport interface ListProps<K = any> {\n  /** Unique Keys. */\n  k: K[],\n  /** Stateless Nodes. */\n  v: VAny[],\n}\n\n/**\n * Element Directive.\n */\nexport type ElementDirective = <E extends Element>(element: E) => void;\n\nexport const _flushDOMEffects = () => {\n  const e = RENDER_CONTEXT.e;\n  if (e.length > 0) {\n    RENDER_CONTEXT.e = [];\n    for (let i = 0; i < e.length; i++) {\n      e[i]();\n    }\n  }\n};\n\nconst _updateTemplateProperties = (\n  currentElement: Element,\n  opCodes: PropOpCode[],\n  data: string[],\n  state: Node[],\n  prevProps: any[] | null,\n  nextProps: any[],\n  svg: boolean,\n) => {\n  let style: CSSStyleDeclaration | undefined;\n  for (let i = 0; i < opCodes.length; i++) {\n    const op = opCodes[i];\n    const type = op & PropOpCode.TypeMask;\n    const dataIndex = op >> PropOpCode.DataShift;\n    if (type === PropOpCode.SetNode) {\n      currentElement = state[dataIndex] as Element;\n      style = void 0;\n    } else {\n      const propsIndex = (op >> PropOpCode.InputShift) & PropOpCode.Mask6;\n      const next = nextProps[propsIndex];\n\n      if (type === PropOpCode.DiffDOMProperty) {\n        const key = data[dataIndex];\n        if (prevProps === null) {\n          if (next !== void 0) {\n            (currentElement as Record<string, any>)[key] = next;\n          }\n        } else if ((currentElement as Record<string, any>)[key] !== next) {\n          (currentElement as Record<string, any>)[key] = next;\n        }\n      } else {\n        let prev;\n        if (prevProps !== null) {\n          prev = prevProps[propsIndex];\n        }\n\n        if (prev !== next) {\n          if (type === PropOpCode.Common) {\n            if (dataIndex === CommonPropType.ClassName) {\n              if (next !== \"\" && next != null && next !== false) {\n                elementSetClassName.call(currentElement, next);\n              } else if (prev !== \"\" && prev != null && prev !== false) {\n                elementSetClassName.call(currentElement, \"\");\n              }\n            } else if (dataIndex === CommonPropType.TextContent) {\n              if (next !== \"\" && next != null && next !== false) {\n                if (prev == null || prev === \"\" || prev === false) {\n                  nodeSetTextContent.call(currentElement, next);\n                } else {\n                  nodeGetFirstChild.call(currentElement)!.nodeValue = next;\n                }\n              } else if (prev != null && prev !== \"\" && prev !== false) {\n                nodeSetTextContent.call(currentElement, \"\");\n              }\n            } else { // CommonPropType.InnerHTML\n              if (next !== \"\" && next != null && next !== false) {\n                elementSetInnerHTML.call(currentElement, next);\n              } else if (prev !== \"\" && prev != null && prev !== false) {\n                nodeSetTextContent.call(currentElement, \"\");\n              }\n            }\n          } else if (type === PropOpCode.Directive) {\n            (next as ElementDirective)(currentElement);\n          } else {\n            const key = data[dataIndex];\n            if (type === PropOpCode.Attribute) {\n              if (next !== false && next != null) {\n                elementSetAttribute.call(currentElement, key, next as string);\n              } else if (prev !== false && prev != null) {\n                elementRemoveAttribute.call(currentElement, key);\n              }\n            } else if (type === PropOpCode.Property) {\n              (currentElement as Record<string, any>)[key] = next;\n            } else if (type === PropOpCode.Style) {\n              if (next !== false && next != null) {\n                if (style === void 0) {\n                  style = (svg === false)\n                    ? htmlElementGetStyle.call(currentElement as HTMLElement)\n                    : svgElementGetStyle.call(currentElement as SVGElement);\n                }\n                style!.setProperty(key, next as string);\n              } else if (prev !== false && prev != null) {\n                if (style === void 0) {\n                  style = (svg === false)\n                    ? htmlElementGetStyle.call(currentElement as HTMLElement)\n                    : svgElementGetStyle.call(currentElement as SVGElement);\n                }\n                style!.removeProperty(key);\n              }\n            } else { // PropOpCode.Event\n              if (prev != null && prev !== false) {\n                elementRemoveEventListener.call(currentElement, key, prev);\n              }\n              if (next != null && next !== false) {\n                elementAddEventListener.call(currentElement, key, next);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n};\n\nconst _assignTemplateSlots = (\n  currentNode: Node,\n  opCodes: StateOpCode[],\n  offset: number,\n  endOffset: number,\n  state: Node[],\n) => {\n  const ctx = RENDER_CONTEXT;\n  while (true) {\n    const op = opCodes[offset++];\n    if (op & StateOpCode.Save) {\n      state[++ctx.si] = currentNode;\n    }\n    if (op & StateOpCode.EnterOrRemove) {\n      const enterOffset = op >> StateOpCode.OffsetShift;\n      // Enter offset is used to disambiguate between enter and remove\n      // operations. Remove operations will always have a 0 enterOffset.\n      if (enterOffset) { // Enter\n        _assignTemplateSlots(\n          nodeGetFirstChild.call(currentNode)!,\n          opCodes,\n          offset,\n          offset += enterOffset,\n          state,\n        );\n      } else { // Remove\n        // Remove operation implies that current node is always a comment node\n        // followed by a text node.\n        const commentNode = currentNode as Comment;\n        state[++ctx.si] = currentNode = nodeGetNextSibling.call(currentNode)!;\n        commentNode.remove();\n      }\n    }\n    if (offset === endOffset) {\n      return;\n    }\n    currentNode = nodeGetNextSibling.call(currentNode as ChildNode)!;\n  }\n};\n\nconst _mountList = (\n  parentState: SNode1,\n  flags: Flags,\n  children: VAny[],\n  vNode: VAny,\n): SNode1 => {\n  let i = children.length;\n  const sChildren = _Array(i);\n  const sNode = createSNode(flags, vNode, sChildren, parentState, null);\n  while (i > 0) {\n    sChildren[--i] = _mount(sNode, children[i]);\n  }\n  return sNode;\n};\n\nconst _updateArray = (\n  parentSNode: SNode1,\n  sNode: SNode1,\n  next: VAny,\n  updateFlags: Flags,\n): SNode1 | null => {\n  if (!_isArray(next)) {\n    _unmount(sNode, true);\n    return _mount(parentSNode, next);\n  }\n  const prevSChildren = sNode.c as (SNode1 | null)[];\n  let nextSChildren = prevSChildren;\n  let prevLength = prevSChildren.length;\n  let nextLength = next.length;\n  if (nextLength !== prevLength) {\n    sNode.c = nextSChildren = _Array(nextLength);\n    while (prevLength > nextLength) {\n      const sChild = prevSChildren[--prevLength];\n      if (sChild !== null) {\n        _unmount(sChild, true);\n      }\n    }\n    while (nextLength > prevLength) {\n      nextSChildren[--nextLength] = _mount(sNode, next[nextLength]);\n    }\n  }\n  while (nextLength > 0) {\n    nextSChildren[--nextLength] = _update(\n      sNode,\n      prevSChildren[nextLength],\n      next[nextLength],\n      updateFlags,\n    );\n  }\n  return sNode;\n};\n\n/**\n * Updates a Stateful Node with a new Stateless Node.\n *\n * @param parentSNode Parent Stateul Node.\n * @param sNode Stateful Node to update.\n * @param next New Stateless Node.\n * @param updateFlags Update flags (ForceUpdate and DisplaceNode).\n * @returns Stateful Node.\n */\nconst _update = (\n  parentSNode: SNode,\n  sNode: SNode | null,\n  next: VAny,\n  updateFlags: number,\n): SNode | null => {\n  if (sNode === null) {\n    return _mount(parentSNode, next);\n  }\n  if (next === false || next == null || next === \"\") {\n    _unmount(sNode, true);\n    return null;\n  }\n\n  // polymorphic call-site\n  const children = sNode.c;\n  const prev = sNode.v;\n  const state = sNode.s1;\n  const flags = sNode.f;\n  const type = flags & Flags.TypeMask;\n  sNode.f = type;\n\n  // Reassign to reduce memory consumption even if next value is strictly\n  // equal to the prev value.\n  sNode.v = next;\n\n  // Text and Array should be checked before Component, Template and List\n  // because their stateless nodes are represented with basic string and array\n  // types.\n  if (type === Flags.Text) {\n    const ctx = RENDER_CONTEXT;\n    if (typeof next !== \"object\") {\n      if (prev !== next) {\n        (state as Text).nodeValue = next as string;\n      }\n      if (updateFlags & Flags.DisplaceNode) {\n        elementMoveBefore!.call(\n          ctx.p,\n          (state as Text),\n          ctx.n,\n        );\n      }\n      ctx.n = state;\n      return sNode;\n    }\n    nodeRemoveChild!.call(ctx.p, (state as Text));\n    return _mount(parentSNode, next)!;\n  }\n\n  if (prev === next) {\n    _dirtyCheck(sNode, updateFlags);\n    return sNode;\n  }\n  // Dirty flags should be cleared after dirty checking.\n  sNode.f = type;\n\n  if (type === Flags.Array) {\n    return _updateArray(parentSNode, sNode, next, updateFlags);\n  }\n\n  const descriptor = (next as VNode).d;\n  const nextProps = (next as VNode).p;\n  const prevProps = (prev as VNode).p;\n  if ((prev as VNode).d !== descriptor) {\n    _unmount(sNode, true);\n    return _mount(parentSNode, next);\n  }\n\n  if (type === Flags.Component) {\n    if (\n      ((flags | updateFlags) & (Flags.Dirty | Flags.ForceUpdate)) ||\n      (descriptor.p2 === void 0) ||\n      (descriptor.p2(prevProps, nextProps) !== true)\n    ) {\n      sNode.c = _update(\n        sNode,\n        children as SNode,\n        (state as ComponentRenderFn)(nextProps),\n        updateFlags,\n      );\n    } else if (children !== null) {\n      _dirtyCheck(children as SNode, updateFlags);\n    }\n  } else if (type === Flags.Template) {\n    const ctx = RENDER_CONTEXT;\n    const parentElement = ctx.p;\n    const tplData = (descriptor as TemplateDescriptor).p1;\n    const flags = tplData.f;\n    const data = tplData.d;\n    const propsOpCodes = tplData.p;\n    const childOpCodes = tplData.c;\n    const rootDOMNode = state[0] as Element;\n\n    if (updateFlags & Flags.DisplaceNode) {\n      updateFlags ^= Flags.DisplaceNode;\n      elementMoveBefore!.call(parentElement, rootDOMNode, ctx.n);\n    }\n\n    _updateTemplateProperties(\n      rootDOMNode,\n      propsOpCodes,\n      data,\n      state as Node[],\n      prevProps,\n      nextProps,\n      !!(flags & TemplateFlags.Svg),\n    );\n\n    if (children !== null) {\n      ctx.p = rootDOMNode;\n      ctx.n = null;\n\n      let childrenIndex = 0;\n      for (let i = 0; i < childOpCodes.length; i++) {\n        const childOpCode = childOpCodes[i];\n        const type = childOpCode & ChildOpCode.Type;\n        const value = childOpCode >> ChildOpCode.ValueShift;\n        if (type === ChildOpCode.Child) {\n          (children as (SNode | null)[])[childrenIndex] =\n            _update(\n              sNode,\n              (children as (SNode | null)[])[childrenIndex++],\n              nextProps[value],\n              updateFlags,\n            );\n        } else if (type === ChildOpCode.SetNext) {\n          ctx.n = state[value];\n        } else { // ChildOpCode.SetParent\n          ctx.p = state[value] as Element;\n          ctx.n = null;\n        }\n      }\n\n      ctx.p = parentElement;\n    }\n\n    ctx.n = rootDOMNode;\n  } else if (type === Flags.List) {\n    _updateList(\n      sNode as SList,\n      prevProps,\n      nextProps,\n      updateFlags,\n    );\n  } else { // Context\n    if (prevProps.v !== nextProps.v) {\n      updateFlags |= Flags.ForceUpdate;\n    }\n    sNode.c = _update(\n      sNode,\n      children as SNode | null,\n      nextProps.c,\n      updateFlags,\n    );\n  }\n\n  return sNode;\n};\n\n/**\n * Mounts Stateless Node.\n *\n * @param parentSNode Parent Stateful Node.\n * @param v Stateless Node.\n * @returns Mounted Stateful Node.\n */\nconst _mount = (parentSNode: SNode, v: VAny): SNode | null => {\n  if (v !== false && v != null) {\n    if (typeof v === \"object\") {\n      if (_isArray(v)) {\n        return _mountList(parentSNode, Flags.Array, v, v);\n      } else {\n        const descriptor = v.d;\n        const props = v.p;\n        const descriptorP1 = descriptor.p1;\n        const type = descriptor.f & (Flags.Template | Flags.Component | Flags.List);\n        if (type === Flags.Template) {\n          const ctx = RENDER_CONTEXT;\n          const parentDOMElement = ctx.p;\n          const nextDOMNode = ctx.n;\n          const tplData = descriptorP1 as TemplateData;\n          const data = tplData.d;\n          const propsOpCodes = tplData.p;\n          const stateOpCodes = tplData.s;\n          const childOpCodes = tplData.c;\n          const flags = tplData.f;\n          const rootDOMNode = (descriptor as TemplateDescriptor).p2();\n          const state = _Array<Node>(flags & TemplateFlags.Mask6);\n          state[0] = rootDOMNode;\n\n          if (stateOpCodes.length > 0) {\n            ctx.si = 0;\n            _assignTemplateSlots(\n              nodeGetFirstChild.call(rootDOMNode)!,\n              stateOpCodes,\n              0,\n              stateOpCodes.length,\n              state,\n            );\n          }\n          _updateTemplateProperties(\n            rootDOMNode,\n            propsOpCodes,\n            data,\n            state,\n            null,\n            props,\n            !!(flags & TemplateFlags.Svg),\n          );\n\n          const sNode = createSNode(\n            Flags.Template,\n            v,\n            null,\n            parentSNode,\n            state,\n          );\n          if (childOpCodes.length > 0) {\n            const children = _Array<SNode | null>(\n              (flags >> TemplateFlags.ChildrenSizeShift) & TemplateFlags.Mask6\n            );\n            sNode.c = children;\n            ctx.p = rootDOMNode;\n            ctx.n = null;\n            let childrenIndex = 0;\n            for (let i = 0; i < childOpCodes.length; i++) {\n              const childOpCode = childOpCodes[i];\n              const type = childOpCode & ChildOpCode.Type;\n              const value = childOpCode >> ChildOpCode.ValueShift;\n              if (type === ChildOpCode.Child) {\n                children[childrenIndex++] = _mount(sNode, props[value]);\n              } else if (type === ChildOpCode.SetNext) {\n                ctx.n = state[value];\n              } else { // ChildOpCode.SetParent\n                ctx.p = state[value] as Element;\n                ctx.n = null;\n              }\n            }\n            ctx.p = parentDOMElement;\n          }\n          ctx.n = rootDOMNode;\n\n          nodeInsertBefore!.call(parentDOMElement, rootDOMNode, nextDOMNode);\n          return sNode;\n        } else if (type === Flags.Component) {\n          const sNode: Component = {\n            f: Flags.Component,\n            v: v as VComponent,\n            c: null,\n            p: parentSNode,\n            s1: null!,\n            s2: null,\n          };\n          const renderFn = (descriptorP1 as ComponentFactoryFn)(sNode);\n          sNode.c = _mount(sNode, renderFn(props));\n          sNode.s1 = renderFn;\n          return sNode;\n        } else if (type === Flags.List) {\n          return _mountList(parentSNode, Flags.List, (props as ListProps).v, v);\n        }\n        // Context\n        const sNode = createSNode(Flags.Context, v, null, parentSNode, null);\n        sNode.c = _mount(sNode, (props as ContextProps).c);\n        return sNode;\n      }\n    } else if (v !== \"\") { // text\n      const ctx = RENDER_CONTEXT;\n      const next = ctx.n;\n      const e = doc.createTextNode(v as string);\n      ctx.n = e;\n      nodeInsertBefore.call(ctx.p, e, next);\n      return createSNode(Flags.Text, v, null, parentSNode, e);\n    }\n  }\n  return null;\n};\n\n/**\n * Performs a Dirty Checking in a Stateful Node Subtree.\n *\n * @param sNode Stateful Node.\n * @param updateFlags Update flags (ForceUpdate and DisplaceNode).\n */\nconst _dirtyCheck = (sNode: SNode, updateFlags: number): void => {\n  const ctx = RENDER_CONTEXT;\n  // polymorphic call-site\n  const state = sNode.s1;\n  const v = sNode.v;\n  const children = sNode.c;\n  const flags = sNode.f;\n  const type = flags & Flags.TypeMask;\n  sNode.f = type;\n  if (type === Flags.Template) {\n    const rootDOMNode = (state as Node[])[0] as Element;\n    if (updateFlags & Flags.DisplaceNode) {\n      updateFlags ^= Flags.DisplaceNode;\n      elementMoveBefore.call(ctx.p, rootDOMNode, ctx.n);\n    }\n    if (flags & Flags.DirtySubtree) {\n      ctx.p = rootDOMNode;\n      ctx.n = null;\n      const parentDOMElement = ctx.p;\n      const childOpCodes = (v as VTemplate).d.p1.c;\n      let childrenIndex = 0;\n      for (let i = 0; i < childOpCodes.length; i++) {\n        const op = childOpCodes[i];\n        const type = op & ChildOpCode.Type;\n        const value = op >> ChildOpCode.ValueShift;\n        if (type === ChildOpCode.Child) {\n          const sChild = (children as (SNode1 | null)[])[childrenIndex++];\n          if (sChild !== null) {\n            _dirtyCheck(sChild, updateFlags);\n          }\n        } else if (type === ChildOpCode.SetNext) {\n          ctx.n = (state as Node[])[value];\n        } else { // ChildOpCode.SetParent\n          ctx.p = state[value] as Element;\n          ctx.n = null;\n        }\n      }\n      ctx.p = parentDOMElement;\n    }\n    ctx.n = rootDOMNode;\n  } else if (type === Flags.Text) {\n    if (updateFlags & Flags.DisplaceNode) {\n      elementMoveBefore.call(ctx.p, state as Text, ctx.n);\n    }\n    ctx.n = state as Text;\n  } else if (type === Flags.Component) {\n    if ((flags | updateFlags) & (Flags.Dirty | Flags.ForceUpdate)) {\n      sNode.c = _update(\n        sNode,\n        children as SNode,\n        (state as ComponentRenderFn)!((v as VComponent).p),\n        updateFlags,\n      );\n    } else if (children !== null) {\n      _dirtyCheck(children as SNode, updateFlags);\n    }\n  } else if (type === Flags.Context) {\n    if (children !== null) {\n      _dirtyCheck(children as SNode, updateFlags);\n    }\n  } else { // Array || List\n    let i = (children as Array<SNode | null>).length;\n    while (--i >= 0) {\n      const sChild = (children as Array<SNode | null>)[i];\n      if (sChild !== null) {\n        _dirtyCheck(sChild, updateFlags);\n      }\n    }\n  }\n};\n\n/**\n * Unmounts Stateful Node.\n *\n * @param sNode Stateful Node.\n * @param detach Detach root DOM nodes from the DOM.\n */\nconst _unmount = (sNode: SNode, detach: boolean): void => {\n  const flags = sNode.f; // polymorphic call-site\n  const sChildren = sNode.c;\n\n  if (detach === true && (flags & (Flags.Template | Flags.Text))) {\n    detach = false;\n    nodeRemoveChild.call(\n      RENDER_CONTEXT.p,\n      (flags & Flags.Template)\n        ? (sNode as STemplate).s1[0]\n        : (sNode as SText).s1\n    );\n  }\n  if (flags & Flags.Component) {\n    const unmountHooks = (sNode as SComponent).s2;\n    if (unmountHooks !== null) {\n      if (typeof unmountHooks === \"function\") {\n        unmountHooks();\n      } else {\n        for (let i = 0; i < unmountHooks.length; i++) {\n          unmountHooks[i]();\n        }\n      }\n    }\n  }\n\n  if (sChildren !== null) {\n    if (_isArray(sChildren)) {\n      for (let i = 0; i < sChildren.length; i++) {\n        const sChild = sChildren[i];\n        if (sChild !== null) {\n          _unmount(sChild, detach);\n        }\n      }\n    } else {\n      _unmount(sChildren as SNode, detach);\n    }\n  }\n};\n\nconst enum MagicValues {\n  /**\n   * One of the children nodes were moved.\n   */\n  RearrangeNodes = 1073741823, // Max SMI Value\n  /**\n   * New node marker.\n   */\n  NewNodeMark = -1,\n  /**\n   * LIS marker.\n   */\n  LISMark = -2,\n}\n\n/**\n * Update children list with track by key algorithm.\n *\n * High-level overview of the algorithm that is implemented in this function:\n *\n * This algorithm finds a minimum number of DOM operations. It works in\n * several steps:\n *\n * 1. Common prefix and suffix optimization.\n *\n * Look for nodes with identical keys by simultaneously iterating through nodes\n * in the old children list `A` and new children list `B` from both sides.\n *\n *     A: -> [a b c d] <-\n *     B: -> [a b d] <-\n *\n * Skip nodes \"a\" and \"b\" at the start, and node \"d\" at the end.\n *\n *     A: -> [c] <-\n *     B: -> [] <-\n *\n * 2. Zero length optimizations.\n *\n * Check if the size of one of the list is equal to zero. When length of the\n * old children list is zero, insert remaining nodes from the new list. When\n * length of the new children list is zero, remove remaining nodes from the old\n * list.\n *\n *     A: -> [a b c g] <-\n *     B: -> [a g] <-\n *\n * Skip nodes \"a\" and \"g\" (prefix and suffix optimization).\n *\n *     A: [b c]\n *     B: []\n *\n * Remove nodes \"b\" and \"c\".\n *\n * 3. Index and unmount removed nodes.\n *\n *     A: [b c d e f]\n *     B: [c b h f e]\n *     P: [. . . . .] // . == -1\n *\n * Create array `P` (`sources`) with the length of the new children list and\n * fills it with `NewNodeMark` values. This mark indicates that node at this\n * position should be mounted. Later we will assign node positions in the old\n * children list to this array.\n *\n *     A: [b c d e f]\n *     B: [c b h f e]\n *     P: [. . . . .] // . == -1\n *     I: {\n *       c: 0, // B[0] == c\n *       b: 1, // B[1] == b\n *       h: 2,\n *       f: 3,\n *       e: 4,\n *     }\n *     last = 0\n *\n * Create reverse index `I` that maps keys to node positions in the new\n * children list.\n *\n *     A: [b c d e f]\n *         ^\n *     B: [c b h f e]\n *     P: [. 0 . . .] // . == -1\n *     I: {\n *       c: 0,\n *       b: 1, <-\n *       h: 2,\n *       f: 3,\n *       e: 4,\n *     }\n *     last = 1\n *\n * Assign original positions of the nodes from the old children list to the\n * array `P`.\n *\n * Iterate through nodes in the old children list and gets their new positions\n * from the index `I`. Assign old node position to the array `P`. When index\n * `I` doesn't have a key for the old node, it means that it should be\n * unmounted.\n *\n * When we assigning positions to the array `P`, we also store position of the\n * last seen node in the new children list `pos`, if the last seen position is\n * greater than the current position of the node at the new list, then we are\n * switching `rearrangeNodes` flag to `true` (`pos === RearrangeNodes`).\n *\n *     A: [b c d e f]\n *           ^\n *     B: [c b h f e]\n *     P: [1 0 . . .] // . == -1\n *     I: {\n *       c: 0, <-\n *       b: 1,\n *       h: 2,\n *       f: 3,\n *       e: 4,\n *     }\n *     last = 1 // last > 0; rearrangeNodes = true\n *\n * The last position `1` is greater than the current position of the node at the\n * new list `0`, switch `rearrangeNodes` flag to `true`.\n *\n *     A: [b c d e f]\n *             ^\n *     B: [c b h f e]\n *     P: [1 0 . . .] // . == -1\n *     I: {\n *       c: 0,\n *       b: 1,\n *       h: 2,\n *       f: 3,\n *       e: 4,\n *     }\n *     rearrangeNodes = true\n *\n * Node with key \"d\" doesn't exist in the index `I`, unmounts node `d`.\n *\n *     A: [b c d e f]\n *               ^\n *     B: [c b h f e]\n *     P: [1 0 . . 3] // . == -1\n *     I: {\n *       c: 0,\n *       b: 1,\n *       h: 2,\n *       f: 3,\n *       e: 4, <-\n *     }\n *     rearrangeNodes = true\n *\n * Assign position `3` for `e` node.\n *\n *     A: [b c d e f]\n *                 ^\n *     B: [c b h f e]\n *     P: [1 0 . 4 3] // . == -1\n *     I: {\n *       c: 0,\n *       b: 1,\n *       h: 2,\n *       f: 3, <-\n *       e: 4,\n *     }\n *     rearrangeNodes = true\n *\n * Assign position `4` for 'f' node.\n *\n * 4. Find minimum number of moves when `rearrangeNodes` flag is on and mount\n *    new nodes.\n *\n *     A: [b c d e f]\n *     B: [c b h f e]\n *     P: [1 * . 4 *] // . == -1  * == -2\n *\n * When `rearrangeNodes` is on, mark all nodes in the array `P` that belong to\n * the [longest increasing subsequence](http://en.wikipedia.org/wiki/Longest_increasing_subsequence)\n * and move all nodes that doesn't belong to this subsequence.\n *\n * Iterate over the new children list and the `P` array simultaneously. When\n * value from `P` array is equal to `NewNodeMark`, mount a new node. When it\n * isn't equal to `LisMark`, move it to a new position.\n *\n *     A: [b c d e f]\n *     B: [c b h f e]\n *                 ^  // new_pos == 4\n *     P: [1 * . 4 *] // . == NewNodeMark  * == LisMark\n *                 ^\n *\n * Node \"e\" has `LisMark` value in the array `P`, nothing changes.\n *\n *     A: [b c d e f]\n *     B: [c b h f e]\n *               ^    // new_pos == 3\n *     P: [1 * . 4 *] // . == NewNodeMark  * == LisMark\n *               ^\n *\n * Node \"f\" has `4` value in the array `P`, move it before the next node \"e\".\n *\n *     A: [b c d e f]\n *     B: [c b h f e]\n *             ^      // new_pos == 2\n *     P: [1 * . 4 *] // . == NewNodeMark  * == LisMark\n *             ^\n *\n * Node \"h\" has `NewNodeMark` value in the array `P`, mount new node \"h\".\n *\n *     A: [b c d e f]\n *     B: [c b h f e]\n *           ^        // new_pos == 1\n *     P: [1 * . 4 *] // . == NewNodeMark  * == LisMark\n *           ^\n *\n * Node \"b\" has `LisMark` value in the array `P`, nothing changes.\n *\n *     A: [b c d e f]\n *     B: [c b h f e]\n *         ^          // new_pos == 0\n *     P: [1 * . 4 *] // . == NewNodeMark  * == LisMark\n *\n * Node \"c\" has `1` value in the array `P`, move it before the next node \"b\".\n *\n * When `rearrangeNodes` flag is off, skip LIS algorithm and mount nodes that\n * have `NewNodeMark` value in the array `P`.\n *\n * NOTE: There are many variations of this algorithm that are used by many UI\n * libraries and many implementations are still using an old optimization\n * technique that were removed several years ago from this implementation. This\n * optimization were used to improve performance of simple moves/swaps. E.g.\n *\n *     A: -> [a b c] <-\n *     B: -> [c b a] <-\n *\n * Move \"a\" and \"c\" nodes to the other edge.\n *\n *     A: -> [b] <-\n *     B: -> [b] <-\n *\n * Skip node \"b\".\n *\n * This optimization were removed because it breaks invariant that insert and\n * remove operations shouldn't trigger a move operation. E.g.\n *\n *     A: -> [a b]\n *     B:    [c a] <-\n *\n * Move node \"a\" to the end.\n *\n *     A: [b]\n *     B: [c a]\n *\n * Remove node \"b\" and insert node \"c\".\n *\n * In this use case, this optimization performs one unnecessary operation.\n * Instead of removing node \"b\" and inserting node \"c\", it also moves node \"a\".\n *\n * @param sNode {@link SList} node.\n * @param a Previous {@link ListProps}.\n * @param b Next {@link ListProps}.\n * @param updateFlags Update flags.\n * @noinline\n * @__NOINLINE__\n */\nconst _updateList = (\n  sNode: SList,\n  a: ListProps<any>,\n  b: ListProps<any>,\n  updateFlags: Flags,\n): void => {\n  const aKeys = a.k;\n  const bKeys = b.k;\n  const bVNodes = b.v;\n  let bLength = bKeys.length;\n  let aLength = aKeys.length;\n  const result = _Array(bLength);\n\n  if (bLength === 0) { // New children list is empty.\n    if (aLength > 0) { // Unmount nodes from the old children list.\n      _unmount(sNode, true);\n    }\n  } else if (aLength === 0) { // Old children list is empty.\n    while (bLength > 0) { // Mount nodes from the new children list.\n      result[--bLength] = _mount(sNode, bVNodes[bLength]);\n    }\n  } else {\n    const sChildren = sNode.c as Array<SNode | null>;\n    let aEnd = aLength - 1;\n    let bEnd = bLength - 1;\n    let start = 0;\n\n    // Step 1\n    outer: while (true) {\n      // Update nodes with the same key at the end.\n      while (aKeys[aEnd] === bKeys[bEnd]) {\n        result[bEnd] = _update(\n          sNode,\n          sChildren[aEnd--],\n          bVNodes[bEnd],\n          updateFlags,\n        );\n        if (start > --bEnd || start > aEnd) {\n          break outer;\n        }\n      }\n\n      // Update nodes with the same key at the beginning.\n      while (aKeys[start] === bKeys[start] && ++start <= aEnd && start <= bEnd) {\n        // delayed update (all updates should be performed from right-to-left).\n      }\n\n      break;\n    }\n\n    // Step 2\n    if (start > aEnd) {\n      // All nodes from `a` are updated, insert the rest from `b`.\n      while (bEnd >= start) {\n        result[bEnd] = _mount(sNode, bVNodes[bEnd--]);\n      }\n    } else if (start > bEnd) {\n      // All nodes from `b` are updated, remove the rest from `a`.\n      bLength = start;\n      do {\n        const sChild = sChildren[bLength++];\n        if (sChild !== null) {\n          _unmount(sChild, true);\n        }\n      } while (bLength <= aEnd);\n    } else { // Step 3\n      let bLength = bEnd - start + 1;\n      const sources = new _Int32Array(bLength); // Maps positions in the new children list to positions in the old list.\n      const keyIndex = new _Map<any, number>(); // Maps keys to their positions in the new children list.\n      for (let i = 0; i < bLength; i++) {\n        // `NewNodeMark` value indicates that node doesn't exist in the old children list.\n        sources[i] = MagicValues.NewNodeMark;\n        const j = start + i;\n        keyIndex.set(bKeys[j], j);\n      }\n\n      // When `nodePosition === RearrangeNodes`, it means that one of the nodes is in the wrong position and we should\n      // rearrange nodes with LIS-based algorithm `markLIS()`.\n      let nodePosition = 0;\n      for (let i = start; i <= aEnd; i++) {\n        const sChild = sChildren[i];\n        const nextPosition = keyIndex.get(aKeys[i]);\n        if (nextPosition !== void 0) {\n          nodePosition = (nodePosition < nextPosition)\n            ? nextPosition\n            : MagicValues.RearrangeNodes;\n          sources[nextPosition - start] = i;\n          result[nextPosition] = sChild;\n        } else if (sChild !== null) {\n          _unmount(sChild, true);\n        }\n      }\n\n      // Step 4\n\n      // Mark LIS nodes only when this node weren't moved `moveNode === false` and we've detected that one of the\n      // children nodes were moved `pos === MagicValues.MovedChildren`.\n      if (!(updateFlags & Flags.DisplaceNode) && nodePosition === MagicValues.RearrangeNodes) {\n        markLIS(sources);\n      }\n      while (bLength-- > 0) {\n        bEnd = bLength + start;\n        const node = bVNodes[bEnd];\n        const lisValue = sources[bLength];\n        result[bEnd] = (lisValue === -1)\n          ? _mount(sNode, node)\n          : _update(\n            sNode,\n            result[bEnd],\n            node,\n            updateFlags |\n            ((nodePosition === MagicValues.RearrangeNodes && lisValue !== MagicValues.LISMark)\n              ? Flags.DisplaceNode\n              : 0),\n          );\n      }\n    }\n\n    // Delayed update for nodes from Step 1 (prefix only). Reconciliation algorithm always updates nodes from right to\n    // left.\n    while (start > 0) {\n      result[--start] = _update(\n        sNode,\n        sChildren[start],\n        bVNodes[start],\n        updateFlags,\n      );\n    }\n  }\n  sNode.c = result;\n};\n\n/**\n * Modified Longest Increased Subsequence algorithm.\n *\n * Mutates input array `a` and replaces all values that are part of LIS with -2 value.\n *\n * Constraints:\n * - Doesn't work with negative numbers. -1 values are ignored.\n * - Input array `a` should contain at least one value that is greater than -1.\n *\n * {@link http://en.wikipedia.org/wiki/Longest_increasing_subsequence}\n *\n * @example\n *\n *     const A = Int32Array.from([-1, 0, 2, 1]);\n *     markLIS(A);\n *     // A => [-1, -2, 2, -2]\n *\n * @param a Array of numbers.\n * @noinline\n * @__NOINLINE__\n */\nconst markLIS = (a: Int32Array): void => {\n  const length = a.length;\n  const parent = new _Int32Array(length);\n  const index = new _Int32Array(length);\n  let indexLength = 0;\n  let i = 0;\n  let j: number;\n  let k: number;\n  let lo: number;\n  let hi: number;\n\n  // Skip -1 values at the start of the input array `a`.\n  for (; a[i] === MagicValues.NewNodeMark; i++) { /**/ }\n\n  index[0] = i++;\n  for (; i < length; i++) {\n    k = a[i];\n    if (k !== MagicValues.NewNodeMark) { // Ignore -1 values.\n      j = index[indexLength];\n      if (a[j] < k) {\n        parent[i] = j;\n        index[++indexLength] = i;\n      } else {\n        lo = 0;\n        hi = indexLength;\n\n        while (lo < hi) {\n          j = (lo + hi) >> 1;\n          if (a[index[j]] < k) {\n            lo = j + 1;\n          } else {\n            hi = j;\n          }\n        }\n\n        if (k < a[index[lo]]) {\n          if (lo > 0) {\n            parent[i] = index[lo - 1];\n          }\n          index[lo] = i;\n        }\n      }\n    }\n  };\n\n  // Mutate input array `a` and assign -2 value to all nodes that are part of LIS.\n  j = index[indexLength];\n  while (indexLength-- >= 0) {\n    a[j] = MagicValues.LISMark;\n    j = parent[j];\n  }\n};\n\n/**\n * Creates a HTML Template cloning factory.\n * \n * @__NO_SIDE_EFFECTS__\n */\nexport const _hN = (t: string | Node): () => Element => (\n  () => {\n    if (typeof t === \"string\") {\n      HTM_TEMPLATE.innerHTML = t;\n      t = HTM_TEMPLATE_CONTENT.firstChild!;\n    }\n    return nodeCloneNode.call(t, true) as Element;\n  }\n);\n\n/**\n * Creates a HTML Element factory.\n * \n * @__NO_SIDE_EFFECTS__\n */\nexport const _hE = (t: string): () => Element => (\n  () => doc.createElement(t)\n);\n\n/**\n * Creates a SVG Template cloning factory.\n */\nexport const _sN = (t: string | Node): () => Element => (\n  () => {\n    if (typeof t === \"string\") {\n      SVG_TEMPLATE.innerHTML = t;\n      t = SVG_TEMPLATE_CONTENT.firstChild!;\n    }\n    return nodeCloneNode.call(t, true) as Element;\n  }\n);\n\n/**\n * Creates a SVG Element factory.\n * \n * @__NO_SIDE_EFFECTS__\n */\nexport const _sE = (t: string): () => Element => (\n  () => doc.createElementNS(\"http://www.w3.org/2000/svg\", t)\n);\n\n/**\n * Creates a template descriptor with globally shared data.\n * \n * @__NO_SIDE_EFFECTS__\n */\nexport const _T = (\n  p2: () => Element,\n  f: number,\n  p: PropOpCode[],\n  c: ChildOpCode[],\n  s: StateOpCode[],\n  d = __IVI_STRINGS__,\n): TemplateDescriptor => ({\n  f: Flags.Template,\n  p1: { f, p, c, s, d },\n  p2,\n});\n\n/**\n * @__NO_SIDE_EFFECTS__\n */\nexport const _t = (d: TemplateDescriptor, p: any[]): VTemplate => ({ d, p });\n\nexport type ComponentFactory = {\n  (\n    factory: (c: Component) => () => VAny,\n    areEqual?: (a?: any, b?: any) => boolean\n  ): () => VComponent<undefined>;\n  <P>(\n    factory: (c: Component<P>) => (props: P) => VAny,\n    areEqual?: (prev: P, next: P) => boolean\n  ): (props: P) => VComponent<P>;\n};\n\n/**\n * Creates a factory that produces component nodes.\n *\n * @typeparam P Property type.\n * @param factory Function that produces stateful render functions.\n * @param areEqyal Function that checks `props` for equality.\n * @returns Factory that produces component nodes.\n * @__NO_SIDE_EFFECTS__\n */\nexport const component: ComponentFactory = <P>(\n  p1: (c: Component) => (props?: P) => VAny,\n  p2?: (prev: P, next: P) => boolean,\n): (p?: any) => VComponent => {\n  const d: ComponentDescriptor = { f: Flags.Component, p1, p2 };\n  return (p: P) => ({ d, p });\n};\n\n/**\n * Gets current component props.\n *\n * @typeparam P Property type.\n * @param component Component node.\n * @returns Current component props.\n */\nexport const getProps = <P>(component: Component<P>): P => (\n  component.v.p\n);\n\n/**\n * Adds an unmount hook.\n *\n * @example\n *\n *     const Example = component((c) => {\n *       useUnmount(c, () => { console.log(\"unmounted\"); });\n *\n *       return () => null;\n *     });\n *\n * @param component Component instance.\n * @param hook Unmount hook.\n */\nexport const useUnmount = (component: Component, hook: () => void): void => {\n  const hooks = component.s2;\n  component.s2 = (hooks === null)\n    ? hook\n    : (typeof hooks === \"function\")\n      ? [hooks, hook]\n      : (hooks.push(hook), hooks);\n};\n\n\nexport type Effect = {\n  (\n    component: Component,\n    effect: () => (() => void) | void,\n    areEqual?: (prev?: any, next?: any) => boolean\n  ): () => void;\n  <P>(\n    component: Component,\n    effect: (props: P) => (() => void) | void,\n    areEqual?: (prev: P, next: P) => boolean\n  ): (props: P) => void;\n};\n\n/**\n * Creates a side effect hook.\n *\n * @example\n *\n *     const Example = component((c) => {\n *       const [count, setCount] = useState(c, 0);\n *       const timer = useEffect(c, ({ interval }) => {\n *         const tid = setInterval(() => { setCount(count() + 1); }, interval);\n *         return () => { clearInterval(tid); };\n *       }, shallowEq);\n *\n *       return (interval) => (\n *         timer({ interval }),\n *\n *         html`<span>${count()}</span>`\n *       );\n *     });\n *\n * @typeparam T Hook props type.\n * @param component Component instance.\n * @param hook Side effect function.\n * @param areEqual Function that checks if input value hasn't changed.\n * @returns Side effect hook.\n */\nexport const useEffect: Effect = <P>(\n  component: Component,\n  hook: (props?: P) => (() => void) | void,\n  areEqual?: (prev: P, next: P) => boolean,\n): (props?: P) => void => {\n  // var usage is intentional, see `docs/internals/perf.md` for an explanation.\n  var reset: (() => void) | void;\n  var prev: P | undefined;\n  var pending: boolean | undefined;\n  return (next?: P) => {\n    if (\n      pending !== true && (\n        areEqual === void 0 ||\n        prev === void 0 ||\n        areEqual(prev as P, next as P) === false\n      )\n    ) {\n      if (pending === void 0) {\n        useUnmount(component, () => {\n          pending = false;\n          if (reset !== void 0) {\n            reset();\n          }\n        });\n      }\n      pending = true;\n      RENDER_CONTEXT.e.push(() => {\n        if (pending === true) {\n          pending = false;\n          if (reset !== void 0) {\n            reset();\n          }\n          reset = hook(next!);\n        }\n      });\n    }\n    prev = next;\n  };\n};\n\nlet _animationFrameEffects: (() => void)[] = [];\nlet _idleEffects: (() => void)[] = [];\n\nconst _flushAnimationFrameEffects = () => {\n  while (_animationFrameEffects.length > 0) {\n    const e = _animationFrameEffects;\n    _animationFrameEffects = [];\n    for (let i = 0; i < e.length; i++) {\n      e[i]();\n    }\n  }\n};\n\nconst _flushIdleEffects = () => {\n  while (_idleEffects.length > 0) {\n    const e = _idleEffects;\n    _idleEffects = [];\n    for (let i = 0; i < e.length; i++) {\n      e[i]();\n    }\n  }\n};\n\n/* @__NO_SIDE_EFFECTS__ */\nexport const createEffectHandler = (scheduleFlushTask: () => Array<() => void>) => <P>(\n  component: Component,\n  hook: (props?: P) => (() => void) | void,\n  areEqual?: (prev: P, next: P) => boolean,\n): (props?: P) => void => {\n  // var usage is intentional, see `docs/internals/perf.md` for an explanation.\n  var reset: (() => void) | void;\n  var prev: P | undefined;\n  var pending: boolean | undefined;\n  return (next?: P) => {\n    if (\n      pending !== true && (\n        areEqual === void 0 ||\n        prev === void 0 ||\n        areEqual(prev as P, next as P) === false\n      )\n    ) {\n      if (pending === void 0) {\n        useUnmount(component, () => {\n          pending = false;\n          if (reset !== void 0) {\n            reset();\n          }\n        });\n      }\n      pending = true;\n      scheduleFlushTask().push(() => {\n        if (pending === true) {\n          pending = false;\n          if (reset !== void 0) {\n            reset();\n          }\n          reset = hook(next!);\n        }\n      });\n    }\n    prev = next;\n  };\n};\n\nconst _scheduleAnimationFrameEffects = () => {\n  const queue = _animationFrameEffects;\n  if (queue.length === 0) {\n    _requestAnimationFrame(_flushAnimationFrameEffects);\n  }\n  return queue;\n};\nexport const useAnimationFrameEffect = createEffectHandler(_scheduleAnimationFrameEffects);\n\nconst _scheduleIdleEffects = () => {\n  const queue = _idleEffects;\n  if (queue.length === 0) {\n    _requestIdleCallback(_flushIdleEffects);\n  }\n  return queue;\n};\nexport const useIdleEffect = createEffectHandler(_scheduleIdleEffects);\n\n/**\n * Invalidates a component.\n *\n * @param c Component instance.\n */\nexport const invalidate = (c: Component): void => {\n  if (!(c.f & Flags.Dirty)) {\n    c.f |= Flags.Dirty;\n    let prev: SNode = c;\n    let parent = c.p;\n    while (parent !== null) {\n      // Polymorphic call-sites\n      if (parent.f & Flags.DirtySubtree) {\n        return;\n      }\n      prev = parent;\n      parent.f |= Flags.DirtySubtree;\n      parent = parent.p;\n    }\n    (prev as SRoot).v.d.p1(prev as SRoot, prev.s1);\n  }\n};\n\n/**\n * VDescriptor for List nodes.\n */\nexport const LIST_DESCRIPTOR: ListDescriptor = {\n  f: Flags.List,\n  p1: null,\n  p2: null,\n};\n\n/**\n * Creates a dynamic list.\n *\n * @typeparam E Entry type.\n * @typeparam K Key type.\n * @param entries Entries.\n * @param getKey Get key from entry function.\n * @param render Render entry function.\n * @returns Dynamic list.\n * @__NO_SIDE_EFFECTS__\n */\nexport const List = <E, K>(\n  entries: E[],\n  getKey: (entry: E, index: number) => K,\n  render: (entry: E, index: number) => VAny,\n): VList => ({\n  d: LIST_DESCRIPTOR,\n  p: {\n    k: entries.map(getKey),\n    v: entries.map(render),\n  },\n});\n\n/**\n * Performs dirty checking in a root subtree.\n *\n * When `forceUpdate` option is enabled, all components in a root subtree will\n * be updated.\n *\n * @param root Root Node.\n * @param forceUpdate Force update components.\n */\nexport const dirtyCheck = (root: Root, forceUpdate?: boolean): void => {\n  _dirtyCheckRoot(\n    root,\n    forceUpdate === true\n      ? Flags.ForceUpdate\n      : 0,\n  );\n};\n\n/**\n * Performs a Dirty Checking in a root subtree.\n *\n * @param root Stateful Root Node.\n * @param updateFlags Update flags (ForceUpdate and DisplaceNode).\n */\nconst _dirtyCheckRoot = (root: SRoot, updateFlags: number): void => {\n  while ((updateFlags | root.f) & (Flags.DirtySubtree | Flags.ForceUpdate)) {\n    const ctx = RENDER_CONTEXT;\n    const { p, n } = ctx;\n    root.f = Flags.Root;\n    if (root.c !== null) {\n      const domSlot = root.v.p;\n      RENDER_CONTEXT.p = domSlot.p;\n      RENDER_CONTEXT.n = domSlot.n;\n      _dirtyCheck(root.c as SNode, updateFlags);\n      updateFlags = 0;\n      _flushDOMEffects();\n    }\n    ctx.p = p;\n    ctx.n = n;\n  }\n};\n\n/**\n * Updates a root subtree.\n *\n * @param root Stateful Root Node.\n * @param next New Stateless Node.\n * @param updateFlags Update flags (ForceUpdate and DisplaceNode).\n */\nconst _updateRoot = (root: SRoot, next: VAny, updateFlags: number): void => {\n  const ctx = RENDER_CONTEXT;\n  const { p, n } = ctx;\n  const domSlot = root.v.p;\n  ctx.p = domSlot.p;\n  ctx.n = domSlot.n;\n  root.f = Flags.Root;\n  root.c = _update(\n    root,\n    root.c as SNode,\n    next,\n    updateFlags,\n  );\n  _flushDOMEffects();\n  ctx.p = p;\n  ctx.n = n;\n  _dirtyCheckRoot(root, 0);\n};\n\n\n/**\n * Unmounts a root subtree.\n *\n * When `detach` option is enabled, root DOM nodes will be detached from the\n * DOM.\n *\n * @param root Root Node.\n * @param detach Detach root DOM nodes from the DOM.\n */\nexport const unmount = (root: Root, detach: boolean): void => {\n  if (root.c !== null) {\n    const ctx = RENDER_CONTEXT;\n    const { p, n } = ctx;\n    ctx.p = root.v.p.p;\n    root.f = Flags.Root;\n    _unmount(root.c as SNode, detach);\n    ctx.p = p;\n    ctx.n = n;\n  }\n};\n\nexport type RootFactory = {\n  (\n    onInvalidate: (root: Root<undefined>) => void\n  ): (parentElement: Element, nextNode?: Node | null) => Root<undefined>;\n  <S>(\n    onInvalidate: (root: Root<S>, state: S) => void,\n  ): (parentElement: Element, nextNode: Node | null, state: S) => Root<S>;\n};\n\n/**\n * Defines a root node with a custom invalidation hook.\n *\n * @param onInvalidate Invalidated Hook.\n * @returns Root Node factory.\n * @__NO_SIDE_EFFECTS__\n */\nexport const defineRoot: RootFactory = (\n  p1: (root: SRoot<any>, state?: any) => void,\n): (p: Element, n?: Node | null, s?: any) => SRoot<any> => {\n  var d: RootDescriptor = { f: Flags.Root, p1, p2: null };\n  return (p: Element, n: Node | null = null, s: any) => createSNode<VRoot, any>(\n    Flags.Root,\n    // VNode object.\n    {\n      // Root Descriptor.\n      d,\n      // VNode props object contains the location in the DOM tree where subtree\n      // should be rendered.\n      p: {\n        // Parent DOM Element.\n        p,\n        // Next DOM Node.\n        n,\n      },\n    },\n    // Children.\n    null,\n    // Parent SNode.\n    null,\n    // Root state.\n    s,\n  );\n};\n\n/**\n * Creates a root node that uses microtask queue for scheduling updates.\n *\n * @param parentElement Parent DOM Element.\n * @param nextNode Next DOM Node.\n * @returns Root Node.\n */\nexport const createRoot = /*@__PURE__*/defineRoot(\n  // OnRootInvalidated hook\n  (root: Root) => {\n    // Schedules a microtask for dirty checking.\n    _queueMicrotask(() => {\n      _dirtyCheckRoot(root, 0);\n    });\n  }\n);\n\n/**\n * Updates a root subtree.\n *\n * When `forceUpdate` option is enabled, all components in a root subtree will\n * be updated.\n *\n * @param root Root Node.\n * @param v Stateless View Node.\n * @param forceUpdate Force update components.\n */\nexport const update = (root: Root, v: VAny, forceUpdate?: boolean): void => {\n  _updateRoot(\n    root,\n    v,\n    forceUpdate === true\n      ? Flags.ForceUpdate\n      : 0,\n  );\n};\n\n/**\n * Context.\n * \n * @returns Context getter and context provider.\n * @__NO_SIDE_EFFECTS__\n */\nexport const context = <T>(): [\n  get: (component: Component) => T | undefined,\n  provider: (value: T, child: VAny) => VContext<T>,\n] => {\n  const d: ContextDescriptor = { f: Flags.Context, p1: null, p2: null };\n  return [\n    (c: Component) => _getContextValue(c, d),\n    (v: T, c: VAny) => ({ d, p: { v, c } }),\n  ];\n};\n\nconst _getContextValue = <T>(\n  c: Component,\n  d: ContextDescriptor,\n): T | undefined => {\n  let node: SNode | null = c.p;\n  while (node !== null) {\n    if (node.f & Flags.Context && (node as SContext).v.d === d) {\n      return (node as SContext).v.p.v;\n    }\n    node = node.p;\n  }\n};\n"
  },
  {
    "path": "packages/ivi/src/lib/equal.ts",
    "content": "/**\n * Prevents triggering updates.\n */\nexport const preventUpdates = (a: any, b: any) => true;\n\n/**\n * Checks if values are equal with a strict equality operator `===`.\n *\n * @param a\n * @param b\n * @returns True when values are strictly equal.\n */\nexport const strictEq = <T>(a: T, b: T): boolean => a === b;\n\nconst objectKeys = Object.keys;\n\n/**\n * Checks if objects are shallow equal.\n *\n * shallowEq algorithm is using strict equality operator `===` to\n * compare object values.\n *\n * @param a\n * @param b\n * @returns True when objects are shallow equal.\n */\nexport const shallowEq = <T extends object>(a: T, b: T): boolean => {\n  if (a !== b) {\n    const aKeys = objectKeys(a);\n    const bKeys = objectKeys(b);\n\n    if (aKeys.length !== bKeys.length) {\n      return false;\n    }\n\n    for (let i = 0; i < aKeys.length; ++i) {\n      const key = aKeys[i];\n      if ((a as any)[key] !== (b as any)[key]) {\n        return false;\n      }\n    }\n  }\n\n  return true;\n};\n\n/**\n * Checks if arrays are shallow equal.\n *\n * shallowEqArray algorithm is using strict equality operator `===` to\n * compare array values.\n *\n * @param a\n * @param b\n * @returns True whan arrays are shallow equal.\n */\nexport const shallowEqArray = <T>(a: T[], b: T[]): boolean => {\n  if (a !== b) {\n    if (a.length !== b.length) {\n      return false;\n    }\n\n    for (let i = 0; i < a.length; ++i) {\n      if (a[i] !== b[i]) {\n        return false;\n      }\n    }\n  }\n\n  return true;\n};\n"
  },
  {
    "path": "packages/ivi/src/lib/state.ts",
    "content": "import type { Component } from \"./core.js\";\nimport { invalidate } from \"./core.js\";\n\n/**\n * Creates a memoized function.\n *\n * @example\n *\n *     const Example = component((c) => {\n *       const fullName = useMemo(shallowEqArray, ([firstName, lastName]) => (\n *         `${firstName} ${lastName}`\n *       ));\n *\n *       return ({firstName, lastName}) => html`\n *         div.fullName ${fullName([firstName, lastName])}\n *       `;\n *     });\n *\n * @typeparam T Input type.\n * @typeparam U Output type.\n * @param areEqual Function that checks if input value hasn't changed.\n * @param fn Function to memoize.\n * @returns Memoized function.\n */\nexport const useMemo = <T, U>(\n  areEqual: (prev: T, next: T) => boolean,\n  fn: (props: T) => U,\n): (props: T) => U => {\n  var prev: T | undefined;\n  var v: U | undefined;\n  return (props: T) => (\n    (v === void 0 || areEqual(prev!, props) === false)\n      ? v = fn(prev = props)\n      : v\n  );\n};\n\n/**\n * Creates a reactive state.\n *\n * @example\n *\n *     const Example = component((c) => {\n *       const [getCounter, setCounter] = useState(c, 0);\n *       const inc = () => { setCounter(getCounter() + 1); };\n *\n *       return () => html`\n *         div.app\n *           div.counter ${getCounter()}\n *           button @click=${inc} 'Increment'\n *       `;\n *     });\n *\n * @typeparam S State type.\n * @param component Component instance.\n * @param state Initial state value.\n * @returns A tuple with a getter and setter functions.\n */\nexport const useState = <S>(\n  component: Component,\n  state: S,\n): [\n    get: () => S,\n    set: (s: S) => void,\n  ] => ([\n    // getter\n    () => state,\n    // setter\n    (next: S) => {\n      if (next !== state) {\n        state = next;\n        invalidate(component);\n      }\n    }\n  ]);\n\n/**\n * Reducer Dispatch Function.\n *\n * @typeparam A Action Type.\n */\nexport type Dispatch<A> = (action: A) => void;\n\n/**\n * Creates a reactive state reducer.\n *\n * @example\n *\n *     function reducer(state, action) {\n *       switch (action.type) {\n *         case \"inc\":\n *           return state + 1;\n *       }\n *       return state;\n *     }\n *\n *     const Example = component((c) => {\n *        const [counter, dispatch] = useReducer(c, 0, reducer);\n *        const inc = () => { dispatch(\"inc\"); };\n *\n *       return () => html`\n *         div.app\n *           div.counter ${counter()}\n *           button @click=${inc} 'Increment'\n *       `;\n *     });\n *\n * @typeparam S State type.\n * @typeparam A Reducer action type.\n * @param component Component instance.\n * @param state Initial state.\n * @param reducer Reducer function.\n * @returns State getter and dispatch functions.\n */\nexport const useReducer = <S, A>(\n  component: Component,\n  state: S,\n  reducer: (state: S, action: A) => S,\n): [\n    get: () => S,\n    dispatch: Dispatch<A>,\n  ] => ([\n    () => state,\n    (action: A) => {\n      const nextState = reducer(state, action);\n      if (state !== nextState) {\n        state = nextState;\n        invalidate(component);\n      }\n      return state;\n    }\n  ]);\n"
  },
  {
    "path": "packages/ivi/src/lib/template.ts",
    "content": "/** Template Data. */\nexport interface TemplateData {\n  /**\n   * SMI (Small Integer) value that packs several values:\n   *\n   *     struct Data {\n   *       stateSize:10;    // The number of state slots\n   *       childrenSize:10; // The number of children slots\n   *       svg:1;           // Template with SVG elements\n   *     }\n   *\n   * stateSize and childrenSize are used for preallocating arrays with\n   * exact number to avoid dynamic growth and reduce memory consumption.\n   */\n  f: number,\n  /**\n   * Array of SMI values that stores OpCodes for updating element properties.\n   */\n  p: PropOpCode[],\n  /**\n   * Array of SMI values that stores OpCodes for updating children nodes.\n   */\n  c: ChildOpCode[],\n  /**\n   * Array of SMI values that stores opCodes for traversing DOM nodes and\n   * saving references to DOM nodes into internal state when template is\n   * instantiated.\n   */\n  s: StateOpCode[],\n  /** Array of string values that stores keys for dynamic properties. */\n  d: any[],\n}\n\n/**\n * Template flags.\n *\n * State size and children size are used to preallocate arrays when template is\n * instantiated.\n *\n *     TemplateFlags {\n *       stateSize:6,    // The number of state slots\n *       childrenSize:6, // The number of children slots\n *       svg:1,\n *     }\n *\n *     stateSize = flags & Mask10;\n *     childrenSize = (flags >> ChildrenSizeShift) & Mask10;\n *     svg = flags & Svg;\n */\nexport const enum TemplateFlags {\n  ChildrenSizeShift = 6,\n  /** Template contains SVG elements */\n  Svg = 1 << 12,\n  Mask6 = (1 << 6) - 1,\n}\n\n/**\n * Template state opcodes.\n *\n *     OpCode {\n *       flags:3,\n *       offset:..,   // Assigned to Enter opCodes\n *     }\n *\n * To disambiguate between Enter and Remove operations we can check for the\n * presence of offset value. Remove OpCode shouldn't have any offsets.\n *\n * Enter operation moves cursor inside of the current node. Offset value is\n * used to determine which operations should be applied to the current node.\n *\n *     offset = `op >> OffsetShift`\n *\n * Remove operation removes a marker `<!>` that separates two text nodes.\n *\n * Remove operation implies that next node is a text node and it should be\n * saved.\n */\nexport const enum StateOpCode {\n  /** Saves current node */\n  Save = 0b01,\n  /** Enter or Remove operation. */\n  EnterOrRemove = 0b10,\n  OffsetShift = 2,\n}\n\n/**\n * Commonly used properties.\n */\nexport const enum CommonPropType {\n  ClassName = 0,\n  TextContent = 1,\n  InnerHTML = 2,\n}\n\n/**\n * Template property opcodes.\n *\n *     PropOpCode(SetNode) {\n *       type:3,   // SetNode\n *       index:6,  // State index\n *     }\n *     PropOpCode(Common) {\n *       type:3,   // Common\n *       input:6,  // Expr index\n *       data:..,   // Common Property Type\n *     }\n *     PropOpCode(..) {\n *       type:3,\n *       input:6,  // Expr index\n *       data:..,  // Data Index\n *     }\n */\nexport const enum PropOpCode {\n  /** Sets current node. */\n  SetNode = 0,\n  /** Commonly used properties to reduce data size. */\n  Common = 1,\n  /** Updates an attribute. */\n  Attribute = 2,\n  /** Updates a property. */\n  Property = 3,\n  /** Use DOM as a source of truth when diffing property value. */\n  DiffDOMProperty = 4,\n  /** Updates a style. */\n  Style = 5,\n  /** Updates an event. */\n  Event = 6,\n  /** Executes a directive. */\n  Directive = 7,\n  /** propType = `op & PropTypeMask` */\n  TypeMask = 0b111,\n  /** inputIndex = `(op >> InputShift) & Mask6` */\n  InputShift = 3,\n  /** dataIndex = `op >> DataShift` */\n  DataShift = 9,\n  /** Masks 10 lowest bits. */\n  Mask6 = (1 << 6) - 1,\n}\n\n/**\n * Template child opcodes.\n *\n *     ChildOpCode {\n *       type:2,   // Child | SetNext | SetParent\n *       value:10,\n *     }\n */\nexport const enum ChildOpCode {\n  Child = 0b00,\n  SetNext = 0b01,\n  SetParent = 0b11,\n  Type = 0b11,\n  ValueShift = 2,\n}\n"
  },
  {
    "path": "packages/ivi/src/lib/utils.ts",
    "content": "import {\n  type SNode, type Component, type STemplate, type SText,\n  Flags\n} from \"./core.js\";\n\n/**\n * Dispatch Event options.\n */\nexport interface DispatchEventOptions {\n  /**\n   * Option indicating whether the event bubbles. The default is `true`.\n   */\n  bubbles?: boolean;\n  /**\n   * Option indicating whether the event can be cancelled. The default is\n   * `false`.\n   */\n  cancelable?: boolean;\n  /**\n   * Option indicating whether the event will trigger listeners outside of a\n   * shadow root. The default is `false`.\n   */\n  composed?: boolean;\n}\n\nexport type EventDispatcher = {\n  (component: Component): boolean;\n  <T>(component: Component, value: T): boolean;\n};\n\n/**\n * Creates an event dispatcher that finds the closest child DOM node and emits\n * a CustomEvent with `EventTarget.dispatchEvent()` method.\n *\n * Event dispatcher invokes event handlers synchronously. All event handlers are\n * invoked before event dispatcher returns.\n *\n * @typeparam T Data type.\n * @param eventType Event type.\n * @param options {@link DispatchEventOptions}.\n * @returns `false` if event is cancelable, and at least one of the event\n *   handlers which received event called `Event.preventDefault()`. Otherwise\n *   `true`.\n * @__NO_SIDE_EFFECTS__\n */\nexport const eventDispatcher = <T>(\n  eventType: string,\n  options?: DispatchEventOptions,\n): EventDispatcher => (component: Component, detail?: T) => (\n  findDOMNode(component)!.dispatchEvent(\n    new _CustomEvent(eventType,\n      {\n        bubbles: true,\n        detail,\n        ...options,\n      },\n    ),\n  )\n);\n\nconst _CustomEvent = CustomEvent;\n\n/**\n * Finds the closest DOM node from a Stateful Tree {@link SNode}.\n *\n * @typeparam T DOM node type.\n * @param sNode Stateful Tree {@link SNode}.\n * @returns DOM node.\n */\nexport const findDOMNode = <T extends Node | Text>(\n  sNode: SNode | null,\n): T | null => {\n  if (sNode === null) {\n    return null;\n  }\n\n  let flags: number = sNode.f; // polymorphic call-site\n  if (flags & (Flags.Template | Flags.Text)) {\n    return (flags & Flags.Template)\n      ? (sNode as STemplate).s1[0] as T\n      : (sNode as SText).s1 as T;\n  }\n  const children = sNode.c;\n  if (flags & (Flags.Array | Flags.List)) {\n    for (let i = 0; i < (children as Array<SNode | null>).length; i++) {\n      const c = findDOMNode((children as Array<SNode | null>)[i]);\n      if (c !== null) {\n        return c as T;\n      }\n    }\n    return null;\n  }\n  return findDOMNode(children as SNode | null);\n};\n\n/**\n * VisitNodesDirective controls the {@link visitNodes} traversal algorithm.\n */\nexport const enum VisitNodesDirective {\n  /** Continue traversing the tree. */\n  Continue = 0,\n  /** Stops immediately. */\n  StopImmediate = 1,\n  /**\n   * Stops traversing through children nodes and continues trversing through\n   * adjacent nodes.\n   */\n  Stop = 1 << 1,\n}\n\n/**\n * Traverses stateful tree and invokes `visitor` function on each {@link SNode}.\n *\n * @param sNode {@link SNode}.\n * @param visitor Visitor function.\n * @returns {@link VisitNodesDirective}\n */\nexport const visitNodes = (\n  sNode: SNode,\n  visitor: (opState: SNode) => VisitNodesDirective,\n): VisitNodesDirective => {\n  let i = visitor(sNode);\n  if (i !== VisitNodesDirective.Continue) {\n    return (i & VisitNodesDirective.StopImmediate);\n  }\n\n  const { f, c } = sNode; // polymorphic call-site\n  if (f & (Flags.Array | Flags.List)) {\n    for (i = 0; i < (c as Array<SNode | null>).length; i++) {\n      if (\n        (sNode = (c as Array<SNode | null>)[i]!) !== null &&\n        (visitNodes(sNode, visitor) & VisitNodesDirective.StopImmediate)\n      ) {\n        return VisitNodesDirective.StopImmediate;\n      }\n    }\n  } else if (c !== null) {\n    return visitNodes(c as SNode, visitor);\n  }\n  return VisitNodesDirective.Continue;\n};\n\n/**\n * Checks if a Stateful Tree {@link SNode} contains a DOM element.\n *\n * @param node Stateful Tree {@link SNode}.\n * @param element DOM element.\n * @returns True when parent contains an element.\n */\nexport const containsDOMElement = (\n  node: SNode,\n  element: Element,\n): boolean => {\n  var result = false;\n  visitNodes(node, (sNode) => (\n    (sNode.f & Flags.Template) // polymorphic call-site\n      ? ((((sNode as STemplate).s1[0] as Element).contains(element) === true)\n        ? (result = true, VisitNodesDirective.StopImmediate)\n        : VisitNodesDirective.Stop)\n      : VisitNodesDirective.Continue\n  ));\n  return result;\n};\n\n/**\n * Checks if a Stateful Tree {@link SNode} has a child DOM element.\n *\n * @param node Stateful Tree {@link SNode}.\n * @param child DOM element.\n * @returns True when parent has a DOM element child.\n */\nexport const hasDOMElement = (\n  node: SNode,\n  child: Element,\n): boolean => {\n  var result = false;\n  visitNodes(node, (sNode) => (\n    (sNode.f & Flags.Template) // polymorphic call-site\n      ? (((sNode as STemplate).s1[0] === child)\n        ? (result = true, VisitNodesDirective.StopImmediate)\n        : VisitNodesDirective.Stop)\n      : VisitNodesDirective.Continue\n  ));\n  return result;\n};\n"
  },
  {
    "path": "packages/ivi/src/template/compiler.ts",
    "content": "import {\n  TemplateFlags, ChildOpCode, CommonPropType, PropOpCode, StateOpCode,\n} from \"../lib/template.js\";\nimport {\n  type INode, type ITemplate, type INodeElement, type ITemplateType,\n  NODE_TYPE_ELEMENT,\n  NODE_TYPE_EXPR,\n  NODE_TYPE_TEXT,\n  TEMPLATE_TYPE_SVG,\n  PROPERTY_TYPE_ATTRIBUTE,\n  PROPERTY_TYPE_STYLE,\n  PROPERTY_TYPE_VALUE,\n  PROPERTY_TYPE_DOMVALUE,\n  PROPERTY_TYPE_EVENT,\n  PROPERTY_TYPE_DIRECTIVE,\n} from \"./ir.js\";\nimport {\n  type SNode, SNodeFlags,\n  VOID_ELEMENTS,\n  createSNode,\n} from \"./shared.js\";\n\nexport interface TemplateCompilationArtifact {\n  /** Root Nodes */\n  readonly roots: TemplateNode[],\n}\n\nexport enum TemplateNodeType {\n  Block = 0,\n  Text = 1,\n  Expr = 2,\n}\n\nexport interface TemplateNodeBlock {\n  /** Node type */\n  readonly type: TemplateNodeType.Block;\n  /** Template Flags */\n  readonly flags: TemplateFlags;\n  /** Static Template */\n  readonly template: (string | number)[] | string,\n  /** Prop OpCodes */\n  readonly props: number[],\n  /** Child OpCodes */\n  readonly child: number[],\n  /** State OpCodes */\n  readonly state: number[],\n  /** Data */\n  readonly data: string[],\n  /** Dynamic Expressions */\n  readonly exprs: number[],\n}\n\nexport interface TemplateNodeText {\n  readonly type: TemplateNodeType.Text;\n  readonly value: string;\n}\n\nexport interface TemplateNodeExpr {\n  readonly type: TemplateNodeType.Expr;\n  readonly value: number;\n}\n\nexport type TemplateNode =\n  | TemplateNodeBlock\n  | TemplateNodeText\n  | TemplateNodeExpr\n  ;\n\nexport const compileTemplate = (tpl: ITemplate): TemplateCompilationArtifact => {\n  return {\n    roots: tpl.children.map((node) => compileTemplateNode(node, tpl.type)),\n  };\n};\n\nexport class TemplateCompilerError extends Error {\n  constructor(msg: string) {\n    super(msg);\n  }\n}\n\nconst compileTemplateNode = (node: INode, type: ITemplateType): TemplateNode => {\n  switch (node.type) {\n    case NODE_TYPE_ELEMENT:\n      return compileRootElement(node, type);\n    case NODE_TYPE_EXPR:\n      return {\n        type: TemplateNodeType.Expr,\n        value: node.value,\n      };\n    case NODE_TYPE_TEXT:\n      return {\n        type: TemplateNodeType.Text,\n        value: node.value,\n      };\n  }\n};\n\nconst compileRootElement = (\n  element: INodeElement,\n  type: ITemplateType,\n): TemplateNodeBlock => {\n  // Emits a static template. It can be either a string if it is an element\n  // without any static parts, or an array of strings and expression indices.\n  const template = emitStaticTemplate(element);\n  // Creates a new tree with additional data for compilation.\n  const sRoot = createSNode(element, 0);\n  // Assigns state slots in DFS LTR order.\n  assignStateSlots(sRoot);\n  // Creates dynamic expressions map that stores expr indices that will be\n  // used in the current template block.\n  const exprMap = createExprMap(element);\n  if (exprMap.size > 64) {\n    throw Error(\"Exceeded maximum number (64) of expressions per template block.\");\n  }\n\n  // Emits state OpCodes and traverses tree in DFS LTR order.\n  const state = emitStateOpCodes(sRoot);\n  // Emits props OpCodes and traverses tree in DFS LTR order.\n  const data: string[] = [];\n  const props = emitPropsOpCodes(sRoot, data, exprMap);\n  // Emits child OpCodes and traverses tree in DFS RTL order.\n  const child = emitChildOpCodes(sRoot, exprMap);\n\n  const stateSlots = countStateSlots(state);\n  if (stateSlots > 64) {\n    throw Error(\"Exceeded maximum number (64) of state slots per template block.\");\n  }\n\n  const childSlots = countChildSlots(child);\n  if (childSlots > 64) {\n    throw Error(\"Exceeded maximum number (64) of child slots per template block.\");\n  }\n\n  return {\n    type: TemplateNodeType.Block,\n    flags: (\n      (stateSlots) |\n      (childSlots << TemplateFlags.ChildrenSizeShift) |\n      (type === TEMPLATE_TYPE_SVG ? TemplateFlags.Svg : 0)\n    ),\n    template,\n    props,\n    child,\n    state,\n    data,\n    exprs: Array.from(exprMap.keys()),\n  };\n};\n\nconst countStateSlots = (stateOpCodes: number[]) => {\n  let count = 1; // Root node always occupy 1 slot.\n  for (let i = 0; i < stateOpCodes.length; i++) {\n    const op = stateOpCodes[i];\n    if (\n      (op & StateOpCode.Save) ||\n      ((op & StateOpCode.EnterOrRemove) && (op >> StateOpCode.OffsetShift) === 0)\n    ) {\n      count++;\n    }\n  }\n  return count;\n};\n\nconst countChildSlots = (childOpCodes: number[]) => {\n  let count = 0;\n  for (let i = 0; i < childOpCodes.length; i++) {\n    if ((childOpCodes[i] & ChildOpCode.Type) === ChildOpCode.Child) {\n      count++;\n    }\n  }\n  return count;\n};\n\nconst createExprMap = (\n  root: INodeElement,\n) => {\n  const exprMap = new Map<number, number>();\n  _createExprMap(exprMap, root);\n  return exprMap;\n};\n\nconst _createExprMap = (\n  exprMap: Map<number, number>,\n  node: INodeElement,\n) => {\n  const { properties, children } = node;\n  for (let i = 0; i < properties.length; i++) {\n    const prop = properties[i];\n    const value = prop.value;\n    if (typeof value === \"number\") {\n      exprMap.set(value, exprMap.size);\n    }\n  }\n\n  for (let i = 0; i < children.length; i++) {\n    const child = children[i];\n    const type = child.type;\n    if (type === NODE_TYPE_ELEMENT) {\n      _createExprMap(exprMap, child);\n    } else if (type === NODE_TYPE_EXPR) {\n      exprMap.set(child.value, exprMap.size);\n    }\n  }\n};\n\nconst emitStaticTemplate = (root: INodeElement) => {\n  const staticTemplate: Array<string | number> = [];\n  _emitStaticTemplate(staticTemplate, root);\n  if (staticTemplate.length <= 3 && staticTemplate[1] === \">\") {\n    return root.tag;\n  }\n  return staticTemplate;\n};\n\nconst _emitStaticTemplate = (\n  staticTemplate: Array<string | number>,\n  node: INodeElement,\n) => {\n  const { tag, properties, children } = node;\n  let style = \"\";\n\n  staticTemplate.push(`<${tag}`);\n  for (let i = 0; i < properties.length; i++) {\n    const prop = properties[i];\n    const { type, key, value } = prop;\n    if (type === PROPERTY_TYPE_ATTRIBUTE) {\n      if (key === \"style\") {\n        if (typeof value === \"string\") {\n          if (style !== \"\") {\n            style += `;${value}`;\n          } else {\n            style = value;\n          }\n        }\n      } else {\n        if (value === true) {\n          staticTemplate.push(` ${key}`);\n        } else if (typeof value === \"string\") {\n          staticTemplate.push(` ${key}=\"${value}\"`);\n        }\n      }\n    } else if (type === PROPERTY_TYPE_STYLE) {\n      if (typeof value === \"string\") {\n        if (style !== \"\") {\n          style += `;${key}:${value}`;\n        } else {\n          style = `${key}:${value}`;\n        }\n      }\n    }\n  }\n  if (style !== \"\") {\n    staticTemplate.push(` style=\"${style}\"`);\n  }\n  staticTemplate.push(`>`);\n  if (VOID_ELEMENTS.test(tag)) {\n    if (children.length > 0) {\n      throw new TemplateCompilerError(`Invalid template, void element '${tag}' shouldn't have any children.`);\n    }\n    return;\n  }\n\n  let state = 0;\n  for (let i = 0; i < children.length; i++) {\n    const child = children[i];\n    switch (child.type) {\n      case NODE_TYPE_ELEMENT:\n        _emitStaticTemplate(staticTemplate, child);\n        state = 0;\n        break;\n      case NODE_TYPE_TEXT:\n        if ((state & 3) === 3) {\n          staticTemplate.push(\"<!>\");\n        }\n        state = 1;\n        staticTemplate.push(child.value);\n        break;\n      case NODE_TYPE_EXPR:\n        state |= 2;\n        break;\n    }\n  }\n  staticTemplate.push(`</${tag}>`);\n};\n\nconst getDataIndex = (\n  data: string[],\n  dataMap: Map<string, number>,\n  key: string,\n) => {\n  let index = dataMap.get(key);\n  if (index === void 0) {\n    index = data.length;\n    data.push(key);\n    dataMap.set(key, index);\n  }\n  return index;\n};\n\nconst emitPropsOpCodes = (\n  root: SNode,\n  data: string[],\n  exprMap: Map<number, number>,\n): number[] => {\n  const dataMap = new Map<string, number>();\n  const opCodes: number[] = [];\n  _emitPropsOpCodes(opCodes, root, true, data, dataMap, exprMap);\n  return opCodes;\n};\nconst _emitPropsOpCodes = (\n  opCodes: number[],\n  node: SNode,\n  isRoot: boolean,\n  data: string[],\n  dataMap: Map<string, number>,\n  exprMap: Map<number, number>,\n) => {\n  const iNode = node.node;\n  if (iNode.type === NODE_TYPE_ELEMENT) {\n    if (node.propsExprs > 0) {\n      if (isRoot === false) {\n        opCodes.push(PropOpCode.SetNode | (node.stateIndex << PropOpCode.DataShift));\n      }\n      const properties = iNode.properties;\n      for (let i = 0; i < properties.length; i++) {\n        const prop = properties[i];\n        const value = prop.value;\n        if (typeof value === \"number\") {\n          const { type, key } = prop;\n          switch (type) {\n            case PROPERTY_TYPE_ATTRIBUTE:\n              const exprIndex = exprMap.get(value)!;\n              if (exprIndex !== -1) {\n                if (key === \"class\") {\n                  opCodes.push(\n                    PropOpCode.Common |\n                    (CommonPropType.ClassName << PropOpCode.DataShift) |\n                    (exprIndex << PropOpCode.InputShift)\n                  );\n                } else {\n                  opCodes.push(\n                    PropOpCode.Attribute |\n                    (getDataIndex(data, dataMap, key) << PropOpCode.DataShift) |\n                    (exprIndex << PropOpCode.InputShift)\n                  );\n                }\n              }\n              break;\n            case PROPERTY_TYPE_VALUE:\n              if (key === \"textContent\") {\n                opCodes.push(\n                  PropOpCode.Common |\n                  (CommonPropType.TextContent << PropOpCode.DataShift) |\n                  (exprMap.get(value)! << PropOpCode.InputShift)\n                );\n              } else if (key === \"innerHTML\") {\n                opCodes.push(\n                  PropOpCode.Common |\n                  (CommonPropType.InnerHTML << PropOpCode.DataShift) |\n                  (exprMap.get(value)! << PropOpCode.InputShift)\n                );\n              } else {\n                opCodes.push(\n                  PropOpCode.Property |\n                  (getDataIndex(data, dataMap, key) << PropOpCode.DataShift) |\n                  (exprMap.get(value)! << PropOpCode.InputShift)\n                );\n              }\n              break;\n            case PROPERTY_TYPE_DOMVALUE:\n              if (key === \"textContent\") {\n                opCodes.push(\n                  PropOpCode.Common |\n                  (CommonPropType.TextContent << PropOpCode.DataShift) |\n                  (exprMap.get(value)! << PropOpCode.InputShift)\n                );\n              } else if (key === \"innerHTML\") {\n                opCodes.push(\n                  PropOpCode.Common |\n                  (CommonPropType.InnerHTML << PropOpCode.DataShift) |\n                  (exprMap.get(value)! << PropOpCode.InputShift)\n                );\n              } else {\n                opCodes.push(\n                  PropOpCode.DiffDOMProperty |\n                  (getDataIndex(data, dataMap, key) << PropOpCode.DataShift) |\n                  (exprMap.get(value)! << PropOpCode.InputShift)\n                );\n              }\n              break;\n            case PROPERTY_TYPE_STYLE:\n              opCodes.push(\n                PropOpCode.Style |\n                (getDataIndex(data, dataMap, key) << PropOpCode.DataShift) |\n                (exprMap.get(value)! << PropOpCode.InputShift)\n              );\n              break;\n            case PROPERTY_TYPE_EVENT:\n              opCodes.push(\n                PropOpCode.Event |\n                (getDataIndex(data, dataMap, key) << PropOpCode.DataShift) |\n                (exprMap.get(value)! << PropOpCode.InputShift)\n              );\n              break;\n            case PROPERTY_TYPE_DIRECTIVE:\n              opCodes.push(\n                PropOpCode.Directive |\n                (exprMap.get(value)! << PropOpCode.InputShift)\n              );\n              break;\n          }\n        }\n      }\n    }\n\n    const children = node.children;\n    if (children !== null) {\n      for (let i = 0; i < children.length; i++) {\n        _emitPropsOpCodes(opCodes, children[i], false, data, dataMap, exprMap);\n      }\n    }\n  }\n};\n\nconst emitStateOpCodes = (root: SNode<INodeElement>): number[] => {\n  const opCodes: number[] = [];\n  _emitStateOpCodes(opCodes, root);\n  return opCodes;\n};\n\nconst enum VisitState {\n  PrevText = 1,\n  PrevExpr = 1 << 1,\n}\n\nconst _emitStateOpCodes = (\n  opCodes: number[],\n  node: SNode<INodeElement>,\n) => {\n  const children = node.children;\n  if (children !== null) {\n    let state = 0;\n    outer: for (let i = 0; i < children.length; i++) {\n      const child = children[i];\n      switch (child.node.type) {\n        case NODE_TYPE_ELEMENT: {\n          let opCode = 0;\n          if (\n            state & VisitState.PrevExpr ||\n            child.childrenExprs > 0 ||\n            child.propsExprs > 0\n          ) {\n            opCode = StateOpCode.Save;\n          }\n\n          const currentOpCodeIndex = opCodes.length;\n          opCodes.push(opCode);\n\n          if (child.flags & SNodeFlags.HasExpressions) {\n            _emitStateOpCodes(opCodes, child as SNode<INodeElement>);\n            const childrenOffset = opCodes.length - (currentOpCodeIndex + 1);\n            if (childrenOffset > 0) {\n              opCode |= StateOpCode.EnterOrRemove | (childrenOffset << StateOpCode.OffsetShift);\n              opCodes[currentOpCodeIndex] = opCode;\n            }\n          }\n          if (\n            (child.flags & (SNodeFlags.HasNextExpressions | SNodeFlags.HasNextDOMNode)) !==\n            (SNodeFlags.HasNextExpressions | SNodeFlags.HasNextDOMNode)\n          ) {\n            if (opCode === 0) {\n              opCodes.pop();\n            }\n            break outer;\n          }\n          state = 0;\n          break;\n        }\n        case NODE_TYPE_TEXT: {\n          if ((state & (VisitState.PrevText | VisitState.PrevExpr)) === (VisitState.PrevText | VisitState.PrevExpr)) {\n            opCodes.push(StateOpCode.EnterOrRemove);\n          } else if (state & VisitState.PrevExpr) {\n            opCodes.push(StateOpCode.Save);\n          } else {\n            if (\n              (child.flags & (SNodeFlags.HasNextExpressions | SNodeFlags.HasNextDOMNode)) !==\n              (SNodeFlags.HasNextExpressions | SNodeFlags.HasNextDOMNode)\n            ) {\n              break outer;\n            } else {\n              opCodes.push(0);\n            }\n          }\n          state = 1;\n          break;\n        }\n        case NODE_TYPE_EXPR:\n          state |= VisitState.PrevExpr;\n          break;\n      }\n    }\n  }\n};\n\nconst emitChildOpCodes = (\n  root: SNode<INodeElement>,\n  exprMap: Map<number, number>,\n): number[] => {\n  const opCodes: number[] = [];\n  _emitChildOpCodes(opCodes, root, true, exprMap);\n  return opCodes;\n};\n\nconst _emitChildOpCodes = (\n  opCodes: number[],\n  node: SNode,\n  isRoot: boolean,\n  exprMap: Map<number, number>,\n) => {\n  if (node.childrenExprs > 0) {\n    const children = node.children;\n    if (children !== null) {\n      // Do not emit SetParent for root nodes\n      if (isRoot === false) {\n        opCodes.push(ChildOpCode.SetParent | (node.stateIndex << ChildOpCode.ValueShift));\n      }\n      let prev: SNode | undefined;\n      let i = children.length;\n      while (--i >= 0) {\n        const child = children[i];\n        if (child.node.type === NODE_TYPE_EXPR) {\n          if (prev !== void 0 && prev.node.type !== NODE_TYPE_EXPR) {\n            opCodes.push(ChildOpCode.SetNext | (prev.stateIndex << ChildOpCode.ValueShift));\n          }\n          opCodes.push(ChildOpCode.Child | (exprMap.get(child.node.value)! << ChildOpCode.ValueShift));\n        }\n        prev = child;\n      }\n    }\n  }\n  const children = node.children;\n  if (children !== null) {\n    let i = children.length;\n    while (--i >= 0) {\n      const child = children[i];\n      if (child.node.type === NODE_TYPE_ELEMENT) {\n        _emitChildOpCodes(opCodes, child, false, exprMap);\n      }\n    }\n  }\n};\n\nconst assignStateSlots = (root: SNode): number => (\n  _assignStateSlots(root, 1)\n);\n\nconst _assignStateSlots = (node: SNode, stateIndex: number): number => {\n  const children = node.children;\n  if (children !== null) {\n    let prevExpr = false;\n    for (let i = 0; i < children.length; i++) {\n      const child = children[i];\n      switch (child.node.type) {\n        case NODE_TYPE_ELEMENT:\n          if (prevExpr) {\n            child.stateIndex = stateIndex++;\n            prevExpr = false;\n          } else if (child.propsExprs > 0 || child.childrenExprs > 0) {\n            child.stateIndex = stateIndex++;\n          }\n          stateIndex = _assignStateSlots(child, stateIndex);\n        case NODE_TYPE_TEXT:\n          if (prevExpr) {\n            child.stateIndex = stateIndex++;\n            prevExpr = false;\n          }\n          break;\n        case NODE_TYPE_EXPR:\n          prevExpr = true;\n      }\n    }\n  }\n  return stateIndex;\n};\n"
  },
  {
    "path": "packages/ivi/src/template/ir.ts",
    "content": "/**\n * Template Intermediate Representation.\n */\n\nexport interface ITemplate {\n  readonly type: ITemplateType;\n  readonly children: INode[];\n}\n\nexport type ITemplateType =\n  | typeof TEMPLATE_TYPE_HTM\n  | typeof TEMPLATE_TYPE_SVG\n  ;\n\nexport const TEMPLATE_TYPE_HTM = 0;\nexport const TEMPLATE_TYPE_SVG = 1;\n\nexport const NODE_TYPE_ELEMENT = 0;\nexport const NODE_TYPE_TEXT = 1;\nexport const NODE_TYPE_EXPR = 2;\n\nexport type IPropertyType =\n  | typeof PROPERTY_TYPE_ATTRIBUTE\n  | typeof PROPERTY_TYPE_VALUE\n  | typeof PROPERTY_TYPE_DOMVALUE\n  | typeof PROPERTY_TYPE_STYLE\n  | typeof PROPERTY_TYPE_EVENT\n  | typeof PROPERTY_TYPE_DIRECTIVE\n  ;\n\nexport const PROPERTY_TYPE_ATTRIBUTE = 0;\nexport const PROPERTY_TYPE_VALUE = 1;\nexport const PROPERTY_TYPE_DOMVALUE = 2;\nexport const PROPERTY_TYPE_STYLE = 3;\nexport const PROPERTY_TYPE_EVENT = 4;\nexport const PROPERTY_TYPE_DIRECTIVE = 5;\n\nexport interface IPropertyAttribute {\n  readonly type: typeof PROPERTY_TYPE_ATTRIBUTE;\n  readonly key: string;\n  readonly value: string | boolean | number;\n}\n\nexport interface IPropertyValue {\n  readonly type: typeof PROPERTY_TYPE_VALUE;\n  readonly key: string;\n  readonly value: number;\n  readonly hoist: boolean;\n}\n\nexport interface IPropertyDOMValue {\n  readonly type: typeof PROPERTY_TYPE_DOMVALUE;\n  readonly key: string;\n  readonly value: number;\n  readonly hoist: boolean;\n}\n\nexport interface IPropertyStyle {\n  readonly type: typeof PROPERTY_TYPE_STYLE;\n  readonly key: string;\n  readonly value: string | number;\n  readonly hoist: boolean;\n}\n\nexport interface IPropertyEvent {\n  readonly type: typeof PROPERTY_TYPE_EVENT;\n  readonly key: string;\n  readonly value: number;\n  readonly hoist: boolean;\n}\n\nexport interface IPropertyDirective {\n  readonly type: typeof PROPERTY_TYPE_DIRECTIVE;\n  readonly key: null;\n  readonly value: number;\n  readonly hoist: boolean;\n}\n\nexport type IProperty =\n  | IPropertyAttribute\n  | IPropertyValue\n  | IPropertyDOMValue\n  | IPropertyStyle\n  | IPropertyEvent\n  | IPropertyDirective\n  ;\n\nexport interface INodeElement {\n  readonly type: typeof NODE_TYPE_ELEMENT;\n  readonly tag: string;\n  readonly properties: IProperty[];\n  readonly children: INode[];\n}\n\nexport interface INodeText {\n  readonly type: typeof NODE_TYPE_TEXT;\n  readonly value: string;\n}\n\nexport interface INodeExpr {\n  readonly type: typeof NODE_TYPE_EXPR;\n  readonly value: number;\n}\n\nexport type INode = INodeElement | INodeText | INodeExpr;\n"
  },
  {
    "path": "packages/ivi/src/template/parser.ts",
    "content": "export class TemplateParserError extends Error {\n  staticsOffset: number;\n  textOffset: number;\n\n  constructor(message: string, staticsOffset: number, textOffset: number) {\n    super(message);\n    this.staticsOffset = staticsOffset;\n    this.textOffset = textOffset;\n  }\n}\n\nexport class TemplateScanner {\n  readonly statics: string[] | TemplateStringsArray;\n  readonly exprCount: number;\n  text: string;\n  i: number;\n  e: number;\n\n  constructor(statics: string[] | TemplateStringsArray) {\n    if (statics.length === 0) {\n      throw new TemplateParserError(\"Template is empty.\", 0, 0);\n    }\n\n    this.statics = statics;\n    this.exprCount = statics.length - 1;\n    this.text = statics[0];\n    this.i = 0;\n    this.e = 0;\n  }\n\n  isEnd(): boolean {\n    return (\n      this.i === this.text.length &&\n      this.e === this.exprCount\n    );\n  }\n\n  peekCharCode(i: number = 0): number {\n    i += this.i;\n    return i < this.text.length\n      ? this.text.charCodeAt(i)\n      : -1;\n  }\n\n  charCode(c: number): boolean {\n    if (this.i < this.text.length && this.text.charCodeAt(this.i) === c) {\n      this.i++;\n      return true;\n    }\n    return false;\n  }\n\n  peekExpr(): number {\n    const e = this.e;\n    return (e < this.exprCount && this.i === this.text.length)\n      ? e\n      : -1;\n  }\n\n  expr(): number {\n    const e = this.e;\n    if (e < this.exprCount && this.i === this.text.length) {\n      this.i = 0;\n      this.text = this.statics[++this.e];\n      return e;\n    }\n    return -1;\n  }\n\n  peekString(s: string): boolean {\n    const text = this.text;\n    const end = this.i + s.length;\n    return (\n      end <= text.length &&\n      text.substring(this.i, end) === s\n    );\n  }\n\n  string(s: string): boolean {\n    const r = this.peekString(s);\n    if (r) {\n      this.i += s.length;\n    }\n    return r;\n  }\n\n  peekRegExp(re: RegExp): string | undefined {\n    if (!re.sticky) {\n      throw Error(\"RegExp should have a sticky flag.\");\n    }\n\n    re.lastIndex = this.i;\n    const match = re.exec(this.text);\n    if (match !== null) {\n      return match[0];\n    }\n  }\n\n  regExp(re: RegExp): string | undefined {\n    const r = this.peekRegExp(re);\n    if (r !== void 0) {\n      this.i += r.length;\n    }\n    return r;\n  }\n}\n\n/**\n * Formats error message.\n *\n * @param errorMsg Error message.\n * @param errorCol Line column.\n */\nconst formatErrorMsg = (errorMsg: string, errorCol: number): string => {\n  let msg = \"\";\n  while (--errorCol >= 0) {\n    msg += \" \";\n  }\n  msg += \"^\\nError: \" + errorMsg + \"\\n\";\n  return msg;\n};\n\n/**\n * Formats compiler errors.\n *\n * @param statics Statics.\n * @param errorMsg Error message.\n * @param staticsOffset Expression offset.\n * @param textOffset Text offset.\n * @returns Formatted error message.\n */\nexport const formatError = (\n  statics: TemplateStringsArray,\n  errorMsg: string,\n  staticsOffset: number,\n  textOffset: number,\n): string => {\n  for (let i = 0; i < staticsOffset; i++) {\n    if (i > 0) {\n      textOffset += 4 + (Math.log10(i) | 0); // ${i}\n    }\n    textOffset += statics[i].length;\n  }\n\n  let msg = \"\\n\";\n  let text = statics[0];\n  for (let i = 1; i < statics.length; i++) {\n    text += \"${\" + (i - 1) + \"}\" + statics[i];\n  }\n\n  for (const line of text.split(\"\\n\")) {\n    msg += line + \"\\n\";\n    if (textOffset >= 0 && textOffset < line.length) {\n      msg += formatErrorMsg(errorMsg, textOffset);\n    }\n    textOffset -= (line.length + 1);\n  }\n\n  if (textOffset >= 0) {\n    msg += formatErrorMsg(errorMsg, textOffset);\n  }\n\n  return msg;\n};\n\n/**\n * ASCII Char Codes.\n */\nexport const enum CharCode {\n  /** \"\\\\t\" */Tab = 9,\n  /** \"\\\\n\" */Newline = 10,\n  /** \"\\\\v\" */VerticalTab = 11,\n  /** \"\\\\r\" */CarriageReturn = 13,\n  /** [space] */Space = 32,\n  /** \"!\" */ExclamationMark = 33,\n  /** \"\\\\\"\" */DoubleQuote = 34,\n  /** \"#\" */NumberSign = 35,\n  /** \"#\" */Hash = 35,\n  /** \"$\" */DollarSign = 36,\n  /** \"%\" */PercentSign = 37,\n  /** \"&\" */Ampersand = 38,\n  /** \"'\" */SingleQuote = 39,\n  /** \"(\" */LeftParen = 40,\n  /** \")\" */RightParen = 41,\n  /** \"*\" */Asterisk = 42,\n  /** \"+\" */PlusSign = 43,\n  /** \",\" */Comma = 44,\n  /** \"-\" */MinusSign = 45,\n  /** \".\" */Dot = 46,\n  /** \"/\" */Slash = 47,\n  /** \":\" */Colon = 58,\n  /** \";\" */Semicolon = 59,\n  /** \"<\" */LessThan = 60,\n  /** \"=\" */EqualsTo = 61,\n  /** \">\" */MoreThan = 62,\n  /** \"?\" */QuestionMark = 63,\n  /** \"@\" */AtSign = 64,\n  /** \"[\" */LeftSquareBracket = 91,\n  /** \"\\\" */Backslash = 92,\n  /** \"]\" */RightSquareBracket = 93,\n  /** \"^\" */Caret = 94,\n  /** \"_\" */Underscore = 95,\n  /** \"`\" */GraveAccent = 96,\n  /** \"{\" */LeftCurlyBrace = 123,\n  /** \"|\" */VerticalBar = 124,\n  /** \"}\" */RightCurlyBrace = 125,\n  /** \"~\" */Tilde = 126,\n}\n"
  },
  {
    "path": "packages/ivi/src/template/shared.ts",
    "content": "import {\n  NODE_TYPE_ELEMENT,\n  NODE_TYPE_EXPR,\n  NODE_TYPE_TEXT,\n  type INode, type INodeElement,\n} from \"./ir.js\";\n\nexport const enum SNodeFlags {\n  /** Has expressions in the subtree. */\n  HasExpressions = 1,\n  /** Has expressions in the subtrees of next siblings. */\n  HasNextExpressions = 1 << 1,\n  /** Has next DOM node. */\n  HasNextDOMNode = 1 << 2,\n}\n\nexport interface SNode<T extends INode = INode> {\n  readonly node: T;\n  stateIndex: number;\n  children: SNode[] | null;\n  propsExprs: number,\n  childrenExprs: number;\n  flags: number;\n}\n\nexport const createSNode = (node: INodeElement, flags: number): SNode<INodeElement> => {\n  const properties = node.properties;\n  const iChildren = node.children;\n\n  let propsExprs = 0;\n  for (let i = 0; i < properties.length; i++) {\n    if (typeof properties[i].value === \"number\") {\n      propsExprs++;\n    }\n  }\n\n  let children: SNode[] | null = null;\n  let siblingsFlags = 0;\n  let childrenExprs = 0;\n  let i = iChildren.length;\n  if (i > 0) {\n    children = Array(i);\n    while (i > 0) {\n      const child = iChildren[--i];\n      switch (child.type) {\n        case NODE_TYPE_ELEMENT:\n          const sNode = createSNode(child, siblingsFlags);\n          if (sNode.flags & SNodeFlags.HasExpressions) {\n            flags |= SNodeFlags.HasExpressions;\n            siblingsFlags |= SNodeFlags.HasNextExpressions;\n          }\n          siblingsFlags |= SNodeFlags.HasNextDOMNode;\n          children[i] = sNode;\n          break;\n        case NODE_TYPE_EXPR:\n          siblingsFlags |= SNodeFlags.HasNextExpressions;\n          childrenExprs++;\n          children[i] = {\n            node: child,\n            stateIndex: 0,\n            children: null,\n            childrenExprs: 0,\n            propsExprs: 0,\n            flags: siblingsFlags,\n          };\n          break;\n        case NODE_TYPE_TEXT:\n          children[i] = {\n            node: child,\n            stateIndex: 0,\n            children: null,\n            childrenExprs: 0,\n            propsExprs: 0,\n            flags: siblingsFlags,\n          };\n          siblingsFlags |= SNodeFlags.HasNextDOMNode;\n      }\n    }\n  }\n\n  if (propsExprs > 0 || childrenExprs > 0) {\n    flags |= SNodeFlags.HasExpressions;\n  }\n\n  return {\n    node,\n    stateIndex: 0,\n    children,\n    childrenExprs,\n    propsExprs,\n    flags,\n  };\n};\n\nexport const VOID_ELEMENTS = /^(audio|video|embed|input|param|source|textarea|track|area|base|link|meta|br|col|hr|img|wbr)$/;\n"
  },
  {
    "path": "packages/ivi/src/test-utils/index.ts",
    "content": "import {\n  type Root, type VAny,\n  defineRoot, dirtyCheck, unmount, findDOMNode, update,\n} from \"../index.js\";\n\nexport class TestRoot {\n  _root: Root;\n  _isDirty: boolean;\n\n  get root() { return this._root; }\n  get isDirty() { return this._isDirty; }\n\n  constructor() {\n    this._root = null!;\n    this._isDirty = false;\n  }\n\n  findDOMNode<T extends Node>(): T | null {\n    return findDOMNode<T>(this._root);\n  }\n\n  update(v: VAny, forceUpdate?: boolean) {\n    update(this._root, v, forceUpdate);\n    this._isDirty = false;\n  }\n\n  dirtyCheck(forceUpdate?: boolean) {\n    dirtyCheck(this._root, forceUpdate);\n    this._isDirty = false;\n  }\n\n  dispose() {\n    unmount(this._root, true);\n    this._isDirty = false;\n  }\n\n  _invalidate() {\n    this._isDirty = true;\n  }\n}\n\nexport const createRoot = (\n  parentElement: Element = document.body,\n  nextNode: Node | null = null,\n) => {\n  const testRoot = new TestRoot();\n  const root = _createRoot(parentElement, nextNode, testRoot);\n  testRoot._root = root;\n  return testRoot;\n};\n\nconst _createRoot = defineRoot<TestRoot>(\n  (root, state) => { state._invalidate(); },\n);\n"
  },
  {
    "path": "packages/ivi/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.composite.json\",\n}\n"
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"stable\"\nprofile = \"default\"\n"
  },
  {
    "path": "tests/compiler/chunk/strings/data/01-basic/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst STRINGS = [\"IVI:fa7327d9-0034-492d-bfdf-576548b2d9cc\"];\n\nconst c = component(() => {\n\treturn (v) => html`<div a=${v}/>`;\n});"
  },
  {
    "path": "tests/compiler/chunk/strings/data/01-basic/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst STRINGS = [\"a\"];\nconst _TPL_ = _T(_dedupe(_hE(\"div\")), 1, _dedupe([2]), _EMPTY_ARRAY, _EMPTY_ARRAY);\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/chunk/strings/data/02-multiple/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst STRINGS = [\"IVI:fa7327d9-0034-492d-bfdf-576548b2d9cc\"];\n\nconst c1 = component(() => {\n\treturn (v) => html`<div a=${v}/>`;\n});\n\nconst c2 = component(() => {\n\treturn (v) => html`<div b=${v}/>`;\n});"
  },
  {
    "path": "tests/compiler/chunk/strings/data/02-multiple/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst STRINGS = [\"a\", \"b\"];\nconst _TPL_ = _T(_dedupe(_hE(\"div\")), 1, _dedupe([2]), _EMPTY_ARRAY, _EMPTY_ARRAY);\nconst c1 = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\nconst _TPL_2 = _T(_dedupe(_hE(\"div\")), 1, _dedupe([514]), _EMPTY_ARRAY, _EMPTY_ARRAY);\nconst c2 = component(() => {\n\treturn (v) => _t(_TPL_2, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/chunk/strings/strings.test.ts",
    "content": "import { beforeEach, expect, test } from \"bun:test\";\nimport { readdir } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { TemplateCompiler } from \"@ivi/compiler\";\nimport { normalizeNewlines } from \"../../normalize.js\";\n\n\nconst units = path.join(import.meta.dir, \"data\");\nconst entries = await readdir(units, { recursive: true });\nfor (const entry of entries) {\n  try {\n    const input = await Bun.file(path.join(units, entry, \"input.js\")).text();\n\n    test(`compiler/chunk/strings/${entry}`, async () => {\n      const compiler = new TemplateCompiler({ oveo: true, dedupeStrings: true });\n      const output = Bun.file(path.join(units, entry, \"output.js\"));\n      const moduleResult = await compiler.transform(input, \"js\");\n      compiler.renderStart();\n      const chunkResult = await compiler.renderChunk(moduleResult.code);\n      expect(normalizeNewlines(chunkResult.code)).toBe(normalizeNewlines(await output.text()));\n    });\n  } catch (err) {\n  }\n}\n"
  },
  {
    "path": "tests/compiler/module/data/01-text/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`a`;\n});"
  },
  {
    "path": "tests/compiler/module/data/01-text/output.js",
    "content": "import { component, html } from \"ivi\";\nconst c = component(() => {\n\treturn (v) => \"a\";\n});\n"
  },
  {
    "path": "tests/compiler/module/data/02-element/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<a/>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/02-element/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"a\")), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/03-element/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<a></a>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/03-element/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"a\")), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/04-nested-text/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<a>a</a>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/04-nested-text/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a>a</a>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/05-nested-expr/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<a>${v}</a>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/05-nested-expr/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"a\")), 65, _EMPTY_ARRAY, _dedupe([0]), _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/06-text-before-expr/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<a>a${v}</a>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/06-text-before-expr/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a>a</a>`)), 65, _EMPTY_ARRAY, _dedupe([0]), _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/07-text-after-expr/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<a>${v}b</a>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/07-text-after-expr/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a>b</a>`)), 66, _EMPTY_ARRAY, _dedupe([5, 0]), _dedupe([1])));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/09-text-before-and-after-expr/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<a>a${v}b</a>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/09-text-before-and-after-expr/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a>a<!>b</a>`)), 66, _EMPTY_ARRAY, _dedupe([5, 0]), _dedupe([0, 2])));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/103-hoist-return-fn/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => (v) => html`<a/>`);\n"
  },
  {
    "path": "tests/compiler/module/data/103-hoist-return-fn/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { hoist as _hoist, dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"a\")), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => _hoist((v) => _t(_TPL_)));\n"
  },
  {
    "path": "tests/compiler/module/data/11-multiple-roots/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`a<a/>b`;\n});"
  },
  {
    "path": "tests/compiler/module/data/11-multiple-roots/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"a\")), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => [\n\t\t\"a\",\n\t\t_t(_TPL_),\n\t\t\"b\"\n\t];\n});\n"
  },
  {
    "path": "tests/compiler/module/data/12-multiple-roots/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`a${v}b`;\n});"
  },
  {
    "path": "tests/compiler/module/data/12-multiple-roots/output.js",
    "content": "import { component, html } from \"ivi\";\nconst c = component(() => {\n\treturn (v) => [\n\t\t\"a\",\n\t\tv,\n\t\t\"b\"\n\t];\n});\n"
  },
  {
    "path": "tests/compiler/module/data/13-multiple-nested-expr/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div>\n\t\t  <span>${v.a}</span>\n\t\t\t<span>${v.b}</span>\n\t\t</div>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/13-multiple-nested-expr/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div><span></span><span></span></div>`)), 131, _EMPTY_ARRAY, _dedupe([\n\t11,\n\t4,\n\t7,\n\t0\n]), _dedupe([1, 1])));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v.a, v.b]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/14-svg-element/input.js",
    "content": "import { component, svg } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => svg`<a></a>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/14-svg-element/output.js",
    "content": "import { component, svg } from \"ivi\";\nimport { _T, _sE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_sE(\"a\")), 4097, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/15-svg-template/input.js",
    "content": "import { component, svg } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => svg`<a a></a>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/15-svg-template/output.js",
    "content": "import { component, svg } from \"ivi\";\nimport { _T, _sN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_sN(`<a a></a>`)), 4097, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/16-nested-mix/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <a>\n      <b>\n        <c>\n          <d class=${v.d1}/>\n        </c>\n        <c>\n          ${v.c1}\n          <d class=${v.d2}/>\n        </c>\n        <c>\n          <d class=${v.d3}/>\n          ${v.c2}\n        </c>\n      </b>\n      <b>\n        <c><d/></c>\n        <c>${v.c1}</c>\n        <c><d/></c>\n      </b>\n      ${v.f}\n    </a>\n  `;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/16-nested-mix/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a><b><c><d></d></c><c><d></d></c><c><d></d></c></b><b><c><d></d></c><c></c><c><d></d></c></b></a>`)), 263, _dedupe([\n\t512,\n\t1,\n\t1536,\n\t17,\n\t2560,\n\t25\n]), _dedupe([\n\t24,\n\t27,\n\t20,\n\t19,\n\t16,\n\t11,\n\t13,\n\t4\n]), _dedupe([\n\t26,\n\t6,\n\t1,\n\t7,\n\t1,\n\t7,\n\t1,\n\t10,\n\t0,\n\t1\n])));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [\n\t\tv.d1,\n\t\tv.c1,\n\t\tv.d2,\n\t\tv.d3,\n\t\tv.c2,\n\t\tv.c1,\n\t\tv.f\n\t]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/17-void-element/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <a>\n\t\t\t<audio>\n\t\t\t<video>\n\t\t\t<embed>\n\t\t\t<input>\n\t\t\t<param>\n\t\t\t<source>\n\t\t\t<track>\n\t\t\t<area>\n\t\t\t<base>\n\t\t\t<link>\n\t\t\t<meta>\n\t\t\t<br>\n\t\t\t<col>\n\t\t\t<hr>\n\t\t\t<img>\n\t\t\t<wbr>\n\t\t</a>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/17-void-element/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a><audio><video><embed><input><param><source><track><area><base><link><meta><br><col><hr><img><wbr></a>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/18-expr-before-element/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <a class=${v.a}>\n      ${v.b}\n      <b/>\n    </a>\n  `;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/18-expr-before-element/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a><b></b></a>`)), 66, _dedupe([1]), _dedupe([5, 4]), _dedupe([1])));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v.a, v.b]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/19-comment/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <div>\n      <!-- comment -->\n      a\n      <!>\n    </div>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/19-comment/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div>a</div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/20-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <a>\n\t\t</a>\n\t`;\n});"
  },
  {
    "path": "tests/compiler/module/data/20-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"a\")), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/21-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <a>\n\t\t\n\t\t</a>\n\t`;\n});"
  },
  {
    "path": "tests/compiler/module/data/21-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"a\")), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/22-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <a> </a>\n\t`;\n});"
  },
  {
    "path": "tests/compiler/module/data/22-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a> </a>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/23-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n    <div>\n      <a></a>\n      <b></b>\n    </div>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/23-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div><a></a><b></b></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/24-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <div>\n      a\n    </div>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/24-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div>a</div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/25-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <div>\n      a\n      b\n    </div>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/25-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div>a b</div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/26-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <div>\n      a  b\n    </div>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/26-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div>a b</div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/27-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <div>  a  b  </div>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/27-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div> a b </div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/28-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <div>\n      \\va\n    </div>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/28-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div> a</div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/29-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <div>\n      a\\v\n    </div>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/29-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div>a </div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/30-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <div>\n      a\n      <a>\n        b\n      </a>\n    </div>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/30-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div>a<a>b</a></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/31-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <a> ${v}</a>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/31-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a> </a>`)), 65, _EMPTY_ARRAY, _dedupe([0]), _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/32-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <a>${v} </a>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/32-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a> </a>`)), 66, _EMPTY_ARRAY, _dedupe([5, 0]), _dedupe([1])));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/33-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <a> ${v} </a>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/33-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a> <!> </a>`)), 66, _EMPTY_ARRAY, _dedupe([5, 0]), _dedupe([0, 2])));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/34-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <a> ${v} ${v} </a>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/34-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a> <!> <!> </a>`)), 131, _EMPTY_ARRAY, _dedupe([\n\t9,\n\t4,\n\t5,\n\t0\n]), _dedupe([\n\t0,\n\t2,\n\t2\n])));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v, v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/35-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <a>\n    ${v}\n    </a>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/35-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"a\")), 65, _EMPTY_ARRAY, _dedupe([0]), _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/36-whitespace/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n  return (v) => html`\n    <a>\n    \\v${v}\\v\n    </a>\n\t`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/36-whitespace/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<a> <!> </a>`)), 66, _EMPTY_ARRAY, _dedupe([5, 0]), _dedupe([0, 2])));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/40-attribute/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<div a/>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/40-attribute/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div a></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/41-attribute/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<div a=\"1\"/>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/41-attribute/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div a=\"1\"></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/42-attribute/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`<div a=${v}/>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/42-attribute/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"div\")), 1, _dedupe([2]), _EMPTY_ARRAY, _EMPTY_ARRAY, [\"a\"]));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/43-attributes/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  a=\"1\"\n\t\t\tb=\"2\"\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/43-attributes/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div a=\"1\" b=\"2\"></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/44-attributes/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  a=\"1\"\n\t\t\tb=\"2\"\n\t\t></div>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/44-attributes/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div a=\"1\" b=\"2\"></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/50-prop/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  .a=${v}\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/50-prop/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"div\")), 1, _dedupe([3]), _EMPTY_ARRAY, _EMPTY_ARRAY, [\"a\"]));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/51-prop/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  *a=${v}\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/51-prop/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"div\")), 1, _dedupe([4]), _EMPTY_ARRAY, _EMPTY_ARRAY, [\"a\"]));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/60-style/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  ~a=\"0\"\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/60-style/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div style=\"a:0\"></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/61-style/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  ~a=${v}\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/61-style/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"div\")), 1, _dedupe([5]), _EMPTY_ARRAY, _EMPTY_ARRAY, [\"a\"]));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/62-style/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  ~a=\"0\"\n\t\t\tstyle=\"b:1\"\n\t\t\t~c=\"2\"\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/62-style/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div style=\"a:0;b:1;c:2\"></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/63-style-mix/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  ~a=\"0\"\n\t\t\t~a=${v}\n\t\t\tstyle=\"b:1\"\n\t\t\t~c=\"2\"\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/63-style-mix/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div style=\"a:0;b:1;c:2\"></div>`)), 1, _dedupe([5]), _EMPTY_ARRAY, _EMPTY_ARRAY, [\"a\"]));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/70-event/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  @a=${v}\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/70-event/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { hoist as _hoist, dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"div\")), 1, _dedupe([6]), _EMPTY_ARRAY, _EMPTY_ARRAY, [\"a\"]));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [_hoist(v)]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/80-directive/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  ${v}\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module/data/80-directive/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hE(\"div\")), 1, _dedupe([7]), _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/90-hoist-class-identifier/input.js",
    "content": "import { component, html } from \"ivi\";\nimport { cls } from \"css\";\n\nconst c = component(() => {\n\treturn (v) => html`<div class=${cls}/>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/90-hoist-class-identifier/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { cls } from \"css\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div class=\"${cls}\"></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/data/91-hoist-class-static-member/input.js",
    "content": "import { component, html } from \"ivi\";\nimport * as s from \"css\";\n\nconst c = component(() => {\n\treturn (v) => html`<div class=${s.cls}/>`;\n});"
  },
  {
    "path": "tests/compiler/module/data/91-hoist-class-static-member/output.js",
    "content": "import { component, html } from \"ivi\";\nimport * as s from \"css\";\nimport { _T, _hN, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nimport { dedupe as _dedupe } from \"oveo\";\nconst _TPL_ = __IVI_TPL__(_T(_dedupe(_hN(`<div class=\"${s.cls}\"></div>`)), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_);\n});\n"
  },
  {
    "path": "tests/compiler/module/module.test.ts",
    "content": "import { beforeEach, expect, test } from \"bun:test\";\nimport { readdir } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { TemplateCompiler } from \"@ivi/compiler\";\nimport { normalizeNewlines } from \"../normalize.js\";\n\nconst units = path.join(import.meta.dir, \"data\");\nconst entries = await readdir(units, { recursive: true });\nfor (const entry of entries) {\n  try {\n    const input = await Bun.file(path.join(units, entry, \"input.js\")).text();\n\n    test(`compiler/module/${entry}`, async () => {\n      const compiler = new TemplateCompiler({ oveo: true, dedupeStrings: true });\n      const output = Bun.file(path.join(units, entry, \"output.js\"));\n      const moduleResult = await compiler.transform(input, \"js\");\n      expect(normalizeNewlines(moduleResult.code)).toBe(normalizeNewlines(await output.text()));\n    });\n  } catch (err) {\n  }\n}\n"
  },
  {
    "path": "tests/compiler/module-all-disabled/data/01-event/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  @a=${v}\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module-all-disabled/data/01-event/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nconst _TPL_ = _T(_hE(\"div\"), 1, [6], _EMPTY_ARRAY, _EMPTY_ARRAY, [\"a\"]);\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module-all-disabled/data/02-hoist-return-fn/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => (v) => html`<a/>`);\n"
  },
  {
    "path": "tests/compiler/module-all-disabled/data/02-hoist-return-fn/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nconst _TPL_ = _T(_hE(\"a\"), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY);\nconst c = component(() => (v) => _t(_TPL_));\n"
  },
  {
    "path": "tests/compiler/module-all-disabled/module.test.ts",
    "content": "import { beforeEach, expect, test } from \"bun:test\";\nimport { readdir } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { TemplateCompiler } from \"@ivi/compiler\";\nimport { normalizeNewlines } from \"../normalize.js\";\n\nconst units = path.join(import.meta.dir, \"data\");\nconst entries = await readdir(units, { recursive: true });\nfor (const entry of entries) {\n  try {\n    const input = await Bun.file(path.join(units, entry, \"input.js\")).text();\n\n    test(`compiler/module-all-disabled/${entry}`, async () => {\n      const compiler = new TemplateCompiler({ oveo: false, dedupeStrings: false });\n      const output = Bun.file(path.join(units, entry, \"output.js\"));\n      const moduleResult = await compiler.transform(input, \"js\");\n      expect(normalizeNewlines(moduleResult.code)).toBe(normalizeNewlines(await output.text()));\n    });\n  } catch (err) {\n  }\n}\n"
  },
  {
    "path": "tests/compiler/module-oveo-disabled/data/01-event/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => {\n\treturn (v) => html`\n\t  <div\n\t\t  @a=${v}\n\t\t/>`;\n});\n"
  },
  {
    "path": "tests/compiler/module-oveo-disabled/data/01-event/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nconst _TPL_ = __IVI_TPL__(_T(_hE(\"div\"), 1, [6], _EMPTY_ARRAY, _EMPTY_ARRAY, [\"a\"]));\nconst c = component(() => {\n\treturn (v) => _t(_TPL_, [v]);\n});\n"
  },
  {
    "path": "tests/compiler/module-oveo-disabled/data/02-hoist-return-fn/input.js",
    "content": "import { component, html } from \"ivi\";\n\nconst c = component(() => (v) => html`<a/>`);\n"
  },
  {
    "path": "tests/compiler/module-oveo-disabled/data/02-hoist-return-fn/output.js",
    "content": "import { component, html } from \"ivi\";\nimport { _T, _hE, _t, EMPTY_ARRAY as _EMPTY_ARRAY } from \"ivi\";\nconst _TPL_ = __IVI_TPL__(_T(_hE(\"a\"), 1, _EMPTY_ARRAY, _EMPTY_ARRAY, _EMPTY_ARRAY));\nconst c = component(() => (v) => _t(_TPL_));\n"
  },
  {
    "path": "tests/compiler/module-oveo-disabled/module.test.ts",
    "content": "import { beforeEach, expect, test } from \"bun:test\";\nimport { readdir } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { TemplateCompiler } from \"@ivi/compiler\";\nimport { normalizeNewlines } from \"../normalize.js\";\n\n\nconst units = path.join(import.meta.dir, \"data\");\nconst entries = await readdir(units, { recursive: true });\nfor (const entry of entries) {\n  try {\n    const input = await Bun.file(path.join(units, entry, \"input.js\")).text();\n\n    test(`compiler/module-oveo-disabled/${entry}`, async () => {\n      const compiler = new TemplateCompiler({ oveo: false, dedupeStrings: true });\n      const output = Bun.file(path.join(units, entry, \"output.js\"));\n      const moduleResult = await compiler.transform(input, \"js\");\n      expect(normalizeNewlines(moduleResult.code)).toBe(normalizeNewlines(await output.text()));\n    });\n  } catch (err) {\n  }\n}\n"
  },
  {
    "path": "tests/compiler/normalize.ts",
    "content": "export const normalizeNewlines = process.platform == \"win32\"\n  ? (s: string) => s.replaceAll(\"\\r\\n\", \"\\n\")\n  : (s: string) => s;\n"
  },
  {
    "path": "tests/package.json",
    "content": "{\n  \"name\": \"tests\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@ivi/compiler\": \"workspace:^\"\n  }\n}\n"
  },
  {
    "path": "tests/runtime/array.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\n\ndescribe(\"arrays\", () => {\n  beforeEach(reset);\n\n  test(`[]`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [],\n        );\n      }),\n      [\n      ],\n    );\n  });\n\n  test(`[\"a\"]`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n          ],\n        );\n      }),\n      [\n        `createTextNode(\"a\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`[\"a\", \"b\"]`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n            \"b\",\n          ],\n        );\n      }),\n      [\n        `createTextNode(\"b\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n        `createTextNode(\"a\") => 3`,\n        `[1] Node.insertBefore(3, 2)`,\n      ],\n    );\n  });\n\n  test(`[\"a\", null, \"b\"]`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n            null,\n            \"b\",\n          ],\n        );\n      }),\n      [\n        `createTextNode(\"b\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n        `createTextNode(\"a\") => 3`,\n        `[1] Node.insertBefore(3, 2)`,\n      ],\n    );\n  });\n\n  test(`[] => []`, () => {\n    const root = createRoot();\n    root.update(\n      [],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [],\n        );\n      }),\n      [\n      ],\n    );\n  });\n\n  test(`[] => null`, () => {\n    const root = createRoot();\n    root.update(\n      [],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          null,\n        );\n      }),\n      [\n      ],\n    );\n  });\n\n  test(`[\"a\"] => []`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [],\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[\"a\", \"b\"] => []`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n        \"b\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [],\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n        `[1] Node.removeChild(3)`,\n      ],\n    );\n  });\n\n  test(`[\"a\", null, \"b\"] => []`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n        null,\n        \"b\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [],\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n        `[1] Node.removeChild(3)`,\n      ],\n    );\n  });\n\n  test(`[\"a\"] => null`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          null,\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[\"a\", \"b\"] => null`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n        \"b\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          null,\n        );\n      }),\n      [\n        `[1] Node.removeChild(3)`,\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[\"a\", null, \"b\"] => null`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n        null,\n        \"b\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          null,\n        );\n      }),\n      [\n        `[1] Node.removeChild(3)`,\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[] => [\"a\"]`, () => {\n    const root = createRoot();\n    root.update(\n      [],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n          ],\n        );\n      }),\n      [\n        `createTextNode(\"a\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`[] => [\"a\", \"b\"]`, () => {\n    const root = createRoot();\n    root.update(\n      [],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n            \"b\",\n          ],\n        );\n      }),\n      [\n        `createTextNode(\"b\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n        `createTextNode(\"a\") => 3`,\n        `[1] Node.insertBefore(3, 2)`,\n      ],\n    );\n  });\n\n  test(`[] => [\"a\", null, \"b\"]`, () => {\n    const root = createRoot();\n    root.update(\n      [],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n            null,\n            \"b\",\n          ],\n        );\n      }),\n      [\n        `createTextNode(\"b\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n        `createTextNode(\"a\") => 3`,\n        `[1] Node.insertBefore(3, 2)`,\n      ],\n    );\n  });\n\n  test(`[\"a\"] => [\"a\"]`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n          ],\n        );\n      }),\n      [\n      ],\n    );\n  });\n\n  test(`[\"a\", \"b\"] => [\"a\", \"b\"]`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n        \"b\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n            \"b\",\n          ],\n        );\n      }),\n      [\n      ],\n    );\n  });\n\n  test(`[\"a\", null, \"b\"] => [\"a\", null, \"b\"]`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n        null,\n        \"b\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n            null,\n            \"b\",\n          ],\n        );\n      }),\n      [\n      ],\n    );\n  });\n\n  test(`[\"a\"] => [\"b\"]`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"b\",\n          ],\n        );\n      }),\n      [\n        `[2] Node.nodeValue = \"b\"`,\n      ],\n    );\n  });\n\n  test(`[\"a\", null, \"c\"] => [\"a\", \"b\", \"c\"]`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n        null,\n        \"c\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n            \"b\",\n            \"c\",\n          ],\n        );\n      }),\n      [\n        `createTextNode(\"b\") => 4`,\n        `[1] Node.insertBefore(4, 2)`,\n      ],\n    );\n  });\n\n  test(`[\"a\", null, \"c\"] => [\"a\", null]`, () => {\n    const root = createRoot();\n    root.update(\n      [\n        \"a\",\n        null,\n        \"c\",\n      ],\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          [\n            \"a\",\n            null,\n          ],\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/component.test.ts",
    "content": "import { deepStrictEqual, ok, strictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport {\n  type Component, component, getProps, useUnmount, invalidate,\n} from \"ivi\";\n\ndescribe(\"component\", () => {\n  beforeEach(reset);\n\n  test(`() => null`, () => {\n    let _trace: string[] = [];\n    const t = component(() => {\n      _trace.push(\"create\");\n      return () => {\n        _trace.push(\"render\");\n        return null;\n      };\n    });\n    deepStrictEqual(_trace, []);\n\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          t(),\n        );\n      }),\n      [\n      ],\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n  });\n\n  test(`() => 1`, () => {\n    let _trace: string[] = [];\n    const t = component(() => {\n      _trace.push(\"create\");\n      return () => {\n        _trace.push(\"render\");\n        return 1;\n      };\n    });\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          t(),\n        );\n      }),\n      [\n        `createTextNode(1) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n  });\n\n  test(`({ a: 1 }) => a`, () => {\n    let _trace: string[] = [];\n    const t = component<{ a: number; }>(() => {\n      _trace.push(\"create\");\n      return ({ a }) => {\n        _trace.push(\"render\");\n        return a;\n      };\n    });\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          t({ a: 1 }),\n        );\n      }),\n      [\n        `createTextNode(1) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n  });\n\n  test(`update #1`, () => {\n    let _trace: string[] = [];\n    const t = component<{ a: number; }>(() => {\n      _trace.push(\"create\");\n      return ({ a }) => {\n        _trace.push(\"render\");\n        return a;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t({ a: 1 }),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          t({ a: 2 }),\n        );\n      }),\n      [\n        `[2] Node.nodeValue = 2`,\n      ],\n    );\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n\n  test(`update #2`, () => {\n    let _trace: string[] = [];\n    const t = component(() => {\n      _trace.push(\"create\");\n      return () => {\n        _trace.push(\"render\");\n        return 1;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n\n  test(`update: areEqual => false`, () => {\n    let _trace: string[] = [];\n    const t = component(() => {\n      _trace.push(\"create\");\n      return () => {\n        _trace.push(\"render\");\n        return 1;\n      };\n    }, () => false);\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n\n  test(`update: areEqual => true`, () => {\n    let _trace: string[] = [];\n    const t = component<{ a: number; }>(() => {\n      _trace.push(\"create\");\n      return ({ a }) => {\n        _trace.push(\"render\");\n        return a;\n      };\n    }, (a, b) => {\n      _trace.push(`areEqual(${a.a}, ${b.a})`);\n      return true;\n    });\n    const root = createRoot();\n    root.update(\n      t({ a: 1 }),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    root.update(\n      t({ a: 2 }),\n    );\n    deepStrictEqual(_trace, [\"areEqual(1, 2)\"]);\n  });\n\n  test(`forceUpdate: areEqual => true`, () => {\n    let _trace: string[] = [];\n    const t = component<{ a: number; }>(() => {\n      _trace.push(\"create\");\n      return ({ a }) => {\n        _trace.push(\"render\");\n        return a;\n      };\n    }, () => true);\n    const root = createRoot();\n    root.update(\n      t({ a: 1 }),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    root.update(\n      t({ a: 2 }),\n      true,\n    );\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n\n  test(`dirtyCheck #1`, () => {\n    let _trace: string[] = [];\n    const t = component<{ a: number; }>(() => {\n      _trace.push(\"create\");\n      return ({ a }) => {\n        _trace.push(\"render\");\n        return a;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t({ a: 1 }),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    root.dirtyCheck();\n    deepStrictEqual(_trace, []);\n  });\n\n  test(`dirtyCheck: areEqual => false`, () => {\n    let _trace: string[] = [];\n    const t = component<{ a: number; }>(() => {\n      _trace.push(\"create\");\n      return ({ a }) => {\n        _trace.push(\"render\");\n        return a;\n      };\n    }, () => false);\n    const root = createRoot();\n    root.update(\n      t({ a: 1 }),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    root.dirtyCheck();\n    deepStrictEqual(_trace, []);\n  });\n\n  test(`dirtyCheck: areEqual => true`, () => {\n    let _trace: string[] = [];\n    const t = component<{ a: number; }>(() => {\n      _trace.push(\"create\");\n      return ({ a }) => {\n        _trace.push(\"render\");\n        return a;\n      };\n    }, () => true);\n    const root = createRoot();\n    root.update(\n      t({ a: 1 }),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    root.dirtyCheck();\n    deepStrictEqual(_trace, []);\n  });\n\n  test(`dirtyCheck+forceUpdate: areEqual => true`, () => {\n    let _trace: string[] = [];\n    const t = component<{ a: number; }>(() => {\n      _trace.push(\"create\");\n      return ({ a }) => {\n        _trace.push(\"render\");\n        return a;\n      };\n    }, () => true);\n    const root = createRoot();\n    root.update(\n      t({ a: 1 }),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    root.dirtyCheck(true);\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n\n  test(`unmount #1`, () => {\n    let _trace: string[] = [];\n    const t = component(() => {\n      _trace.push(\"create\");\n      return () => {\n        _trace.push(\"render\");\n        return 1;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          null,\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n  });\n\n  test(`getProps`, () => {\n    let _c: Component;\n    const t = component<{ a: number; }>((c) => {\n      _c = c;\n      return ({ a }) => {\n        return a;\n      };\n    });\n    const root = createRoot();\n    const props1 = { a: 0 };\n    const props2 = { a: 1 };\n    root.update(\n      t(props1),\n    );\n    strictEqual(getProps(_c!), props1);\n    root.update(\n      t(props2),\n    );\n    strictEqual(getProps(_c!), props2);\n  });\n\n  test(`useUnmount 1`, () => {\n    let _unmounted = 0;\n    const t = component((c) => {\n      useUnmount(c, () => {\n        _unmounted++;\n      });\n      return () => null;\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    strictEqual(_unmounted, 0);\n    root.update(\n      null,\n    );\n    strictEqual(_unmounted, 1);\n  });\n\n  test(`useUnmount 2`, () => {\n    let _unmounted: number[] = [];\n    const t = component((c) => {\n      useUnmount(c, () => {\n        _unmounted.push(0);\n      });\n      useUnmount(c, () => {\n        _unmounted.push(1);\n      });\n      return () => null;\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_unmounted, []);\n    root.update(\n      null,\n    );\n    deepStrictEqual(_unmounted, [0, 1]);\n  });\n\n  test(`useUnmount 3`, () => {\n    let _unmounted: number[] = [];\n    const t = component((c) => {\n      useUnmount(c, () => {\n        _unmounted.push(0);\n      });\n      useUnmount(c, () => {\n        _unmounted.push(1);\n      });\n      useUnmount(c, () => {\n        _unmounted.push(2);\n      });\n      return () => null;\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_unmounted, []);\n    root.update(\n      null,\n    );\n    deepStrictEqual(_unmounted, [0, 1, 2]);\n  });\n\n  test(`invalidate 1`, () => {\n    let _trace: string[] = [];\n    let _c: Component;\n    const t = component((c) => {\n      _trace.push(\"create\");\n      _c = c;\n      return () => {\n        _trace.push(\"render\");\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    invalidate(_c!);\n    deepStrictEqual(_trace, []);\n    ok(root.isDirty);\n    root.dirtyCheck();\n    ok(!root.isDirty);\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n\n  test(`invalidate 2`, () => {\n    let _trace: string[] = [];\n    let _c: Component;\n    const t = component((c) => {\n      _trace.push(\"create\");\n      _c = c;\n      return () => {\n        _trace.push(\"render\");\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    invalidate(_c!);\n    invalidate(_c!);\n    deepStrictEqual(_trace, []);\n    ok(root.isDirty);\n    root.dirtyCheck();\n    ok(!root.isDirty);\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n\n  test(`invalidate 3`, () => {\n    let _trace: string[] = [];\n    let _c: Component;\n    const t = component((c) => {\n      _trace.push(\"create\");\n      _c = c;\n      return () => {\n        _trace.push(\"render\");\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n    invalidate(_c!);\n    deepStrictEqual(_trace, []);\n    ok(root.isDirty);\n    root.dirtyCheck();\n    deepStrictEqual(_trace, [\"render\"]);\n    _trace = [];\n    invalidate(_c!);\n    deepStrictEqual(_trace, []);\n    ok(root.isDirty);\n    root.dirtyCheck();\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n});\n"
  },
  {
    "path": "tests/runtime/containsDOMElement.test.ts",
    "content": "import { ok } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset } from \"@ivi/mock-dom/global\";\nimport {\n  type VAny, containsDOMElement, component, context, List,\n} from \"ivi\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"containsDOMElement\", () => {\n  beforeEach(reset);\n\n  const DEEP = (ref: (element: Element) => void) => html`\n    <div>\n      <span ${ref}/>\n    </div>\n  `;\n\n  test(`null`, () => {\n    const root = createRoot();\n    const el = document.createElement(\"div\");\n    root.update(null);\n    ok(!containsDOMElement(root.root, el));\n  });\n\n  test(`element 1`, () => {\n    const root = createRoot();\n    const el = document.createElement(\"div\");\n    root.update(html`<div/>`);\n    ok(!containsDOMElement(root.root, el));\n  });\n\n  test(`element 2`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    root.update(DEEP(ref));\n    ok(containsDOMElement(root.root, el!));\n  });\n\n  test(`array`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    root.update([\n      \"a\",\n      DEEP(ref),\n      \"b\",\n    ]);\n    ok(containsDOMElement(root.root, el!));\n  });\n\n  test(`component`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    const T = component<VAny>(() => (child) => child);\n    root.update(T(DEEP(ref)));\n    ok(containsDOMElement(root.root, el!));\n  });\n\n  test(`context`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    const [_, provider] = context<number>();\n    root.update(provider(1, DEEP(ref)));\n    ok(containsDOMElement(root.root, el!));\n  });\n\n  test(`List`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    const r = (c: VAny) => c;\n    root.update(\n      List([\n        1,\n        DEEP(ref),\n        2,\n      ], r, r),\n    );\n    ok(containsDOMElement(root.root, el!));\n  });\n});\n"
  },
  {
    "path": "tests/runtime/context.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { component, context } from \"ivi\";\n\ndescribe(\"context\", () => {\n  beforeEach(reset);\n\n  test(`undefined`, () => {\n    const [get, _provider] = context<number>();\n\n    const t = component((c) => {\n      return () => {\n        return get(c) ?? \"undefined\";\n      };\n    });\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          t(),\n        );\n      }),\n      [\n        `createTextNode(\"undefined\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`basic`, () => {\n    const [get, provider] = context<number>();\n\n    const t = component((c) => {\n      return () => {\n        return get(c);\n      };\n    });\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider(1, t()),\n        );\n      }),\n      [\n        `createTextNode(1) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider(2, t()),\n        );\n      }),\n      [\n        `[2] Node.nodeValue = 2`,\n      ],\n    );\n  });\n\n  test(`nested #1`, () => {\n    const [get, provider] = context<number>();\n\n    const t = component((c) => {\n      return () => {\n        return get(c);\n      };\n    });\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider(0,\n            provider(1, t()),\n          ),\n        );\n      }),\n      [\n        `createTextNode(1) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider(0,\n            provider(2, t()),\n          )\n        );\n      }),\n      [\n        `[2] Node.nodeValue = 2`,\n      ],\n    );\n  });\n\n  test(`nested #2`, () => {\n    const [get1, provider1] = context<number>();\n    const [get2, provider2] = context<number>();\n\n    const t = component((c) => {\n      return () => {\n        return `${get1(c)}|${get2(c)}`;\n      };\n    });\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider1(1,\n            provider2(10, t()),\n          ),\n        );\n      }),\n      [\n        `createTextNode(\"1|10\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider1(2,\n            provider2(20, t()),\n          )\n        );\n      }),\n      [\n        `[2] Node.nodeValue = \"2|20\"`,\n      ],\n    );\n  });\n\n  test(`nested #3`, () => {\n    const [_get1, provider1] = context<number>();\n    const [get2, _provider2] = context<number>();\n\n    const t = component((c) => {\n      return () => {\n        return get2(c) ?? \"undefined\";\n      };\n    });\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider1(1, t()),\n        );\n      }),\n      [\n        `createTextNode(\"undefined\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`forceUpdate`, () => {\n    const [get, provider] = context<number>();\n    let _trace: string[] = [];\n    const t = component((c) => {\n      return () => {\n        _trace.push(\"render\");\n        return get(c);\n      };\n    }, () => true);\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider(1, t()),\n        );\n      }),\n      [\n        `createTextNode(1) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n    deepStrictEqual(_trace, [\"render\"]);\n    _trace = [];\n\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider(1, t()),\n        );\n      }),\n      [\n      ],\n    );\n\n    deepStrictEqual(_trace, []);\n\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          provider(2, t()),\n        );\n      }),\n      [\n        `[2] Node.nodeValue = 2`,\n      ],\n    );\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n});\n"
  },
  {
    "path": "tests/runtime/equal.test.ts",
    "content": "import { strictEqual } from \"node:assert\";\nimport { describe, test } from \"bun:test\";\nimport {\n  preventUpdates, strictEq, shallowEq, shallowEqArray,\n} from \"ivi\";\n\ndescribe(\"equal\", () => {\n  test(\"preventUpdates\", () => {\n    strictEqual(preventUpdates(0, 1), true);\n  });\n\n  describe(\"strictEq\", () => {\n    test(\"same instance\", () => {\n      const a = { a: 1 };\n      strictEqual(strictEq(a, a), true);\n    });\n\n    test(\"{a:1} !== {a:1}\", () => {\n      strictEqual(strictEq({ a: 1 }, { a: 1 }), false);\n    });\n  });\n\n  describe(\"shallowEq\", () => {\n    test(\"same instance\", () => {\n      const a = { a: 1 };\n      strictEqual(shallowEq(a, a), true);\n    });\n\n    test(\"{a:1} === {a:1} (different instances)\", () => {\n      strictEqual(shallowEq({ a: 1 }, { a: 1 }), true);\n    });\n\n    test(\"{a:1} !== {a:2}\", () => {\n      strictEqual(shallowEq({ a: 1 }, { a: 2 }), false);\n    });\n\n    test(\"{a, b} !== {a}\", () => {\n      strictEqual(shallowEq({ a: 1, b: 2 }, { a: 1 }), false);\n    });\n  });\n\n  describe(\"shallowEqArray\", () => {\n    test(\"same instance\", () => {\n      const a = [1];\n      strictEqual(shallowEqArray(a, a), true);\n    });\n\n    test(\"[1] === [1] (different instances)\", () => {\n      strictEqual(shallowEqArray([1], [1]), true);\n    });\n\n    test(\"[1] !== [2]\", () => {\n      strictEqual(shallowEqArray([1], [2]), false);\n    });\n\n    test(\"[1, 2] !== [1]\", () => {\n      strictEqual(shallowEqArray([1, 2], [1]), false);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/runtime/findDOMNode.test.ts",
    "content": "import { strictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset } from \"@ivi/mock-dom/global\";\nimport { component, context, List } from \"ivi\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"findDOMNode\", () => {\n  beforeEach(reset);\n\n  test(`null`, () => {\n    const root = createRoot();\n    root.update(\n      null,\n    );\n    strictEqual(root.findDOMNode(), null);\n  });\n\n  test(`\"1\"`, () => {\n    const root = createRoot();\n    root.update(\n      \"1\",\n    );\n    strictEqual((root.findDOMNode() as any).uid, 2);\n  });\n\n  test(`[]`, () => {\n    const root = createRoot();\n    root.update(\n      [],\n    );\n    strictEqual(root.findDOMNode(), null);\n  });\n\n  test(`[0, 1]`, () => {\n    const root = createRoot();\n    root.update(\n      [0, 1],\n    );\n    strictEqual((root.findDOMNode() as any).uid, 3);\n  });\n\n  test(`[0, null, 1]`, () => {\n    const root = createRoot();\n    root.update(\n      [0, null, 1],\n    );\n    strictEqual((root.findDOMNode() as any).uid, 3);\n  });\n\n  test(`div`, () => {\n    const root = createRoot();\n    root.update(\n      html`<div/>`,\n    );\n    strictEqual((root.findDOMNode() as any).uid, 2);\n  });\n\n  test(`component`, () => {\n    const root = createRoot();\n    const T = component(() => () => 1);\n    root.update(\n      T(),\n    );\n    strictEqual((root.findDOMNode() as any).uid, 2);\n  });\n\n  test(`context`, () => {\n    const root = createRoot();\n    const [_, provider] = context<number>();\n    root.update(\n      provider(0, 1),\n    );\n    strictEqual((root.findDOMNode() as any).uid, 2);\n  });\n\n  test(`List`, () => {\n    const root = createRoot();\n    const r = (i: number) => i;\n    root.update(\n      List([0, 1], r, r),\n    );\n    strictEqual((root.findDOMNode() as any).uid, 3);\n  });\n});\n"
  },
  {
    "path": "tests/runtime/hasDOMElement.test.ts",
    "content": "import { ok } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset } from \"@ivi/mock-dom/global\";\nimport {\n  type VAny, hasDOMElement, component, context, List,\n} from \"ivi\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"hasDOMElement\", () => {\n  beforeEach(reset);\n\n  const ROOT = (ref: (element: Element) => void) => html`\n    <div ${ref}>\n      <span/>\n    </div>\n  `;\n\n  const DEEP = (ref: (element: Element) => void) => html`\n    <div>\n      <span ${ref}/>\n    </div>\n  `;\n\n  test(`null`, () => {\n    const root = createRoot();\n    const el = document.createElement(\"div\");\n    root.update(null);\n    ok(!hasDOMElement(root.root, el));\n  });\n\n  test(`element 1`, () => {\n    const root = createRoot();\n    const el = document.createElement(\"div\");\n    root.update(html`<div/>`);\n    ok(!hasDOMElement(root.root, el));\n  });\n\n  test(`element 2`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    root.update(DEEP(ref));\n    ok(!hasDOMElement(root.root, el!));\n  });\n\n  test(`element 3`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    root.update(ROOT(ref));\n    ok(hasDOMElement(root.root, el!));\n  });\n\n  test(`array 1`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    root.update([\n      \"a\",\n      DEEP(ref),\n      \"b\",\n    ]);\n    ok(!hasDOMElement(root.root, el!));\n  });\n\n  test(`array 2`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    root.update([\n      \"a\",\n      ROOT(ref),\n      \"b\",\n    ]);\n    ok(hasDOMElement(root.root, el!));\n  });\n\n  test(`component 1`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    const T = component<VAny>(() => (child) => child);\n    root.update(T(DEEP(ref)));\n    ok(!hasDOMElement(root.root, el!));\n  });\n\n  test(`component 2`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    const T = component<VAny>(() => (child) => child);\n    root.update(T(ROOT(ref)));\n    ok(hasDOMElement(root.root, el!));\n  });\n\n  test(`context 1`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    const [_, provider] = context<number>();\n    root.update(provider(1, DEEP(ref)));\n    ok(!hasDOMElement(root.root, el!));\n  });\n\n  test(`context 2`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    const [_, provider] = context<number>();\n    root.update(provider(1, ROOT(ref)));\n    ok(hasDOMElement(root.root, el!));\n  });\n\n  test(`List 1`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    const r = (c: VAny) => c;\n    root.update(\n      List([\n        1,\n        DEEP(ref),\n        2,\n      ], r, r),\n    );\n    ok(!hasDOMElement(root.root, el!));\n  });\n\n  test(`List 2`, () => {\n    const root = createRoot();\n    let el: Element;\n    const ref = (element: Element) => { el = element; };\n    const r = (c: VAny) => c;\n    root.update(\n      List([\n        1,\n        ROOT(ref),\n        2,\n      ], r, r),\n    );\n    ok(hasDOMElement(root.root, el!));\n  });\n});\n"
  },
  {
    "path": "tests/runtime/hole.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\n\ndescribe(\"hole\", () => {\n  beforeEach(reset);\n\n  test(`null`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(null); }),\n      [],\n    );\n  });\n\n  test(`undefined`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(void 0); }),\n      [],\n    );\n  });\n\n  test(`false`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(false); }),\n      [],\n    );\n  });\n\n  test(`\"a\" => null => \"b\"`, () => {\n    const root = createRoot();\n    root.update(\"a\");\n    deepStrictEqual(\n      trace(() => { root.update(null); }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n\n    deepStrictEqual(\n      trace(() => { root.update(\"b\"); }),\n      [\n        `createTextNode(\"b\") => 3`,\n        `[1] Node.insertBefore(3, null)`,\n      ],\n    );\n  });\n\n  test(`\"a\" => undefined => \"b\"`, () => {\n    const root = createRoot();\n    root.update(\"a\");\n    deepStrictEqual(\n      trace(() => { root.update(void 0); }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n\n    deepStrictEqual(\n      trace(() => { root.update(\"b\"); }),\n      [\n        `createTextNode(\"b\") => 3`,\n        `[1] Node.insertBefore(3, null)`,\n      ],\n    );\n  });\n\n  test(`\"a\" => false => \"b\"`, () => {\n    const root = createRoot();\n    root.update(\"a\");\n    deepStrictEqual(\n      trace(() => { root.update(false); }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n\n    deepStrictEqual(\n      trace(() => { root.update(\"b\"); }),\n      [\n        `createTextNode(\"b\") => 3`,\n        `[1] Node.insertBefore(3, null)`,\n      ],\n    );\n  });\n\n  test(`\"a\" => \"\" => \"b\"`, () => {\n    const root = createRoot();\n    root.update(\"a\");\n    deepStrictEqual(\n      trace(() => { root.update(\"\"); }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n\n    deepStrictEqual(\n      trace(() => { root.update(\"b\"); }),\n      [\n        `createTextNode(\"b\") => 3`,\n        `[1] Node.insertBefore(3, null)`,\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/html/parser.test.ts",
    "content": "import { deepStrictEqual, } from \"node:assert\";\nimport { describe, test } from \"bun:test\";\nimport { parseTemplate } from \"ivi/html/parser\";\nimport {\n  type INodeText, type INodeExpr, type INodeElement, type INode,\n  type IProperty, type IPropertyAttribute, type IPropertyValue,\n  type IPropertyDOMValue, type IPropertyStyle, type IPropertyEvent,\n  type IPropertyDirective,\n  TEMPLATE_TYPE_HTM,\n  NODE_TYPE_EXPR,\n  NODE_TYPE_TEXT,\n  NODE_TYPE_ELEMENT,\n  PROPERTY_TYPE_ATTRIBUTE,\n  PROPERTY_TYPE_VALUE,\n  PROPERTY_TYPE_DOMVALUE,\n  PROPERTY_TYPE_STYLE,\n  PROPERTY_TYPE_EVENT,\n  PROPERTY_TYPE_DIRECTIVE,\n} from \"ivi/template/ir\";\n\nconst _ = void 0;\n\nconst ATTR = (key: string, value: string | boolean | number): IPropertyAttribute => ({\n  type: PROPERTY_TYPE_ATTRIBUTE,\n  key,\n  value,\n});\n\nconst PROP = (key: string, value: number): IPropertyValue => ({\n  type: PROPERTY_TYPE_VALUE,\n  key,\n  value,\n  hoist: false,\n});\n\nconst DPROP = (key: string, value: number): IPropertyDOMValue => ({\n  type: PROPERTY_TYPE_DOMVALUE,\n  key,\n  value,\n  hoist: false,\n});\n\nconst STYLE = (key: string, value: number | string): IPropertyStyle => ({\n  type: PROPERTY_TYPE_STYLE,\n  key,\n  value,\n  hoist: false,\n});\n\nconst EVENT = (key: string, value: number): IPropertyEvent => ({\n  type: PROPERTY_TYPE_EVENT,\n  key,\n  value,\n  hoist: false,\n});\n\nconst DIRECTIVE = (\n  value: number,\n): IPropertyDirective => ({\n  type: PROPERTY_TYPE_DIRECTIVE,\n  key: null,\n  value,\n  hoist: false,\n});\n\nconst E = (tag: string, properties: IProperty[] = [], children: INode[] = []): INodeElement => ({\n  type: NODE_TYPE_ELEMENT,\n  tag,\n  properties,\n  children,\n});\n\nconst T = (value: string): INodeText => ({\n  type: NODE_TYPE_TEXT,\n  value,\n});\n\nconst X = (value: number): INodeExpr => ({\n  type: NODE_TYPE_EXPR,\n  value,\n});\n\ndescribe(\"@ivi/htm/parser\", () => {\n  test(`a`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`a`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          T(\"a\"),\n        ],\n      },\n    );\n  });\n\n  test(`<a/>`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<a/>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\"),\n        ],\n      },\n    );\n  });\n\n  test(`<a></a>`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<a></a>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\"),\n        ],\n      },\n    );\n  });\n\n  test(`<a>a</a>`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<a>a</a>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            T(\"a\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`<a>{0}</a>`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<a>`, `</a>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            X(0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`<a>a{0}</a>`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<a>a`, `</a>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            T(\"a\"), X(0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`<a>{0}b</a>`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<a>`, `b</a>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            X(0), T(\"b\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`<a>a{0}b</a>`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<a>a`, `b</a>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            T(\"a\"), X(0), T(\"b\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`a<a/>b`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`a<a/>b`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          T(\"a\"),\n          E(\"a\"),\n          T(\"b\"),\n        ],\n      },\n    );\n  });\n\n  test(`a{0}b`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`a`, `b`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          T(\"a\"),\n          X(0),\n          T(\"b\"),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #1`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <a>\n          </a>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\"),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #2`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <a>\n\n          </a>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\"),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #3`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <a> </a>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [T(\" \")]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #4`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div>\n            <a></a>\n            <b></b>\n          </div>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", _, [\n            E(\"a\"),\n            E(\"b\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #5`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div>\n            a\n          </div>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", _, [\n            T(\"a\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #6`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div>\n            a\n            b\n          </div>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", _, [\n            T(\"a b\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #7`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div>\n            a  b\n          </div>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", _, [\n            T(\"a b\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #8`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div>  a  b  </div>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", _, [\n            T(\" a b \"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #9`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div>\n            \\va\n          </div>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", _, [\n            T(\" a\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #10`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div>\n            a\\v\n          </div>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", _, [\n            T(\"a \"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #11`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div>\n            a\n            <a>\n              b\n            </a>\n          </div>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", _, [\n            T(\"a\"),\n            E(\"a\", _, [T(\"b\")]),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #12`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `<a> `,\n          `</a>`,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            T(\" \"),\n            X(0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #13`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `<a>`,\n          ` </a>`,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            X(0),\n            T(\" \"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #14`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `<a> `,\n          ` </a>`,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            T(\" \"),\n            X(0),\n            T(\" \"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #15`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `<a> `,\n          ` `,\n          ` </a>`,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            T(\" \"),\n            X(0),\n            T(\" \"),\n            X(1),\n            T(\" \"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #16`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `<a>\n          `,\n          `\n          </a>`,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            X(0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #17`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `<a>\n          \\v`,\n          `\\v\n          </a>`,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            T(\" \"),\n            X(0),\n            T(\" \"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`whitespace #18`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `<a> `, ` `, ` </a>`\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"a\", _, [\n            T(\" \"),\n            X(0),\n            T(\" \"),\n            X(1),\n            T(\" \"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`attr #1`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div a/>\n          `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            ATTR(\"a\", true),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`attr #2`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div a=\"1\"/>\n          `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            ATTR(\"a\", \"1\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`attr #3`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div a=`, `/>\n          `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            ATTR(\"a\", 0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`attr #4`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div\n             a=\"1\"\n             b=\"2\"\n          />\n          `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            ATTR(\"a\", \"1\"),\n            ATTR(\"b\", \"2\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`attr #5`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div\n             a=\"1\"\n             b=\"2\"\n          ></div>\n          `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            ATTR(\"a\", \"1\"),\n            ATTR(\"b\", \"2\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`.prop`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<div .a=`, `/>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            PROP(\"a\", 0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`*prop`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<div *a=`, `/>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            DPROP(\"a\", 0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`~style=\"0\"`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<div ~a=\"0\"/>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            STYLE(\"a\", \"0\"),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`~style={0}`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<div ~a=`, `/>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            STYLE(\"a\", 0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`@event={0}`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<div @a=`, `/>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            EVENT(\"a\", 0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`shorthand directive syntax`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [`<div `, `/>`],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", [\n            DIRECTIVE(0),\n          ]),\n        ],\n      },\n    );\n  });\n\n  test(`comments`, () => {\n    deepStrictEqual(\n      parseTemplate(\n        [\n          `\n          <div>\n            <!-- comment -->\n            a\n            <!>\n          </div>\n        `,\n        ],\n        TEMPLATE_TYPE_HTM,\n      ),\n      {\n        type: TEMPLATE_TYPE_HTM,\n        children: [\n          E(\"div\", _, [\n            T(\"a\"),\n          ]),\n        ],\n      },\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/list.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { List, component } from \"ivi\";\nimport { html } from \"ivi\";\n\nconst r = (i: number) => i;\n\ndescribe(\"List\", () => {\n  beforeEach(reset);\n\n  test(`[]`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0], r, r),\n        );\n      }),\n      [\n        `createTextNode(0) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`[0]`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0], r, r),\n        );\n      }),\n      [\n        `createTextNode(0) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`[0, 1]`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1], r, r),\n        );\n      }),\n      [\n        `createTextNode(1) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n        `createTextNode(0) => 3`,\n        `[1] Node.insertBefore(3, 2)`,\n      ],\n    );\n  });\n\n  test(`[] => []`, () => {\n    const root = createRoot();\n    root.update(\n      List([], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([], r, r),\n        );\n      }),\n      [\n      ],\n    );\n  });\n\n  test(`[0] => [0]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0], r, r),\n        );\n      }),\n      [\n      ],\n    );\n  });\n\n  test(`[0] => [1]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n        `createTextNode(1) => 3`,\n        `[1] Node.insertBefore(3, null)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2] => [0, 1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2], r, r),\n        );\n      }),\n      [\n\n      ],\n    );\n  });\n\n  test(`[] => [0]`, () => {\n    const root = createRoot();\n    root.update(\n      List([], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0], r, r),\n        );\n      }),\n      [\n        `createTextNode(0) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`[] => [0, 1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2], r, r),\n        );\n      }),\n      [\n        `createTextNode(2) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n        `createTextNode(1) => 3`,\n        `[1] Node.insertBefore(3, 2)`,\n        `createTextNode(0) => 4`,\n        `[1] Node.insertBefore(4, 3)`,\n      ],\n    );\n  });\n\n  test(`[1] => [0, 1]`, () => {\n    const root = createRoot();\n    root.update(\n      List([1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1], r, r),\n        );\n      }),\n      [\n        `createTextNode(0) => 3`,\n        `[1] Node.insertBefore(3, 2)`,\n      ],\n    );\n  });\n\n  test(`[0] => [0, 1]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1], r, r),\n        );\n      }),\n      [\n        `createTextNode(1) => 3`,\n        `[1] Node.insertBefore(3, null)`,\n      ],\n    );\n  });\n\n  test(`[2] => [0, 1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2], r, r),\n        );\n      }),\n      [\n        `createTextNode(1) => 3`,\n        `[1] Node.insertBefore(3, 2)`,\n        `createTextNode(0) => 4`,\n        `[1] Node.insertBefore(4, 3)`,\n      ],\n    );\n  });\n\n  test(`[0] => [0, 1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2], r, r),\n        );\n      }),\n      [\n        `createTextNode(2) => 3`,\n        `[1] Node.insertBefore(3, null)`,\n        `createTextNode(1) => 4`,\n        `[1] Node.insertBefore(4, 3)`,\n      ],\n    );\n  });\n\n  test(`[1] => [0, 1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2], r, r),\n        );\n      }),\n      [\n        `createTextNode(2) => 3`,\n        `[1] Node.insertBefore(3, null)`,\n        `createTextNode(0) => 4`,\n        `[1] Node.insertBefore(4, 2)`,\n      ],\n    );\n  });\n\n  test(`[2] => [0, 1, 2, 3, 4]`, () => {\n    const root = createRoot();\n    root.update(\n      List([2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 3, 4], r, r),\n        );\n      }),\n      [\n        `createTextNode(4) => 3`,\n        `[1] Node.insertBefore(3, null)`,\n        `createTextNode(3) => 4`,\n        `[1] Node.insertBefore(4, 3)`,\n        `createTextNode(1) => 5`,\n        `[1] Node.insertBefore(5, 2)`,\n        `createTextNode(0) => 6`,\n        `[1] Node.insertBefore(6, 5)`,\n      ],\n    );\n  });\n\n  test(`[1, 2] => [0, 1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([1, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2], r, r),\n        );\n      }),\n      [\n        `createTextNode(0) => 4`,\n        `[1] Node.insertBefore(4, 3)`,\n      ],\n    );\n  });\n\n  test(`[0, 2] => [0, 1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2], r, r),\n        );\n      }),\n      [\n        `createTextNode(1) => 4`,\n        `[1] Node.insertBefore(4, 2)`,\n      ],\n    );\n  });\n\n  test(`[2, 3] => [0, 1, 2, 3]`, () => {\n    const root = createRoot();\n    root.update(\n      List([2, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 3], r, r),\n        );\n      }),\n      [\n        `createTextNode(1) => 4`,\n        `[1] Node.insertBefore(4, 3)`,\n        `createTextNode(0) => 5`,\n        `[1] Node.insertBefore(5, 4)`,\n      ],\n    );\n  });\n\n  test(`[0, 1] => [0, 1, 2, 3]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 3], r, r),\n        );\n      }),\n      [\n        `createTextNode(3) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n        `createTextNode(2) => 5`,\n        `[1] Node.insertBefore(5, 4)`,\n      ],\n    );\n  });\n\n  test(`[1, 3] => [0, 1, 2, 3, 4]`, () => {\n    const root = createRoot();\n    root.update(\n      List([1, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 3, 4], r, r),\n        );\n      }),\n      [\n        `createTextNode(4) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n        `createTextNode(2) => 5`,\n        `[1] Node.insertBefore(5, 2)`,\n        `createTextNode(0) => 6`,\n        `[1] Node.insertBefore(6, 3)`,\n      ],\n    );\n  });\n\n  test(`[2, 5] => [0, 1, 2, 3, 4, 5]`, () => {\n    const root = createRoot();\n    root.update(\n      List([2, 5], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 3, 4, 5], r, r),\n        );\n      }),\n      [\n        `createTextNode(4) => 4`,\n        `[1] Node.insertBefore(4, 2)`,\n        `createTextNode(3) => 5`,\n        `[1] Node.insertBefore(5, 4)`,\n        `createTextNode(1) => 6`,\n        `[1] Node.insertBefore(6, 3)`,\n        `createTextNode(0) => 7`,\n        `[1] Node.insertBefore(7, 6)`,\n      ],\n    );\n  });\n\n  test(`[1, 3] => [0, 1, 2, 3]`, () => {\n    const root = createRoot();\n    root.update(\n      List([1, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 3], r, r),\n        );\n      }),\n      [\n        `createTextNode(2) => 4`,\n        `[1] Node.insertBefore(4, 2)`,\n        `createTextNode(0) => 5`,\n        `[1] Node.insertBefore(5, 3)`,\n      ],\n    );\n  });\n\n  test(`[0, 2] => [0, 1, 2, 3]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 3], r, r),\n        );\n      }),\n      [\n        `createTextNode(3) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n        `createTextNode(1) => 5`,\n        `[1] Node.insertBefore(5, 2)`,\n      ],\n    );\n  });\n\n  test(`[2, 5] => [0, 1, 2, 3, 4, 5]`, () => {\n    const root = createRoot();\n    root.update(\n      List([2, 5], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 3, 4, 5], r, r),\n        );\n      }),\n      [\n        `createTextNode(4) => 4`,\n        `[1] Node.insertBefore(4, 2)`,\n        `createTextNode(3) => 5`,\n        `[1] Node.insertBefore(5, 4)`,\n        `createTextNode(1) => 6`,\n        `[1] Node.insertBefore(6, 3)`,\n        `createTextNode(0) => 7`,\n        `[1] Node.insertBefore(7, 6)`,\n      ],\n    );\n  });\n\n  test(`[0, 3] => [0, 1, 2, 3, 4, 5]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 3, 4, 5], r, r),\n        );\n      }),\n      [\n        `createTextNode(5) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n        `createTextNode(4) => 5`,\n        `[1] Node.insertBefore(5, 4)`,\n        `createTextNode(2) => 6`,\n        `[1] Node.insertBefore(6, 2)`,\n        `createTextNode(1) => 7`,\n        `[1] Node.insertBefore(7, 6)`,\n      ],\n    );\n  });\n\n  test(`[0] => []`, () => {\n    const root = createRoot();\n    root.update(\n      List([0], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[0, 1] => [1]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(3)`,\n      ],\n    );\n  });\n\n  test(`[0, 1] => [0]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2] => [1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1, 2], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(4)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2] => [0, 1]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2] => [0, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 2], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(3)`,\n        `[1] Node.removeChild(2)`,\n        `createTextNode(2) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n  });\n\n  test(`[0, 1] => []`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(3)`,\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2] => [2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([2], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(4)`,\n        `[1] Node.removeChild(3)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2] => [0, 1]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2, 3] => [2, 3]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([2, 3], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(5)`,\n        `[1] Node.removeChild(4)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2, 3] => [0, 1]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(3)`,\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2, 3] => [1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1, 2], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(5)`,\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2, 3] => [0, 3]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 3], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(4)`,\n        `[1] Node.removeChild(3)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2, 3, 4, 5] => [0, 1, 2, 4]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2, 3, 4, 5], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 1, 2, 4], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(4)`,\n        `[1] Node.removeChild(2)`,\n      ],\n    );\n  });\n\n  test(`[0] => [1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1, 2], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(2)`,\n        `createTextNode(2) => 3`,\n        `[1] Node.insertBefore(3, null)`,\n        `createTextNode(1) => 4`,\n        `[1] Node.insertBefore(4, 3)`,\n      ],\n    );\n  });\n\n  test(`[0, 1] => [2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([2], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(3)`,\n        `[1] Node.removeChild(2)`,\n        `createTextNode(2) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n      ],\n    );\n  });\n\n  test(`[0, 2] => [1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1, 2], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(3)`,\n        `createTextNode(1) => 4`,\n        `[1] Node.insertBefore(4, 2)`,\n      ],\n    );\n  });\n\n  test(`[0, 1] => [1, 2]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1, 2], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(3)`,\n        `createTextNode(2) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2] => [3, 4, 5]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([3, 4, 5], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(4)`,\n        `[1] Node.removeChild(3)`,\n        `[1] Node.removeChild(2)`,\n        `createTextNode(5) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n        `createTextNode(4) => 6`,\n        `[1] Node.insertBefore(6, 5)`,\n        `createTextNode(3) => 7`,\n        `[1] Node.insertBefore(7, 6)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2] => [2, 4, 5]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([2, 4, 5], r, r),\n        );\n      }),\n      [\n        `[1] Node.removeChild(4)`,\n        `[1] Node.removeChild(3)`,\n        `createTextNode(5) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n        `createTextNode(4) => 6`,\n        `[1] Node.insertBefore(6, 5)`,\n      ],\n    );\n  });\n\n  test(`[0, 1] => [1, 0]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1, 0], r, r),\n        );\n      }),\n      [\n        `[1] Node.insertBefore(2, 3)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2, 3] => [3, 2, 1, 0]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([3, 2, 1, 0], r, r),\n        );\n      }),\n      [\n        `[1] Node.insertBefore(4, 5)`,\n        `[1] Node.insertBefore(3, 4)`,\n        `[1] Node.insertBefore(2, 3)`,\n      ],\n    );\n  });\n\n  test(`[0, 1, 2, 3] => [0, 2, 3, 1]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1, 2, 3], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([0, 2, 3, 1], r, r),\n        );\n      }),\n      [\n        `[1] Node.insertBefore(4, null)`,\n      ],\n    );\n  });\n\n  test(`[0, 1] => [2, 1, 0]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([2, 1, 0], r, r),\n        );\n      }),\n      [\n        `[1] Node.insertBefore(2, 3)`,\n        `createTextNode(2) => 4`,\n        `[1] Node.insertBefore(4, 2)`,\n      ],\n    );\n  });\n\n  test(`[0, 1] => [3, 1, 2, 0]`, () => {\n    const root = createRoot();\n    root.update(\n      List([0, 1], r, r),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([3, 1, 2, 0], r, r),\n        );\n      }),\n      [\n        `createTextNode(2) => 4`,\n        `[1] Node.insertBefore(4, 3)`,\n        `[1] Node.insertBefore(2, 4)`,\n        `createTextNode(3) => 5`,\n        `[1] Node.insertBefore(5, 2)`,\n      ],\n    );\n  });\n\n  test(`elements: [0, 1] => [1, 0]`, () => {\n    const root = createRoot();\n    const div = html`<div/>`;\n    root.update(\n      List([0, 1], r, () => div),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1, 0], r, () => div),\n        );\n      }),\n      [\n        `[1] Node.insertBefore(2, 3)`,\n      ],\n    );\n  });\n\n  test(`components: [0, 1] => [1, 0]`, () => {\n    const root = createRoot();\n    const C = component<number>(() => (i) => i);\n    root.update(\n      List([0, 1], r, C),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1, 0], r, C),\n        );\n      }),\n      [\n        `[1] Node.insertBefore(2, 3)`,\n      ],\n    );\n  });\n\n  test(`components (preventUpdates): [0, 1] => [1, 0]`, () => {\n    const root = createRoot();\n    const C = component<number>(() => (i) => i, () => true);\n    root.update(\n      List([0, 1], r, C),\n    );\n    deepStrictEqual(\n      trace(() => {\n        root.update(\n          List([1, 0], r, C),\n        );\n      }),\n      [\n        `[1] Node.insertBefore(2, 3)`,\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/mock-dom/document.test.ts",
    "content": "import { deepStrictEqual, ok, strictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { NodeType } from \"@ivi/mock-dom\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\n\ndescribe(\"mock-dom/document\", () => {\n  beforeEach(reset);\n\n  test(\"createElement\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElement(\"div\");\n        ok(n instanceof HTMLElement);\n        strictEqual(n.nodeType, NodeType.Element);\n        strictEqual(n.nodeName, \"DIV\");\n        strictEqual(n.namespaceURI, \"http://www.w3.org/1999/xhtml\");\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Node.nodeType => 1`,\n        `[2] Node.nodeName => \"DIV\"`,\n        `[2] Element.namespaceURI => \"http://www.w3.org/1999/xhtml\"`,\n      ],\n    );\n  });\n\n  test(\"createElementNS\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElementNS(\"NS\", \"tagName\");\n        ok(n instanceof Element);\n        strictEqual(n.nodeType, NodeType.Element);\n        strictEqual(n.nodeName, \"TAGNAME\");\n        strictEqual(n.namespaceURI, \"NS\");\n      }),\n      [\n        `createElementNS(\"NS\", \"tagName\") => 2`,\n        `[2] Node.nodeType => 1`,\n        `[2] Node.nodeName => \"TAGNAME\"`,\n        `[2] Element.namespaceURI => \"NS\"`,\n      ],\n    );\n  });\n\n  test(\"createText\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createTextNode(\"a b\");\n        strictEqual(n.nodeType, NodeType.Text);\n        strictEqual(n.nodeValue, \"a b\");\n      }),\n      [\n        `createTextNode(\"a b\") => 2`,\n        `[2] Node.nodeType => 3`,\n        `[2] Node.nodeValue => \"a b\"`,\n      ],\n    );\n  });\n\n  test(`createElementNS(\"http://www.w3.org/2000/svg\", \"a\")`, () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElementNS(\"http://www.w3.org/2000/svg\", \"a\");\n        ok(n instanceof SVGElement);\n      }),\n      [\n        `createElementNS(\"http://www.w3.org/2000/svg\", \"a\") => 2`\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/mock-dom/element.test.ts",
    "content": "import { deepStrictEqual, ok, strictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace, emit } from \"@ivi/mock-dom/global\";\n\ndescribe(\"mock-dom/element\", () => {\n  beforeEach(() => { reset(); });\n\n  test(\"attributes\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElement(\"div\");\n        strictEqual(n.getAttribute(\"a\"), null);\n        n.setAttribute(\"a\", \"1\");\n        strictEqual(n.getAttribute(\"a\"), \"1\");\n        n.setAttribute(\"b\", \"2\");\n        strictEqual(n.getAttribute(\"b\"), \"2\");\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.getAttribute(\"a\") => null`,\n        `[2] Element.setAttribute(\"a\", \"1\")`,\n        `[2] Element.getAttribute(\"a\") => \"1\"`,\n        `[2] Element.setAttribute(\"b\", \"2\")`,\n        `[2] Element.getAttribute(\"b\") => \"2\"`,\n      ],\n    );\n  });\n\n  test(\"className\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElement(\"div\");\n        strictEqual(n.className, \"\");\n        n.className = \"a b\";\n        strictEqual(n.className, \"a b\");\n        strictEqual(n.getAttribute(\"class\"), \"a b\");\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.className => \"\"`,\n        `[2] Element.className = \"a b\"`,\n        `[2] Element.className => \"a b\"`,\n        `[2] Element.getAttribute(\"class\") => \"a b\"`,\n      ],\n    );\n  });\n\n  test(\"style\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElement(\"div\");\n        const style = n.style;\n        ok(style instanceof CSSStyleDeclaration);\n        strictEqual(style.getPropertyValue(\"a\"), \"\");\n        style.setProperty(\"a\", \"1\");\n        strictEqual(style.getPropertyValue(\"a\"), \"1\");\n        style.setProperty(\"b\", \"2\");\n        strictEqual(style.getPropertyValue(\"b\"), \"2\");\n        style.removeProperty(\"a\");\n        strictEqual(style.getPropertyValue(\"a\"), \"\");\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] HTMLElement.style`,\n        `[2] style.getPropertyValue(a) => \"\"`,\n        `[2] style.setProperty(\"a\", \"1\")`,\n        `[2] style.getPropertyValue(a) => \"1\"`,\n        `[2] style.setProperty(\"b\", \"2\")`,\n        `[2] style.getPropertyValue(b) => \"2\"`,\n        `[2] style.removeProperty(\"a\")`,\n        `[2] style.getPropertyValue(a) => \"\"`,\n      ],\n    );\n  });\n\n  test(\"events\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElement(\"div\");\n        let i = 0;\n        const test = () => {\n          i++;\n        };\n        n.addEventListener(\"test\", test);\n        emit(n, \"test\");\n        strictEqual(i, 1);\n        emit(n, \"test\");\n        strictEqual(i, 2);\n        n.removeEventListener(\"test\", test);\n        emit(n, \"test\");\n        strictEqual(i, 2);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.addEventListener(\"test\", test)`,\n        `[2] Element.removeEventListener(\"test\", test)`,\n      ],\n    );\n  });\n\n  test(\"properties\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElement(\"div\") as any;\n        strictEqual(n[\"a\"], void 0);\n        (n as any)[\"a\"] = 123;\n        strictEqual(n[\"a\"], 123);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.getProperty(\"a\") => undefined`,\n        `[2] Element.setProperty(\"a\", 123)`,\n        `[2] Element.getProperty(\"a\") => 123`,\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/mock-dom/innerHTML.test.ts",
    "content": "import { strictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { Document as _Document } from \"@ivi/mock-dom\";\nimport { reset, toSnapshot } from \"@ivi/mock-dom/global\";\n\ndeclare global {\n  interface Document {\n    _createElement(tagName: string);\n    _createElementNS(namespace: string, tagName: string);\n  }\n}\n\ndescribe(\"mock-dom/innerHTML\", () => {\n  beforeEach(reset);\n\n  test(\"<span></span>\", () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = \"<span></span>\";\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4/>\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(\"<span><a></a></span>\", () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = \"<span><a></a></span>\";\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4>\n    <A#5/>\n  </SPAN#4>\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(\"<span><a></a><b></b></span>\", () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = \"<span><a></a><b></b></span>\";\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4>\n    <A#5/>\n    <B#6/>\n  </SPAN#4>\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(\"<span> text </span>\", () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = \"<span> text </span>\";\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4>\n    <TEXT#5> text </TEXT#5>\n  </SPAN#4>\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(\"<span><a></a>text<b></b></span>\", () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = \"<span><a></a>text<b></b></span>\";\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4>\n    <A#5/>\n    <TEXT#6>text</TEXT#6>\n    <B#7/>\n  </SPAN#4>\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(`<span attr=\"value\"></span>`, () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = `<span a=\"1\"></span>`;\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4\n    a=\"1\"\n  />\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(`<span a=\"1\" b=\"2\"></span>`, () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = `<span a=\"1\" b=\"2\"></span>`;\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4\n    a=\"1\"\n    b=\"2\"\n  />\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(`<span class=\"a b\"></span>`, () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = `<span class=\"a b\"></span>`;\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4\n    class=\"a b\"\n  />\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(`<div><input><span></span></div>`, () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = `<div><input><span></span></div>`;\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <DIV#4>\n    <INPUT#5/>\n    <SPAN#6/>\n  </DIV#4>\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(\"<span><!></span>\", () => {\n    const e = document._createElement(\"div\");\n    e.innerHTML = \"<span><!></span>\";\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4>\n    <!>\n  </SPAN#4>\n</DIV#2>\n      `.trim(),\n    );\n  });\n\n  test(\"namespace\", () => {\n    const e = document._createElementNS(\"NS\", \"div\");\n    e.innerHTML = \"<span></span>\";\n    strictEqual(\n      toSnapshot(e),\n      `\n<DIV#2>\n  <SPAN#4/>\n</DIV#2>\n      `.trim(),\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/mock-dom/node.test.ts",
    "content": "import { deepStrictEqual, strictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\n\ndescribe(\"mock-dom/node\", () => {\n  beforeEach(reset);\n  test(\"append child\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const parent = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        parent.appendChild(a);\n        strictEqual(parent.firstChild, a);\n        strictEqual(parent.lastChild, a);\n        strictEqual(a.parentNode, parent);\n        strictEqual(a.previousSibling, null);\n        strictEqual(a.nextSibling, null);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"span\") => 3`,\n        `[2] Node.appendChild(3)`,\n        `[2] Node.firstChild => 3`,\n        `[2] Node.lastChild => 3`,\n        `[3] Node.parentNode => 2`,\n        `[3] Node.previousSibling => null`,\n        `[3] Node.nextSibling => null`,\n      ],\n    );\n  });\n\n  test(\"append 2 child\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const parent = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        const b = document.createElement(\"span\");\n        parent.appendChild(a);\n        parent.appendChild(b);\n        strictEqual(parent.firstChild, a);\n        strictEqual(parent.lastChild, b);\n        strictEqual(a.parentNode, parent);\n        strictEqual(b.parentNode, parent);\n\n        strictEqual(a.previousSibling, null);\n        strictEqual(a.nextSibling, b);\n        strictEqual(b.previousSibling, a);\n        strictEqual(b.nextSibling, null);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"span\") => 3`,\n        `createElement(\"span\") => 4`,\n        `[2] Node.appendChild(3)`,\n        `[2] Node.appendChild(4)`,\n        `[2] Node.firstChild => 3`,\n        `[2] Node.lastChild => 4`,\n        `[3] Node.parentNode => 2`,\n        `[4] Node.parentNode => 2`,\n        `[3] Node.previousSibling => null`,\n        `[3] Node.nextSibling => 4`,\n        `[4] Node.previousSibling => 3`,\n        `[4] Node.nextSibling => null`,\n      ],\n    );\n  });\n\n  test(\"insertBefore before 1 node\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const parent = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        const b = document.createElement(\"span\");\n        parent.insertBefore(a, null);\n        parent.insertBefore(b, a);\n        strictEqual(parent.firstChild, b);\n        strictEqual(parent.lastChild, a);\n        strictEqual(a.parentNode, parent);\n        strictEqual(b.parentNode, parent);\n\n        strictEqual(a.previousSibling, b);\n        strictEqual(a.nextSibling, null);\n        strictEqual(b.previousSibling, null);\n        strictEqual(b.nextSibling, a);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"span\") => 3`,\n        `createElement(\"span\") => 4`,\n        `[2] Node.insertBefore(3, null)`,\n        `[2] Node.insertBefore(4, 3)`,\n        `[2] Node.firstChild => 4`,\n        `[2] Node.lastChild => 3`,\n        `[3] Node.parentNode => 2`,\n        `[4] Node.parentNode => 2`,\n        `[3] Node.previousSibling => 4`,\n        `[3] Node.nextSibling => null`,\n        `[4] Node.previousSibling => null`,\n        `[4] Node.nextSibling => 3`,\n      ],\n    );\n  });\n\n  test(\"insertBefore between 2 nodes\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const parent = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        const b = document.createElement(\"span\");\n        const c = document.createElement(\"span\");\n        parent.insertBefore(a, null);\n        parent.insertBefore(b, null);\n        parent.insertBefore(c, b);\n        strictEqual(parent.firstChild, a);\n        strictEqual(parent.lastChild, b);\n        strictEqual(a.parentNode, parent);\n        strictEqual(b.parentNode, parent);\n        strictEqual(c.parentNode, parent);\n\n        strictEqual(a.previousSibling, null);\n        strictEqual(a.nextSibling, c);\n        strictEqual(b.previousSibling, c);\n        strictEqual(b.nextSibling, null);\n        strictEqual(c.previousSibling, a);\n        strictEqual(c.nextSibling, b);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"span\") => 3`,\n        `createElement(\"span\") => 4`,\n        `createElement(\"span\") => 5`,\n        `[2] Node.insertBefore(3, null)`,\n        `[2] Node.insertBefore(4, null)`,\n        `[2] Node.insertBefore(5, 4)`,\n        `[2] Node.firstChild => 3`,\n        `[2] Node.lastChild => 4`,\n        `[3] Node.parentNode => 2`,\n        `[4] Node.parentNode => 2`,\n        `[5] Node.parentNode => 2`,\n        `[3] Node.previousSibling => null`,\n        `[3] Node.nextSibling => 5`,\n        `[4] Node.previousSibling => 5`,\n        `[4] Node.nextSibling => null`,\n        `[5] Node.previousSibling => 3`,\n        `[5] Node.nextSibling => 4`,\n      ],\n    );\n  });\n\n  test(\"remove first node\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const parent = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        const b = document.createElement(\"span\");\n        const c = document.createElement(\"span\");\n        parent.insertBefore(a, null);\n        parent.insertBefore(b, null);\n        parent.insertBefore(c, null);\n        parent.removeChild(a);\n\n        strictEqual(parent.firstChild, b);\n        strictEqual(parent.lastChild, c);\n\n        strictEqual(a.previousSibling, null);\n        strictEqual(a.nextSibling, null);\n        strictEqual(b.previousSibling, null);\n        strictEqual(b.nextSibling, c);\n        strictEqual(c.previousSibling, b);\n        strictEqual(c.nextSibling, null);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"span\") => 3`,\n        `createElement(\"span\") => 4`,\n        `createElement(\"span\") => 5`,\n        `[2] Node.insertBefore(3, null)`,\n        `[2] Node.insertBefore(4, null)`,\n        `[2] Node.insertBefore(5, null)`,\n        `[2] Node.removeChild(3)`,\n        `[2] Node.firstChild => 4`,\n        `[2] Node.lastChild => 5`,\n        `[3] Node.previousSibling => null`,\n        `[3] Node.nextSibling => null`,\n        `[4] Node.previousSibling => null`,\n        `[4] Node.nextSibling => 5`,\n        `[5] Node.previousSibling => 4`,\n        `[5] Node.nextSibling => null`,\n      ],\n    );\n  });\n\n  test(\"remove middle node\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const parent = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        const b = document.createElement(\"span\");\n        const c = document.createElement(\"span\");\n        parent.insertBefore(a, null);\n        parent.insertBefore(b, null);\n        parent.insertBefore(c, null);\n        parent.removeChild(b);\n\n        strictEqual(parent.firstChild, a);\n        strictEqual(parent.lastChild, c);\n\n        strictEqual(a.previousSibling, null);\n        strictEqual(a.nextSibling, c);\n        strictEqual(b.previousSibling, null);\n        strictEqual(b.nextSibling, null);\n        strictEqual(c.previousSibling, a);\n        strictEqual(c.nextSibling, null);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"span\") => 3`,\n        `createElement(\"span\") => 4`,\n        `createElement(\"span\") => 5`,\n        `[2] Node.insertBefore(3, null)`,\n        `[2] Node.insertBefore(4, null)`,\n        `[2] Node.insertBefore(5, null)`,\n        `[2] Node.removeChild(4)`,\n        `[2] Node.firstChild => 3`,\n        `[2] Node.lastChild => 5`,\n        `[3] Node.previousSibling => null`,\n        `[3] Node.nextSibling => 5`,\n        `[4] Node.previousSibling => null`,\n        `[4] Node.nextSibling => null`,\n        `[5] Node.previousSibling => 3`,\n        `[5] Node.nextSibling => null`,\n      ],\n    );\n  });\n\n  test(\"remove last node\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const parent = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        const b = document.createElement(\"span\");\n        const c = document.createElement(\"span\");\n        parent.insertBefore(a, null);\n        parent.insertBefore(b, null);\n        parent.insertBefore(c, null);\n        parent.removeChild(c);\n\n        strictEqual(parent.firstChild, a);\n        strictEqual(parent.lastChild, b);\n\n        strictEqual(a.previousSibling, null);\n        strictEqual(a.nextSibling, b);\n        strictEqual(b.previousSibling, a);\n        strictEqual(b.nextSibling, null);\n        strictEqual(c.previousSibling, null);\n        strictEqual(c.nextSibling, null);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"span\") => 3`,\n        `createElement(\"span\") => 4`,\n        `createElement(\"span\") => 5`,\n        `[2] Node.insertBefore(3, null)`,\n        `[2] Node.insertBefore(4, null)`,\n        `[2] Node.insertBefore(5, null)`,\n        `[2] Node.removeChild(5)`,\n        `[2] Node.firstChild => 3`,\n        `[2] Node.lastChild => 4`,\n        `[3] Node.previousSibling => null`,\n        `[3] Node.nextSibling => 4`,\n        `[4] Node.previousSibling => 3`,\n        `[4] Node.nextSibling => null`,\n        `[5] Node.previousSibling => null`,\n        `[5] Node.nextSibling => null`,\n      ],\n    );\n  });\n\n  test(\"move node\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const parent = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        const b = document.createElement(\"span\");\n        const c = document.createElement(\"span\");\n        parent.insertBefore(a, null);\n        parent.insertBefore(b, null);\n        parent.insertBefore(c, b);\n        parent.insertBefore(b, a);\n\n        strictEqual(parent.firstChild, b);\n        strictEqual(parent.lastChild, c);\n\n        strictEqual(b.previousSibling, null);\n        strictEqual(b.nextSibling, a);\n        strictEqual(a.previousSibling, b);\n        strictEqual(a.nextSibling, c);\n        strictEqual(c.previousSibling, a);\n        strictEqual(c.nextSibling, null);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"span\") => 3`,\n        `createElement(\"span\") => 4`,\n        `createElement(\"span\") => 5`,\n        `[2] Node.insertBefore(3, null)`,\n        `[2] Node.insertBefore(4, null)`,\n        `[2] Node.insertBefore(5, 4)`,\n        `[2] Node.insertBefore(4, 3)`,\n        `[2] Node.firstChild => 4`,\n        `[2] Node.lastChild => 5`,\n        `[4] Node.previousSibling => null`,\n        `[4] Node.nextSibling => 3`,\n        `[3] Node.previousSibling => 4`,\n        `[3] Node.nextSibling => 5`,\n        `[5] Node.previousSibling => 3`,\n        `[5] Node.nextSibling => null`,\n      ],\n    );\n  });\n\n  test(\"move node between different element\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const parentA = document.createElement(\"div\");\n        const parentB = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        parentA.insertBefore(a, null);\n        strictEqual(parentA.firstChild, a);\n        strictEqual(a.parentNode, parentA);\n\n        parentB.insertBefore(a, null);\n        strictEqual(parentA.firstChild, null);\n        strictEqual(parentA.lastChild, null);\n        strictEqual(parentB.firstChild, a);\n        strictEqual(parentB.lastChild, a);\n        strictEqual(a.parentNode, parentB);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"div\") => 3`,\n        `createElement(\"span\") => 4`,\n        `[2] Node.insertBefore(4, null)`,\n        `[2] Node.firstChild => 4`,\n        `[4] Node.parentNode => 2`,\n        `[3] Node.insertBefore(4, null)`,\n        `[2] Node.firstChild => null`,\n        `[2] Node.lastChild => null`,\n        `[3] Node.firstChild => 4`,\n        `[3] Node.lastChild => 4`,\n        `[4] Node.parentNode => 3`,\n      ],\n    );\n  });\n\n  test(`textContent=\"\"`, () => {\n    deepStrictEqual(\n      trace(() => {\n        const parent = document.createElement(\"div\");\n        const a = document.createElement(\"span\");\n        const b = document.createElement(\"span\");\n        const c = document.createElement(\"span\");\n        parent.insertBefore(a, null);\n        parent.insertBefore(b, null);\n        parent.insertBefore(c, null);\n\n        strictEqual(a.parentNode, parent);\n        strictEqual(b.parentNode, parent);\n        strictEqual(c.parentNode, parent);\n\n        parent.textContent = \"\";\n\n        strictEqual(parent.firstChild, null);\n        strictEqual(parent.lastChild, null);\n\n        strictEqual(a.parentNode, null);\n        strictEqual(b.parentNode, null);\n        strictEqual(c.parentNode, null);\n      }),\n      [\n        `createElement(\"div\") => 2`,\n        `createElement(\"span\") => 3`,\n        `createElement(\"span\") => 4`,\n        `createElement(\"span\") => 5`,\n        `[2] Node.insertBefore(3, null)`,\n        `[2] Node.insertBefore(4, null)`,\n        `[2] Node.insertBefore(5, null)`,\n        `[3] Node.parentNode => 2`,\n        `[4] Node.parentNode => 2`,\n        `[5] Node.parentNode => 2`,\n        `[2] Node.textContent = \"\"`,\n        `[2] Node.firstChild => null`,\n        `[2] Node.lastChild => null`,\n        `[3] Node.parentNode => null`,\n        `[4] Node.parentNode => null`,\n        `[5] Node.parentNode => null`,\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/mock-dom/template.test.ts",
    "content": "import { strictEqual, ok, deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { NodeType } from \"@ivi/mock-dom\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\n\ndescribe(\"mock-dom/template\", () => {\n  beforeEach(reset);\n\n  test(`createElement(\"template\")`, () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElement(\"template\");\n        ok(n instanceof Template);\n        ok(n.content instanceof DocumentFragment);\n        strictEqual(n.nodeType, NodeType.Element);\n        strictEqual(n.nodeName, \"TEMPLATE\");\n        strictEqual(n.content.firstChild, null);\n        strictEqual(n.content.lastChild, null);\n      }),\n      [\n        `createElement(\"template\") => 2`,\n        `[2] Template.content`,\n        `[2] Node.nodeType => 1`,\n        `[2] Node.nodeName => \"TEMPLATE\"`,\n        `[2] Template.content`,\n        `[3] Node.firstChild => null`,\n        `[2] Template.content`,\n        `[3] Node.lastChild => null`,\n      ],\n    );\n  });\n\n  test(\"template.innerHTML\", () => {\n    deepStrictEqual(\n      trace(() => {\n        const n = document.createElement(\"template\");\n        ok(n instanceof Template);\n        const content = n.content;\n        n.innerHTML = \"<a></a>\";\n        strictEqual(n.content, content);\n        strictEqual(content.firstChild?.nodeName, \"A\");\n      }),\n      [\n        `createElement(\"template\") => 2`,\n        `[2] Template.content`,\n        `[2] Template.innerHTML = \"<a></a>\"`,\n        `[2] Template.content`,\n        `[3] Node.firstChild => 5`,\n        `[5] Node.nodeName => \"A\"`,\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/attribute.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"@ivi/htm attribute\", () => {\n  beforeEach(reset);\n  const T = (v: undefined | null | false | string | number) => html`<div attr=${v} />`;\n\n  test(\"short form\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(html`<div attr />`); }),\n      [\n        `[-7] Template.innerHTML = \"<div attr></div>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n      ],\n    );\n  });\n\n  test(`{undefined}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{null}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{false}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(false)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{\"\"}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setAttribute(\"attr\", \"\")`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{0}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setAttribute(\"attr\", 0)`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{\"a\"}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"a\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setAttribute(\"attr\", \"a\")`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`empty transitions`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n  });\n\n  test(`undefined => \"a\" => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.setAttribute(\"attr\", \"a\")`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }),\n      [`[2] Element.removeAttribute(\"attr\")`],\n    );\n  });\n\n  test(`null => \"a\" => null`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.setAttribute(\"attr\", \"a\")`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(null)); }),\n      [`[2] Element.removeAttribute(\"attr\")`],\n    );\n  });\n\n  test(`false => \"a\" => false`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.setAttribute(\"attr\", \"a\")`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(false)); }),\n      [`[2] Element.removeAttribute(\"attr\")`],\n    );\n  });\n\n  test(`\"\" => \"a\"`, () => {\n    const root = createRoot();\n    root.update(T(\"\"));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.setAttribute(\"attr\", \"a\")`],\n    );\n  });\n\n  test(`\"a\" => \"\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }),\n      [`[2] Element.setAttribute(\"attr\", \"\")`],\n    );\n  });\n\n  test(`\"a\" => \"b\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(trace(() => { root.update(T(\"b\")); }),\n      [`[2] Element.setAttribute(\"attr\", \"b\")`],\n    );\n  });\n\n  test(`0 => \"0\"`, () => {\n    const root = createRoot();\n    root.update(T(0));\n    deepStrictEqual(trace(() => { root.update(T(\"0\")); }),\n      [`[2] Element.setAttribute(\"attr\", \"0\")`],\n    );\n  });\n\n  test(`\"a\" => \"a\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/className.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"@ivi/htm className\", () => {\n  beforeEach(reset);\n  const T = (v: undefined | null | false | string | number) => html`<div class=${v} />`;\n\n  test(`\"a\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(html`<div class=\"a\" />`); }),\n      [\n        `[-7] Template.innerHTML = \"<div class=\"a\"></div>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n      ],\n    );\n  });\n\n  test(`\"a b\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(html`<div class=\"a b\" />`); }),\n      [\n        `[-7] Template.innerHTML = \"<div class=\"a b\"></div>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n      ],\n    );\n  });\n\n  test(`{undefined}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{null}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{false}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(false)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{\"\"}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{0}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.className = 0`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{\"a b\"}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"a b\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.className = \"a b\"`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`empty transitions`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n  });\n\n  test(`undefined => \"a\" => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.className = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }),\n      [`[2] Element.className = \"\"`],\n    );\n  });\n\n  test(`null => \"a\" => null`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.className = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(null)); }),\n      [`[2] Element.className = \"\"`],\n    );\n  });\n\n  test(`false => \"a\" => false`, () => {\n    const root = createRoot();\n    root.update(T(false));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.className = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(false)); }),\n      [`[2] Element.className = \"\"`],\n    );\n  });\n\n  test(`\"\" => \"a\" => \"\"`, () => {\n    const root = createRoot();\n    root.update(T(\"\"));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.className = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }),\n      [`[2] Element.className = \"\"`],\n    );\n  });\n\n  test(`\"a\" => \"b\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(trace(() => { root.update(T(\"b\")); }),\n      [`[2] Element.className = \"b\"`],\n    );\n  });\n\n  test(`0 => \"0\"`, () => {\n    const root = createRoot();\n    root.update(T(0));\n    deepStrictEqual(trace(() => { root.update(T(\"0\")); }),\n      [`[2] Element.className = \"0\"`],\n    );\n  });\n\n  test(`\"a\" => \"a\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/directive.test.ts",
    "content": "import { deepStrictEqual, strictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\nimport { type ElementDirective } from \"ivi\";\n\ndescribe(\"directive\", () => {\n  beforeEach(reset);\n  const T = (directive: ElementDirective) => html`<div ${directive} />`;\n\n  test(`root element`, () => {\n    let e: any;\n    let h: boolean | undefined;\n    const d = (element: Element) => {\n      e = element;\n    };\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(d)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n    strictEqual((e as any).uid, 2);\n    strictEqual(h, void 0);\n  });\n\n  test(`deep element`, () => {\n    let e: any;\n    let h: boolean | undefined;\n    const d = (element: Element) => {\n      e = element;\n    };\n\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(html`<div><span ${d} /></div>`); }),\n      [\n        `[-7] Template.innerHTML = \"<div><span></span></div>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 5`,\n        `[5] Node.firstChild => 6`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n    strictEqual((e as any).uid, 6);\n    strictEqual(h, void 0);\n  });\n\n  test(`reuse`, () => {\n    let _trace: string[] = [];\n    const d = (element: Element) => {\n      _trace.push(`directive(${(element as any).uid})`);\n    };\n    const root = createRoot();\n    root.update(T(d));\n    deepStrictEqual(\n      _trace,\n      [\n        `directive(2)`,\n      ],\n    );\n    _trace = [];\n    deepStrictEqual(\n      trace(() => { root.update(T(d)); }),\n      [],\n    );\n    deepStrictEqual(\n      _trace,\n      [],\n    );\n  });\n\n  test(`update`, () => {\n    let _trace: string[] = [];\n    const d1 = (element: Element) => {\n      _trace.push(`directive1(${(element as any).uid})`);\n    };\n    const d2 = (element: Element) => {\n      _trace.push(`directive2(${(element as any).uid})`);\n    };\n    const root = createRoot();\n    root.update(T(d1));\n    deepStrictEqual(\n      _trace,\n      [\n        `directive1(2)`,\n      ],\n    );\n    _trace = [];\n    deepStrictEqual(\n      trace(() => { root.update(T(d2)); }),\n      [],\n    );\n    deepStrictEqual(\n      _trace,\n      [\n        `directive2(2)`,\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/event.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"@ivi/htm event\", () => {\n  beforeEach(reset);\n  const T = (h: (() => void) | undefined | null | false) => html`<div @click=${h} />`;\n  const onClick = () => { };\n\n  test(`{undefined}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{null}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{false}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(false)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`empty transitions`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n  });\n\n  test(`{onClick}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(onClick)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.addEventListener(\"click\", onClick)`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`undefined => onClick => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(\n      trace(() => { root.update(T(onClick)); }),\n      [\n        `[2] Element.addEventListener(\"click\", onClick)`,\n      ],\n    );\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `[2] Element.removeEventListener(\"click\", onClick)`,\n      ],\n    );\n  });\n\n  test(`null => onClick => null`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(\n      trace(() => { root.update(T(onClick)); }),\n      [\n        `[2] Element.addEventListener(\"click\", onClick)`,\n      ],\n    );\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `[2] Element.removeEventListener(\"click\", onClick)`,\n      ],\n    );\n  });\n\n  test(`false => onClick => false`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(\n      trace(() => { root.update(T(onClick)); }),\n      [\n        `[2] Element.addEventListener(\"click\", onClick)`,\n      ],\n    );\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `[2] Element.removeEventListener(\"click\", onClick)`,\n      ],\n    );\n  });\n\n  test(`onClick => onClick2`, () => {\n    const onClick2 = () => { };\n    const root = createRoot();\n    root.update(T(onClick));\n    deepStrictEqual(\n      trace(() => { root.update(T(onClick2)); }),\n      [\n        `[2] Element.removeEventListener(\"click\", onClick)`,\n        `[2] Element.addEventListener(\"click\", onClick2)`,\n      ],\n    );\n  });\n\n  test(`onClick => onClick`, () => {\n    const root = createRoot();\n    root.update(T(onClick));\n    deepStrictEqual(\n      trace(() => { root.update(T(onClick)); }),\n      [],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/htm.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"@ivi/htm\", () => {\n  beforeEach(reset);\n\n  test(\"<h1/>\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1/>\n        `);\n      }),\n      [\n        `createElement(\"h1\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(\"<h1></h1>\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1></h1>\n        `);\n      }),\n      [\n        `createElement(\"h1\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(\"<h1>a</h1>\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>a</h1>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<h1>a</h1>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n  });\n\n  test(\"void elements\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <a>\n            <audio>\n            <video>\n            <embed>\n            <input>\n            <param>\n            <source>\n            <track>\n            <area>\n            <base>\n            <link>\n            <meta>\n            <br>\n            <col>\n            <hr>\n            <img>\n            <wbr>\n          </a>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<a><audio><video><embed><input><param><source><track><area><base><link><meta><br><col><hr><img><wbr></a>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 20`,\n        `[1] Node.insertBefore(20, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 1\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>\n          </h1>\n        `);\n      }),\n      [\n        `createElement(\"h1\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 2\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>\n\n          </h1>\n        `);\n      }),\n      [\n        `createElement(\"h1\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 3\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>  </h1>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<h1> </h1>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 4\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <div>\n            <h2></h2>\n            <h2></h2>\n          </div>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<div><h2></h2><h2></h2></div>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 6`,\n        `[1] Node.insertBefore(6, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 5\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>\n            ab\n          </h1>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<h1>ab</h1>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 6\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>\n            ab\n            cd\n          </h1>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<h1>ab cd</h1>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 7\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>\n            ab  cd\n          </h1>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<h1>ab cd</h1>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 8\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>  ab  cd  </h1>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<h1> ab cd </h1>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 9\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>\n            \\vab\n          </h1>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<h1> ab</h1>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 10\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <h1>\n            ab\\v\n          </h1>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<h1>ab </h1>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 5`,\n        `[1] Node.insertBefore(5, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 11\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <div>\n            a\n            <span>\n              b\n            </span>\n          </div>\n        `);\n      }),\n      [\n        `[-7] Template.innerHTML = \"<div>a<span>b</span></div>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 7`,\n        `[1] Node.insertBefore(7, null)`,\n      ],\n    );\n  });\n\n  test(\"whitespace 12\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <div>\n            ${\"a\"} ${\"b\"} ${\"c\"}\n          </div>\n        `);\n      }),\n      [\n        '[-7] Template.innerHTML = \"<div> <!> </div>\"',\n        '[-6] Node.firstChild => 3',\n        '[3] Node.cloneNode(true) => 7',\n        '[7] Node.firstChild => 8',\n        '[8] Node.nextSibling => 9',\n        '[9] Node.nextSibling => 10',\n        '[9] Node.remove()',\n        'createTextNode(\"c\") => 11',\n        '[7] Node.insertBefore(11, null)',\n        'createTextNode(\"b\") => 12',\n        '[7] Node.insertBefore(12, 10)',\n        'createTextNode(\"a\") => 13',\n        '[7] Node.insertBefore(13, 8)',\n        '[1] Node.insertBefore(7, null)',\n      ],\n    );\n  });\n\n  test(\"whitespace 13\", () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => {\n        root.update(html`\n          <div>\n            ${\"a\"}\\v\n            ${\"b\"}\\v\n            ${\"c\"}\n          </div>\n        `);\n      }),\n      [\n        '[-7] Template.innerHTML = \"<div> <!> </div>\"',\n        '[-6] Node.firstChild => 3',\n        '[3] Node.cloneNode(true) => 7',\n        '[7] Node.firstChild => 8',\n        '[8] Node.nextSibling => 9',\n        '[9] Node.nextSibling => 10',\n        '[9] Node.remove()',\n        'createTextNode(\"c\") => 11',\n        '[7] Node.insertBefore(11, null)',\n        'createTextNode(\"b\") => 12',\n        '[7] Node.insertBefore(12, 10)',\n        'createTextNode(\"a\") => 13',\n        '[7] Node.insertBefore(13, 8)',\n        '[1] Node.insertBefore(7, null)',\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/innerHTML.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"@ivi/htm innerHTML\", () => {\n  beforeEach(reset);\n  const T = (v: undefined | null | false | string | number) => html`<div .innerHTML=${v} />`;\n\n  test(`undefined`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`null`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`false`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(false)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`\"\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`0`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.innerHTML = 0`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`\"0\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"0\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.innerHTML = \"0\"`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`empty transitions`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n  });\n\n  test(`undefined => \"a\" => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.innerHTML = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }),\n      [`[2] Node.textContent = \"\"`],\n    );\n  });\n\n  test(`null => \"a\" => null`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.innerHTML = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(null)); }),\n      [`[2] Node.textContent = \"\"`],\n    );\n  });\n\n  test(`false => \"a\" => false`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.innerHTML = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(false)); }),\n      [`[2] Node.textContent = \"\"`],\n    );\n  });\n\n  test(`\"\" => \"a\" => \"\"`, () => {\n    const root = createRoot();\n    root.update(T(\"\"));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.innerHTML = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }),\n      [`[2] Node.textContent = \"\"`],\n    );\n  });\n\n  test(`\"a\" => \"b\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(trace(() => { root.update(T(\"b\")); }),\n      [`[2] Element.innerHTML = \"b\"`],\n    );\n  });\n\n  test(`\"a\" => \"a\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/property.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"@ivi/htm property\", () => {\n  beforeEach(reset);\n  const T = (v: any) => html`<div .a=${v} />`;\n\n  test(`undefined`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`null`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setProperty(\"a\", null)`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`false`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(false)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setProperty(\"a\", false)`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`0`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setProperty(\"a\", 0)`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`\"0\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"0\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setProperty(\"a\", \"0\")`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`undefined => undefined => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n  });\n\n  test(`undefined => \"a\" => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(\n      trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.setProperty(\"a\", \"a\")`],\n    );\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [`[2] Element.setProperty(\"a\", undefined)`],\n    );\n  });\n\n  test(`\"a\" => \"b\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(\n      trace(() => { root.update(T(\"b\")); }),\n      [`[2] Element.setProperty(\"a\", \"b\")`],\n    );\n  });\n\n  test(`\"a\" => \"a\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(\n      trace(() => { root.update(T(\"a\")); }),\n      [],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/propertyDiffDOM.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"@ivi/htm propertyDiffDOM\", () => {\n  beforeEach(reset);\n  const T = (v: any) => html`<div *a=${v} />`;\n\n  test(`undefined`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`null`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setProperty(\"a\", null)`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`false`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(false)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setProperty(\"a\", false)`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`0`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setProperty(\"a\", 0)`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`\"0\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"0\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Element.setProperty(\"a\", \"0\")`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`undefined => undefined => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [`[2] Element.getProperty(\"a\") => undefined`],\n    );\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [`[2] Element.getProperty(\"a\") => undefined`],\n    );\n  });\n\n  test(`undefined => \"a\" => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(\n      trace(() => { root.update(T(\"a\")); }),\n      [\n        `[2] Element.getProperty(\"a\") => undefined`,\n        `[2] Element.setProperty(\"a\", \"a\")`,\n      ],\n    );\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `[2] Element.getProperty(\"a\") => \"a\"`,\n        `[2] Element.setProperty(\"a\", undefined)`,\n      ],\n    );\n  });\n\n  test(`\"a\" => \"b\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(\n      trace(() => { root.update(T(\"b\")); }),\n      [\n        `[2] Element.getProperty(\"a\") => \"a\"`,\n        `[2] Element.setProperty(\"a\", \"b\")`,\n      ],\n    );\n  });\n\n  test(`\"a\" => \"a\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(\n      trace(() => { root.update(T(\"a\")); }),\n      [`[2] Element.getProperty(\"a\") => \"a\"`],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/style.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"@ivi/htm style\", () => {\n  beforeEach(reset);\n  const T = (v: undefined | null | false | string | number) => html`<div ~a=${v} />`;\n  const T2 = (\n    a: undefined | null | false | string,\n    b: undefined | null | false | string,\n  ) => html`<div ~a=${a} ~b=${b} />`;\n\n  test(`~a=\"0\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(html`<div ~a=\"0\" />`); }),\n      [\n        `[-7] Template.innerHTML = \"<div style=\"a:0\"></div>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n      ],\n    );\n  });\n\n  test(`style=\"a:0\" ~b=\"1\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(html`<div style=\"a:0\" ~b=\"1\" />`); }),\n      [\n        `[-7] Template.innerHTML = \"<div style=\"a:0;b:1\"></div>\"`,\n        `[-6] Node.firstChild => 3`,\n        `[3] Node.cloneNode(true) => 4`,\n        `[1] Node.insertBefore(4, null)`,\n      ],\n    );\n  });\n\n  test(`{undefined}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{null}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{false}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(false)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{\"\"}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] HTMLElement.style`,\n        `[2] style.setProperty(\"a\", \"\")`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`{\"0\"}`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"0\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] HTMLElement.style`,\n        `[2] style.setProperty(\"a\", \"0\")`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`empty transitions`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n  });\n\n  test(`undefined => \"0\" => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(\"0\")); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.setProperty(\"a\", \"0\")`,\n      ],\n    );\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.removeProperty(\"a\")`,\n      ],\n    );\n  });\n\n  test(`null => \"0\" => null`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(trace(() => { root.update(T(\"0\")); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.setProperty(\"a\", \"0\")`,\n      ],\n    );\n    deepStrictEqual(trace(() => { root.update(T(null)); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.removeProperty(\"a\")`,\n      ],\n    );\n  });\n\n  test(`false => \"0\" => false`, () => {\n    const root = createRoot();\n    root.update(T(false));\n    deepStrictEqual(trace(() => { root.update(T(\"0\")); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.setProperty(\"a\", \"0\")`,\n      ],\n    );\n    deepStrictEqual(trace(() => { root.update(T(false)); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.removeProperty(\"a\")`,\n      ],\n    );\n  });\n\n  test(`undefined => \"0\", \"1\" => undefined`, () => {\n    const root = createRoot();\n    root.update(T2(void 0, void 0));\n    deepStrictEqual(trace(() => { root.update(T2(\"0\", \"1\")); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.setProperty(\"a\", \"0\")`,\n        `[2] style.setProperty(\"b\", \"1\")`,\n      ],\n    );\n    deepStrictEqual(trace(() => { root.update(T2(void 0, void 0)); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.removeProperty(\"a\")`,\n        `[2] style.removeProperty(\"b\")`,\n      ],\n    );\n  });\n\n  test(`\"\" => \"0\"`, () => {\n    const root = createRoot();\n    root.update(T(\"\"));\n    deepStrictEqual(trace(() => { root.update(T(\"0\")); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.setProperty(\"a\", \"0\")`,\n      ],\n    );\n  });\n\n  test(`\"0\" => \"\"`, () => {\n    const root = createRoot();\n    root.update(T(\"0\"));\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.setProperty(\"a\", \"\")`,\n      ],\n    );\n  });\n\n  test(`\"0\" => \"0\"`, () => {\n    const root = createRoot();\n    root.update(T(\"0\"));\n    deepStrictEqual(trace(() => { root.update(T(\"0\")); }),\n      [],\n    );\n  });\n\n  test(`0 => \"0\"`, () => {\n    const root = createRoot();\n    root.update(T(0));\n    deepStrictEqual(trace(() => { root.update(T(\"0\")); }),\n      [\n        `[2] HTMLElement.style`,\n        `[2] style.setProperty(\"a\", \"0\")`,\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/svg.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { svg } from \"ivi\";\n\ndescribe(\"@ivi/htm svg\", () => {\n  beforeEach(reset);\n  const T = (v: undefined | null | false | string | number) => svg`<a ~a=${v} />`;\n\n  test(`~style: \"0\" => \"1\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"0\")); }),\n      [\n        `createElementNS(\"http://www.w3.org/2000/svg\", \"a\") => 2`,\n        `[2] SVGElement.style`,\n        `[2] style.setProperty(\"a\", \"0\")`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n\n    deepStrictEqual(\n      trace(() => { root.update(T(\"1\")); }),\n      [\n        `[2] SVGElement.style`,\n        `[2] style.setProperty(\"a\", \"1\")`,\n      ],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/template/textContent.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { html } from \"ivi\";\n\ndescribe(\"@ivi/htm textContent\", () => {\n  beforeEach(reset);\n\n  const T = (v: undefined | null | false | string | number) => html`<div .textContent=${v} />`;\n\n  test(`undefined`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(void 0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`null`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(null)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`false`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(false)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`\"\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`0`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(0)); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Node.textContent = 0`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`\"0\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(T(\"0\")); }),\n      [\n        `createElement(\"div\") => 2`,\n        `[2] Node.textContent = \"0\"`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`empty transitions`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(null)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }), []);\n\n    deepStrictEqual(trace(() => { root.update(T(false)); }), []);\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }), []);\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }), []);\n  });\n\n  test(`undefined => \"a\" => undefined`, () => {\n    const root = createRoot();\n    root.update(T(void 0));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Node.textContent = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(void 0)); }),\n      [`[2] Node.textContent = \"\"`],\n    );\n  });\n\n  test(`null => \"a\" => null`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Node.textContent = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(null)); }),\n      [`[2] Node.textContent = \"\"`],\n    );\n  });\n\n  test(`false => \"a\" => false`, () => {\n    const root = createRoot();\n    root.update(T(null));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Node.textContent = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(false)); }),\n      [`[2] Node.textContent = \"\"`],\n    );\n  });\n\n  test(`\"\" => \"a\" => \"\"`, () => {\n    const root = createRoot();\n    root.update(T(\"\"));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [`[2] Node.textContent = \"a\"`],\n    );\n    deepStrictEqual(trace(() => { root.update(T(\"\")); }),\n      [`[2] Node.textContent = \"\"`],\n    );\n  });\n\n  test(`\"a\" => \"b\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(trace(() => { root.update(T(\"b\")); }),\n      [\n        `[2] Node.firstChild => 3`,\n        `[3] Node.nodeValue = \"b\"`,\n      ],\n    );\n  });\n\n  test(`\"a\" => \"a\"`, () => {\n    const root = createRoot();\n    root.update(T(\"a\"));\n    deepStrictEqual(trace(() => { root.update(T(\"a\")); }),\n      [],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/text.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\n\ndescribe(\"text\", () => {\n  beforeEach(reset);\n\n  test(`\"\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(\"\"); }),\n      [],\n    );\n  });\n\n  test(`\"a\"`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(\"a\"); }),\n      [\n        `createTextNode(\"a\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`0`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(0); }),\n      [\n        `createTextNode(0) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`1`, () => {\n    const root = createRoot();\n    deepStrictEqual(\n      trace(() => { root.update(1); }),\n      [\n        `createTextNode(1) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`null => \"a\"`, () => {\n    const root = createRoot();\n    root.update(null);\n    deepStrictEqual(\n      trace(() => { root.update(\"a\"); }),\n      [\n        `createTextNode(\"a\") => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`null => 0`, () => {\n    const root = createRoot();\n    root.update(null);\n    deepStrictEqual(\n      trace(() => { root.update(0); }),\n      [\n        `createTextNode(0) => 2`,\n        `[1] Node.insertBefore(2, null)`,\n      ],\n    );\n  });\n\n  test(`\"a\" => null`, () => {\n    const root = createRoot();\n    root.update(\"a\");\n    deepStrictEqual(\n      trace(() => { root.update(null); }),\n      [`[1] Node.removeChild(2)`],\n    );\n  });\n\n  test(`1 => null`, () => {\n    const root = createRoot();\n    root.update(1);\n    deepStrictEqual(\n      trace(() => { root.update(null); }),\n      [`[1] Node.removeChild(2)`],\n    );\n  });\n\n  test(`\"0\" => \"0\"`, () => {\n    const root = createRoot();\n    root.update(\"0\");\n    deepStrictEqual(\n      trace(() => { root.update(\"0\"); }),\n      [],\n    );\n  });\n\n  test(`0 => 0`, () => {\n    const root = createRoot();\n    root.update(0);\n    deepStrictEqual(\n      trace(() => { root.update(0); }),\n      [],\n    );\n  });\n\n  test(`\"0\" => 0`, () => {\n    const root = createRoot();\n    root.update(\"0\");\n    deepStrictEqual(\n      trace(() => { root.update(0); }),\n      [`[2] Node.nodeValue = 0`],\n    );\n  });\n\n  test(`0 => \"0\"`, () => {\n    const root = createRoot();\n    root.update(0);\n    deepStrictEqual(\n      trace(() => { root.update(\"0\"); }),\n      [`[2] Node.nodeValue = \"0\"`],\n    );\n  });\n\n  test(`0 => 1`, () => {\n    const root = createRoot();\n    root.update(0);\n    deepStrictEqual(\n      trace(() => { root.update(1); }),\n      [`[2] Node.nodeValue = 1`],\n    );\n  });\n\n  test(`\"a\" => \"b\"`, () => {\n    const root = createRoot();\n    root.update(\"a\");\n    deepStrictEqual(\n      trace(() => { root.update(\"b\"); }),\n      [`[2] Node.nodeValue = \"b\"`],\n    );\n  });\n});\n"
  },
  {
    "path": "tests/runtime/useAnimationFrameEffect.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, flushAnimationFrames } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { component, useAnimationFrameEffect } from \"ivi\";\n\ndescribe(\"useAnimationFrameEffect\", () => {\n  beforeEach(reset);\n\n  test(`#1`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useAnimationFrameEffect(c, () => {\n        _trace.push(\"effect1\");\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      null,\n    );\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n    ]);\n  });\n\n  test(`#2`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useAnimationFrameEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      null,\n    );\n    deepStrictEqual(_trace, [\n      \"reset1\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n    ]);\n  });\n\n  test(`#3`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useAnimationFrameEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      const e2 = useAnimationFrameEffect(c, () => {\n        _trace.push(\"effect2\");\n        return () => {\n          _trace.push(\"reset2\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        e2();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"effect1\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      null,\n    );\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"reset2\",\n    ]);\n  });\n\n  test(`#4`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useAnimationFrameEffect<number>(c, (i) => {\n        _trace.push(`effect1: ${i}`);\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return (i: number) => {\n        _trace.push(\"render\");\n        e1(i);\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(1),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"effect1: 1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1: 2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1: 2\",\n    ]);\n  });\n\n  test(`#5`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useAnimationFrameEffect<number>(c, (i) => {\n        _trace.push(`effect1: ${i}`);\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      }, (a, b) => {\n        _trace.push(`areEqual(${a}, ${b})`);\n        return a === b;\n      });\n      return (i: number) => {\n        _trace.push(\"render\");\n        e1(i);\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(1),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"effect1: 1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"areEqual(1, 2)\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1: 2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"areEqual(2, 2)\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n    ]);\n  });\n\n  test(`#6`, () => {\n    let _trace: string[] = [];\n    const t2 = component((c) => {\n      _trace.push(\"create2\");\n      const e1 = useAnimationFrameEffect(c, () => {\n        _trace.push(`effect2`);\n        return () => {\n          _trace.push(\"reset2\");\n        };\n      });\n      return () => {\n        _trace.push(\"render2\");\n        e1();\n        return null;\n      };\n    });\n    const t1 = component((c) => {\n      _trace.push(\"create1\");\n      const e1 = useAnimationFrameEffect(c, () => {\n        _trace.push(`effect1`);\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render1\");\n        e1();\n        return t2();\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t1(),\n    );\n    deepStrictEqual(_trace, [\n      \"create1\",\n      \"render1\",\n      \"create2\",\n      \"render2\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"effect1\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t1(),\n    );\n    deepStrictEqual(_trace, [\n      \"render1\",\n      \"render2\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t1(),\n    );\n    deepStrictEqual(_trace, [\n      \"render1\",\n      \"render2\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      null,\n    );\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"reset2\",\n    ]);\n  });\n\n  test(`unmount before effect #1`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useAnimationFrameEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    root.update(\n      null,\n    );\n    deepStrictEqual(_trace, [\n    ]);\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n    ]);\n  });\n\n  test(`unmount before effect #2`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useAnimationFrameEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    root.update(\n      null,\n    );\n    deepStrictEqual(_trace, [\n      \"reset1\",\n    ]);\n    _trace = [];\n    flushAnimationFrames(0);\n    deepStrictEqual(_trace, [\n    ]);\n  });\n});\n"
  },
  {
    "path": "tests/runtime/useEffect.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { component, useEffect } from \"ivi\";\n\ndescribe(\"useEffect\", () => {\n  beforeEach(reset);\n\n  test(`#1`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useEffect(c, () => {\n        _trace.push(\"effect1\");\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(t());\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(t());\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(t());\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(null);\n    deepStrictEqual(_trace, []);\n  });\n\n  test(`#2`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(t());\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(t());\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"reset1\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(t());\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"reset1\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(null);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n    ]);\n  });\n\n  test(`#3`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      const e2 = useEffect(c, () => {\n        _trace.push(\"effect2\");\n        return () => {\n          _trace.push(\"reset2\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        e2();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(t());\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n      \"effect1\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(t());\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(t());\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(null);\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"reset2\",\n    ]);\n  });\n\n  test(`#4`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useEffect<number>(c, (i) => {\n        _trace.push(`effect1: ${i}`);\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return (i: number) => {\n        _trace.push(\"render\");\n        e1(i);\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(1),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n      \"effect1: 1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"reset1\",\n      \"effect1: 2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"reset1\",\n      \"effect1: 2\",\n    ]);\n  });\n\n  test(`#5`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useEffect<number>(c, (i) => {\n        _trace.push(`effect1: ${i}`);\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      }, (a, b) => {\n        _trace.push(`areEqual(${a}, ${b})`);\n        return a === b;\n      });\n      return (i: number) => {\n        _trace.push(\"render\");\n        e1(i);\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(1),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n      \"effect1: 1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"areEqual(1, 2)\",\n      \"reset1\",\n      \"effect1: 2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"areEqual(2, 2)\",\n    ]);\n  });\n\n  test(`#6`, () => {\n    let _trace: string[] = [];\n    const t2 = component((c) => {\n      _trace.push(\"create2\");\n      const e1 = useEffect(c, () => {\n        _trace.push(`effect2`);\n        return () => {\n          _trace.push(\"reset2\");\n        };\n      });\n      return () => {\n        _trace.push(\"render2\");\n        e1();\n        return null;\n      };\n    });\n    const t1 = component((c) => {\n      _trace.push(\"create1\");\n      const e1 = useEffect(c, () => {\n        _trace.push(`effect1`);\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render1\");\n        e1();\n        return t2();\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t1(),\n    );\n    deepStrictEqual(_trace, [\n      \"create1\",\n      \"render1\",\n      \"create2\",\n      \"render2\",\n      \"effect1\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t1(),\n    );\n    deepStrictEqual(_trace, [\n      \"render1\",\n      \"render2\",\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t1(),\n    );\n    deepStrictEqual(_trace, [\n      \"render1\",\n      \"render2\",\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      null,\n    );\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"reset2\",\n    ]);\n  });\n});\n"
  },
  {
    "path": "tests/runtime/useIdleEffect.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, flushIdleCallbacks } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { component, useIdleEffect } from \"ivi\";\n\ndescribe(\"useIdleEffect\", () => {\n  beforeEach(reset);\n\n  test(`#1`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useIdleEffect(c, () => {\n        _trace.push(\"effect1\");\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      null,\n    );\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n    ]);\n  });\n\n  test(`#2`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useIdleEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      null,\n    );\n    deepStrictEqual(_trace, [\n      \"reset1\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n    ]);\n  });\n\n  test(`#3`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useIdleEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      const e2 = useIdleEffect(c, () => {\n        _trace.push(\"effect2\");\n        return () => {\n          _trace.push(\"reset2\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        e2();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"effect1\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      null,\n    );\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"reset2\",\n    ]);\n  });\n\n  test(`#4`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useIdleEffect<number>(c, (i) => {\n        _trace.push(`effect1: ${i}`);\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return (i: number) => {\n        _trace.push(\"render\");\n        e1(i);\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(1),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"effect1: 1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1: 2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1: 2\",\n    ]);\n  });\n\n  test(`#5`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useIdleEffect<number>(c, (i) => {\n        _trace.push(`effect1: ${i}`);\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      }, (a, b) => {\n        _trace.push(`areEqual(${a}, ${b})`);\n        return a === b;\n      });\n      return (i: number) => {\n        _trace.push(\"render\");\n        e1(i);\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(1),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"effect1: 1\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"areEqual(1, 2)\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1: 2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t(2),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n      \"areEqual(2, 2)\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n    ]);\n  });\n\n  test(`#6`, () => {\n    let _trace: string[] = [];\n    const t2 = component((c) => {\n      _trace.push(\"create2\");\n      const e1 = useIdleEffect(c, () => {\n        _trace.push(`effect2`);\n        return () => {\n          _trace.push(\"reset2\");\n        };\n      });\n      return () => {\n        _trace.push(\"render2\");\n        e1();\n        return null;\n      };\n    });\n    const t1 = component((c) => {\n      _trace.push(\"create1\");\n      const e1 = useIdleEffect(c, () => {\n        _trace.push(`effect1`);\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render1\");\n        e1();\n        return t2();\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t1(),\n    );\n    deepStrictEqual(_trace, [\n      \"create1\",\n      \"render1\",\n      \"create2\",\n      \"render2\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"effect1\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t1(),\n    );\n    deepStrictEqual(_trace, [\n      \"render1\",\n      \"render2\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      t1(),\n    );\n    deepStrictEqual(_trace, [\n      \"render1\",\n      \"render2\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"effect1\",\n      \"reset2\",\n      \"effect2\",\n    ]);\n    _trace = [];\n\n    root.update(\n      null,\n    );\n    deepStrictEqual(_trace, [\n      \"reset1\",\n      \"reset2\",\n    ]);\n  });\n\n  test(`unmount before effect #1`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useIdleEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    root.update(\n      null,\n    );\n    deepStrictEqual(_trace, [\n    ]);\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n    ]);\n  });\n\n  test(`unmount before effect #2`, () => {\n    let _trace: string[] = [];\n    const t = component((c) => {\n      _trace.push(\"create\");\n      const e1 = useIdleEffect(c, () => {\n        _trace.push(\"effect1\");\n        return () => {\n          _trace.push(\"reset1\");\n        };\n      });\n      return () => {\n        _trace.push(\"render\");\n        e1();\n        return null;\n      };\n    });\n    const root = createRoot();\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"create\",\n      \"render\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n      \"effect1\",\n    ]);\n    _trace = [];\n    root.update(\n      t(),\n    );\n    deepStrictEqual(_trace, [\n      \"render\",\n    ]);\n    _trace = [];\n    root.update(\n      null,\n    );\n    deepStrictEqual(_trace, [\n      \"reset1\",\n    ]);\n    _trace = [];\n    flushIdleCallbacks();\n    deepStrictEqual(_trace, [\n    ]);\n  });\n});\n"
  },
  {
    "path": "tests/runtime/useMemo.test.ts",
    "content": "import { deepStrictEqual } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { component, useMemo } from \"ivi\";\n\ndescribe(\"useMemo\", () => {\n  beforeEach(reset);\n\n  test(`1`, () => {\n    let _trace: string[] = [];\n    const T = component<number>(() => {\n      const memo = useMemo<number, number>(\n        (a, b) => {\n          _trace.push(`areEqual(${a}, ${b})`);\n          return a === b;\n        },\n        (b) => {\n          _trace.push(`memo(${b})`);\n          return b;\n        },\n      );\n      return (i) => {\n        _trace.push(\"render\");\n        return memo(i);\n      };\n    });\n\n    const root = createRoot();\n    root.update(T(0));\n    deepStrictEqual(_trace, [\"render\", \"memo(0)\"]);\n    _trace = [];\n\n    deepStrictEqual(\n      trace(() => { root.update(T(0)); }),\n      [],\n    );\n    deepStrictEqual(_trace, [\"render\", \"areEqual(0, 0)\"]);\n    _trace = [];\n\n    deepStrictEqual(\n      trace(() => { root.update(T(1)); }),\n      [`[2] Node.nodeValue = 1`],\n    );\n    deepStrictEqual(_trace, [\"render\", \"areEqual(0, 1)\", \"memo(1)\"]);\n  });\n});\n"
  },
  {
    "path": "tests/runtime/useReducer.test.ts",
    "content": "import { deepStrictEqual, ok } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { component, useReducer } from \"ivi\";\n\ndescribe(\"useReducer\", () => {\n  beforeEach(reset);\n\n  test(`1`, () => {\n    let _dispatch: (v: number) => void;\n    let _trace: string[] = [];\n    const T = component((c) => {\n      const [state, dispatch] = useReducer<number, number>(c, 0, (prev, action) => {\n        _trace.push(`dispatch(${prev}, ${action})`);\n        return action;\n      });\n      _dispatch = dispatch;\n      _trace.push(\"create\");\n      return () => {\n        _trace.push(\"render\");\n        return state();\n      };\n    });\n\n    const root = createRoot();\n    root.update(T());\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n\n    deepStrictEqual(\n      trace(() => { _dispatch(0); }),\n      [],\n    );\n    deepStrictEqual(_trace, [\"dispatch(0, 0)\"]);\n    _trace = [];\n    ok(!root.isDirty);\n\n    deepStrictEqual(\n      trace(() => { _dispatch(1); }),\n      [],\n    );\n    deepStrictEqual(_trace, [\"dispatch(0, 1)\"]);\n    _trace = [];\n    ok(root.isDirty);\n    deepStrictEqual(\n      trace(() => { root.dirtyCheck(); }),\n      [`[2] Node.nodeValue = 1`],\n    );\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n});\n"
  },
  {
    "path": "tests/runtime/useState.test.ts",
    "content": "import { deepStrictEqual, ok } from \"node:assert\";\nimport { beforeEach, describe, test } from \"bun:test\";\nimport { reset, trace } from \"@ivi/mock-dom/global\";\nimport { createRoot } from \"ivi/test\";\nimport { component, useState } from \"ivi\";\n\ndescribe(\"useState\", () => {\n  beforeEach(reset);\n\n  test(`1`, () => {\n    let _setState: (v: number) => void;\n    let _trace: string[] = [];\n    const T = component((c) => {\n      const [state, setState] = useState(c, 0);\n      _setState = setState;\n      _trace.push(\"create\");\n      return () => {\n        _trace.push(\"render\");\n        return state();\n      };\n    });\n\n    const root = createRoot();\n    root.update(T());\n    deepStrictEqual(_trace, [\"create\", \"render\"]);\n    _trace = [];\n\n    deepStrictEqual(\n      trace(() => { _setState(0); }),\n      [],\n    );\n    ok(!root.isDirty);\n\n    deepStrictEqual(\n      trace(() => { _setState(1); }),\n      [],\n    );\n    deepStrictEqual(_trace, []);\n    ok(root.isDirty);\n    deepStrictEqual(\n      trace(() => { root.dirtyCheck(); }),\n      [\n        `[2] Node.nodeValue = 1`,\n      ],\n    );\n    deepStrictEqual(_trace, [\"render\"]);\n  });\n});\n"
  },
  {
    "path": "tsconfig.composite.json",
    "content": "{\n  \"compilerOptions\": {\n    \"rootDir\": \"${configDir}/src\",\n    \"outDir\": \"${configDir}/dist\",\n    \"declarationDir\": \"${configDir}/dist\",\n    \"composite\": true,\n    \"incremental\": true,\n    \"target\": \"ES2023\",\n    \"module\": \"ES2022\",\n    \"moduleResolution\": \"Bundler\",\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    \"importHelpers\": true,\n    \"noEmitHelpers\": true,\n    \"removeComments\": false,\n\n    \"strict\": true,\n    \"noImplicitAny\": true,\n    \"noImplicitReturns\": false,\n    \"noImplicitOverride\": true,\n    \"noUnusedLocals\": true,\n    \"useUnknownInCatchVariables\": false,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noUncheckedSideEffectImports\": true,\n    \"skipLibCheck\": true,\n  },\n  \"include\": [\n    \"${configDir}/src/**/*.ts\",\n  ],\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"references\": [\n    { \"path\": \"packages/ivi\" },\n    { \"path\": \"packages/@ivi/rolldown\" },\n    { \"path\": \"packages/@ivi/rollup-plugin\" },\n    { \"path\": \"packages/@ivi/vite-plugin\" },\n    { \"path\": \"packages/@ivi/portal\" },\n    { \"path\": \"packages/@ivi/identity\" },\n    { \"path\": \"packages/@ivi/mock-dom\" },\n  ],\n  \"files\": []\n}\n"
  }
]