[
  {
    "path": ".gitignore",
    "content": "node_modules"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"name\": \"vscode-jest-tests\",\n      \"request\": \"launch\",\n      \"program\": \"${workspaceFolder}/node_modules/jest/bin/jest\",\n      \"args\": [\"--runInBand\", \"--watchAll=false\"],\n      \"cwd\": \"${workspaceFolder}\",\n      \"console\": \"integratedTerminal\",\n      \"internalConsoleOptions\": \"neverOpen\",\n      \"disableOptimisticBPs\": true,\n      \"windows\": {\n        \"program\": \"${workspaceFolder}/node_modules/jest/bin/jest\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"liveServer.settings.port\": 5501\n}"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 zenoslin\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, libribute, 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 all\ncopies 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 THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[CN](README.md) / [EN](README_EN.md)\n\n## mini-vue  [![github](https://img.shields.io/badge/%E5%82%AC%E5%AD%A6%E7%A4%BE-mini--vue-blue)](https://github.com/cuixiaorui/mini-vue)\n\n实现最简 vue3 模型，用于深入学习 vue3， 让你更轻松的理解 vue3 的核心逻辑\n\n## Usage\n\n[B 站](https://www.bilibili.com/video/BV1Zy4y1J73E) 提供了视频讲解使用方式\n\n## Why\n\n当我们需要深入学习 vue3 时，我们就需要看源码来学习，但是像这种工业级别的库，源码中有很多逻辑是用于处理边缘情况或者是兼容处理逻辑，是不利于我们学习的。\n\n我们应该关注于核心逻辑，而这个库的目的就是把 vue3 源码中最核心的逻辑剥离出来，只留下核心逻辑，以供大家学习。\n\n## How\n\n基于 vue3 的功能点，一点一点的拆分出来。\n\n代码命名会保持和源码中的一致，方便大家通过命名去源码中查找逻辑。\n\n### Tasking\n\n#### runtime-core\n\n- [x] 支持组件类型\n- [x] 支持 element 类型\n- [x] 初始化 props\n- [x] setup 可获取 props 和 context\n- [x] 支持 component emit\n- [x] 支持 proxy\n- [x] 可以在 render 函数中获取 setup 返回的对象\n- [x] nextTick 的实现\n- [x] 支持 getCurrentInstance\n- [x] 支持 provide/inject\n- [x] 支持最基础的 slots\n- [x] 支持 Text 类型节点\n- [x] 支持 $el api\n- [x] 支持 watchEffect\n\n\n#### reactivity\n\n目标是用自己的 reactivity 支持现有的 demo 运行\n\n- [x] reactive 的实现\n- [x] ref 的实现\n- [x] readonly 的实现\n- [x] computed 的实现\n- [x] track 依赖收集\n- [x] trigger 触发依赖\n- [x] 支持 isReactive\n- [x] 支持嵌套 reactive\n- [x] 支持 toRaw\n- [x] 支持 effect.scheduler\n- [x] 支持 effect.stop\n- [x] 支持 isReadonly\n- [x] 支持 isProxy\n- [x] 支持 shallowReadonly\n- [x] 支持 proxyRefs\n\n### compiler-core\n- [x] 解析插值\n- [x] 解析 element\n- [x] 解析 text\n\n### runtime-dom\n- [x] 支持 custom renderer \n\n### runtime-test\n- [x] 支持测试 runtime-core 的逻辑\n\n### infrastructure\n- [x] support monorepo with pnpm\n### build\n\n```shell\npnpm build\n```\n\n### example\n\n通过 server 的方式打开 packages/vue/example/\\* 下的 index.html 即可\n\n> \b 推荐使用 [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)\n\n### 初始化\n\n#### 流程图\n![初始化流程图](https://user-images.githubusercontent.com/12064746/138114565-3e0eecbb-7fd0-4203-bf36-5e5fd8003ce0.png)\n\n> 可加 vx：cuixr1314  获取所有脑图(备注：github mini-vue 领取脑图)\n#### 关键函数调用图\n\n\n![关键函数调用图2](https://images-1252602850.cos.ap-beijing.myqcloud.com/20220927170658.png)\n\n> 可以基于函数名快速搜索到源码内容\n\n### update\n\n#### 流程图\n\n![image](https://user-images.githubusercontent.com/12064746/138115157-1f4fb8a2-7e60-412d-96de-12e68eb0288c.png)\n\n#### 关键函数调用图\n\n![image](https://user-images.githubusercontent.com/12064746/138114969-9139e4af-b2df-41b2-a5d9-069d8b41903c.png)\n\n\n> 可以基于函数名快速搜索到源码内容\n\n\n\n### 从零到一实现一遍\n\n自从有了 mini-vue 之后 很多同学都问我 能不能带着他从零到一敲一遍\n\n因为对于源码的学习来讲 看在多遍也不如自己写一遍\n\n为此我把 mini-vue 做成了一套视频课  从零到一带着大家实现一遍 不跳过任何一行代码\n\n当然除了功能上的实现还有编程思想融入到了课程内 \n\n比如 TDD、小步走、重构手法、TPP \n\n> TDD 测试驱动开发 影响了我整个技术生涯 可以说在我认识到 TDD 之后 技术才有了质的飞跃 \n\n课程目录如下:\n\n1. vue3 源码结构的介绍\n2. reactivity 的核心流程\n3. runtime-core 初始化的核心流程\n4. runtime-core 更新的核心流程\n5. setup 环境 -> 集成 jest 做单元测试 & 集成 typescript\n6. 实现 effect 返回 runner\n7. 实现 effect 的 scheduler 功能\n8. 实现 effect 的 stop 功能\n9. 实现 readonly 功能\n10. 实现 isReactive 和 isReadonly \n11. 优化 stop 功能\n12. 实现 reactive 和 readonly 嵌套对象转换功能\n13. 实现 shallowReadonly 功能\n14. 实现 isProxy 功能\n15. 实现 isProxy 功能\n16. 实现 ref 功能\n17. 实现 isRef 和 unRef 功能\n18. 实现 proxyR 功能\n19. 实现 computed 计算属性功能\n20. 实现初始化 component 主流程\n21. 实现 rollup 打包\n22. 实现初始化 element 主流程\n23. 实现组件代理对象\n24. 实现 shapeFlags\n25. 实现注册事件功能\n26. 实现组件 props 功能\n27. 实现组件 emit 功能\n28. 实现组件 slots 功能\n29. 实现 Fragment 和 Text 类型节点\n30. 实现 getCurrentInstance \n31. 实现依赖注入功能 provide/inject\n32. 实现自定义渲染器 custom renderer\n33. 更新 element 流程搭建\n34. 更新 element 的props\n35. 更新 element 的 children\n36. 双端对比 diff 算法1\n37. 双端对比 diff 算法2 - key 的作用\n38. 双端对比 diff 算法3 - 最长子序列的作用\n39. 学习尤大解决 bug 的处理方式\n40. 实现组件更新功能\n41. 实现 nextTick 功能\n42. 编译模块概述\n43. 实现解析插值功能\n44. 实现解析 element 标签\n45. 实现解析 text 功能\n46. 实现解析三种联合类型 template\n47. parse 的实现原理&有限状态机\n48. 实现 transform 功能\n49. 实现代码生成 string 类型\n50. 实现代码生成插值类型\n51. 实现代码生成三种联合类型\n52. 实现编译 template 成 render 函数\n53. 实现 monorepo & 使用 vitest 替换 jest \n\n课程内部包含了 vue3 的三大核心模块：reactivity、runtime 以及 compiler 模块\n\n等你自己手写一遍之后 在去看 vue3 源码或者再去看分析解析 vue3 源码的书籍时你会有不同的体验\n\n除此之外 还录制了课程介绍以及课程试听课\n- [课程介绍](https://www.bilibili.com/video/BV16Z4y1r7Wp)\n- [试听课](https://www.bilibili.com/video/BV1R341177P7)\n- [购买链接](https://cua.h5.xeknow.com/s/xDWLc)\n\n> 可以直接购买 也可以加我 wx: cuixr1314 来咨询这门课是否合适你\n\n除了课程内容以外 还有专门的社群来答疑大家在学习上的问题 😊   \n"
  },
  {
    "path": "README_EN.md",
    "content": "[EN](README.md) / [CN](README.md)\n## mini-vue\n\nImplement the simplest vue3 model for in-depth study of vue3 source code\n\n## Usage\n\n[Bilibili](https://www.bilibili.com/video/BV1Zy4y1J73E) Provides a video explaining how to use it\n\n> Can follow my [Bilibili](https://space.bilibili.com/175301983)，Interpretation of live source code from time to time\n\n## Discuss\n\nYou can join the group to discuss the vue3 source code\n\n\n![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cbe1b6e9c67944828c3e653fd7919dc0~tplv-k3u1fbpfcp-watermark.image)\n\n> with WeChat\n\n## Service\n\nProvide one-to-one video teaching services, and take you to see the mini-vue source code hand in hand\n\n> Can add group communication\n\n## Why\n\nWhen we need to learn vue3 in depth, we need to look at the source code to learn, but like this kind of industrial-level library, there are a lot of logic in the source code for processing edge cases or compatible processing logic, which is not conducive to our learning.\n\nWe should focus on the core logic, and the purpose of this library is to separate the core logic from the vue3 source code, leaving only the core logic for everyone to learn.\n\n## How\n\nBased on the function points of vue3, split it out bit by bit\n\nThe code naming will remain consistent with the source code, so that you can find logic in the source code through naming.\n\n### Tasking\n\n- [x] support component type\n- [x] support element type\n- [x] init props of component\n- [x] context can get props and context in setup\n- [x] support component emit\n- [x] support proxy\n- [x] can get the object returned by setup in the render function\n- [x] Implementation of nextTick\n- [x] support getCurrentInstance\n- [x] support provide/inject\n- [x] support basic slots\n- [x] support text type \n\n### roadmap\n\n- [ ] support english\n- [ ] normalize console.log\n\n### build\n\n```shell\nyarn build\n```\n\n### example\n\nOpen index.html under example/\\* use server\n\n> \b Recommended Use [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)\n\n### Initialization\n\n#### flow chart\n\n![初始化流程图](https://user-images.githubusercontent.com/12064746/138114565-3e0eecbb-7fd0-4203-bf36-5e5fd8003ce0.png)\n\n#### Key function call graph\n\n![关键函数调用图1](https://user-gold-cdn.xitu.io/2020/6/22/172dc07fc42b7d2c?w=1342&h=144&f=png&s=54200)\n\n![关键函数调用图2](https://user-gold-cdn.xitu.io/2020/6/22/172dc08840e25b42?w=1816&h=934&f=png&s=550722)\n\n> The source code content can be quickly searched based on the function name\n\n### update\n\n#### flow chart\n\n![update流程图](https://user-images.githubusercontent.com/12064746/138115157-1f4fb8a2-7e60-412d-96de-12e68eb0288c.png)\n\n#### Key function call graph\n\n![update关键函数调用图](https://user-images.githubusercontent.com/12064746/138114969-9139e4af-b2df-41b2-a5d9-069d8b41903c.png)\n\n> The source code content can be quickly searched based on the function name\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": true,\n  \"version\": \"0.0.1\",\n  \"description\": \"Help you learn more efficiently vue3 source code\",\n  \"main\": \"lib/mini-vue.cjs.js\",\n  \"module\": \"lib/mini-vue.esm.js\",\n  \"scripts\": {\n    \"dev\": \"rollup -c -w\",\n    \"build\": \"rollup -c\",\n    \"test\": \"vitest\"\n  },\n  \"author\": \"cuixiaorui\",\n  \"homepage\": \"https://github.com/cuixiaorui\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@rollup/plugin-commonjs\": \"^13.0.0\",\n    \"@rollup/plugin-node-resolve\": \"^8.1.0\",\n    \"@rollup/plugin-replace\": \"^2.3.3\",\n    \"@rollup/plugin-typescript\": \"^8.2.5\",\n    \"rollup\": \"^2.17.1\",\n    \"rollup-plugin-sourcemaps\": \"^0.6.2\",\n    \"tslib\": \"^2.3.1\",\n    \"typescript\": \"^4.4.3\",\n    \"vitest\": \"^0.22.1\"\n  },\n  \"dependencies\": {\n    \"@vue/reactivity\": \"^3.0.5\",\n    \"pixi.js\": \"^6.2.0\"\n  }\n}\n"
  },
  {
    "path": "packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap",
    "content": "// Vitest Snapshot v1\n\nexports[`element and interpolation 1`] = `\n\"\n        const { toDisplayString : _toDisplayString, createElementVNode : _createElementVNode} = Vue \n\n      \nreturn function render(_ctx) {return _createElementVNode('div', null, 'hi,' + _toDisplayString(_ctx.msg))}\"\n`;\n\nexports[`interpolation module 1`] = `\n\"\n        const { toDisplayString : _toDisplayString} = Vue \n\n      \nreturn function render(_ctx) {return _toDisplayString(_ctx.hello)}\"\n`;\n"
  },
  {
    "path": "packages/compiler-core/__tests__/codegen.spec.ts",
    "content": "import { generate } from \"../src/codegen\";\nimport { baseParse } from \"../src/parse\";\nimport { transform } from \"../src/transform\";\nimport { transformElement } from \"../src/transforms/transformElement\";\nimport { transformExpression } from \"../src/transforms/transformExpression\";\nimport { transformText } from \"../src/transforms/transformText\";\n\ntest(\"interpolation module\", () => {\n  const ast = baseParse(\"{{hello}}\");\n  transform(ast, {\n    nodeTransforms: [transformExpression],\n  });\n\n  const { code } = generate(ast);\n  expect(code).toMatchSnapshot();\n});\n\ntest(\"element and interpolation\", () => {\n  const ast = baseParse(\"<div>hi,{{msg}}</div>\");\n  transform(ast, {\n    nodeTransforms: [transformElement, transformText, transformExpression],\n  });\n\n  const { code } = generate(ast);\n  expect(code).toMatchSnapshot();\n});\n"
  },
  {
    "path": "packages/compiler-core/__tests__/parse.spec.ts",
    "content": "import { ElementTypes, NodeTypes } from \"../src/ast\";\nimport { baseParse } from \"../src/parse\";\n\ndescribe(\"parser\", () => {\n  describe(\"text\", () => {\n    test(\"simple text\", () => {\n      const ast = baseParse(\"some text\");\n      const text = ast.children[0];\n\n      expect(text).toStrictEqual({\n        type: NodeTypes.TEXT,\n        content: \"some text\",\n      });\n    });\n\n    test(\"simple text with invalid end tag\", () => {\n      const ast = baseParse(\"some text</div>\");\n      const text = ast.children[0];\n\n      expect(text).toStrictEqual({\n        type: NodeTypes.TEXT,\n        content: \"some text\",\n      });\n    });\n\n    test(\"text with interpolation\", () => {\n      const ast = baseParse(\"some {{ foo + bar }} text\");\n      const text1 = ast.children[0];\n      const text2 = ast.children[2];\n\n      // ast.children[1] 应该是 interpolation\n      expect(text1).toStrictEqual({\n        type: NodeTypes.TEXT,\n        content: \"some \",\n      });\n      expect(text2).toStrictEqual({\n        type: NodeTypes.TEXT,\n        content: \" text\",\n      });\n    });\n  });\n\n  describe(\"Interpolation\", () => {\n    test(\"simple interpolation\", () => {\n      // 1. 看看是不是一个 {{ 开头的\n      // 2. 是的话，那么就作为 插值来处理\n      // 3. 获取内部 message 的内容即可\n      const ast = baseParse(\"{{message}}\");\n      const interpolation = ast.children[0];\n\n      expect(interpolation).toStrictEqual({\n        type: NodeTypes.INTERPOLATION,\n        content: {\n          type: NodeTypes.SIMPLE_EXPRESSION,\n          content: `message`,\n        },\n      });\n    });\n  });\n\n  describe(\"Element\", () => {\n    test(\"simple div\", () => {\n      const ast = baseParse(\"<div>hello</div>\");\n      const element = ast.children[0];\n\n      expect(element).toStrictEqual({\n        type: NodeTypes.ELEMENT,\n        tag: \"div\",\n        tagType: ElementTypes.ELEMENT,\n        children: [\n          {\n            type: NodeTypes.TEXT,\n            content: \"hello\",\n          },\n        ],\n      });\n    });\n\n    test(\"element with interpolation\", () => {\n      const ast = baseParse(\"<div>{{ msg }}</div>\");\n      const element = ast.children[0];\n\n      expect(element).toStrictEqual({\n        type: NodeTypes.ELEMENT,\n        tag: \"div\",\n        tagType: ElementTypes.ELEMENT,\n        children: [\n          {\n            type: NodeTypes.INTERPOLATION,\n            content: {\n              type: NodeTypes.SIMPLE_EXPRESSION,\n              content: `msg`,\n            },\n          },\n        ],\n      });\n    });\n\n    test(\"element with interpolation and text\", () => {\n      const ast = baseParse(\"<div>hi,{{ msg }}</div>\");\n      const element = ast.children[0];\n\n      expect(element).toStrictEqual({\n        type: NodeTypes.ELEMENT,\n        tag: \"div\",\n        tagType: ElementTypes.ELEMENT,\n        children: [\n          {\n            type: NodeTypes.TEXT,\n            content: \"hi,\",\n          },\n          {\n            type: NodeTypes.INTERPOLATION,\n            content: {\n              type: NodeTypes.SIMPLE_EXPRESSION,\n              content: \"msg\",\n            },\n          },\n        ],\n      });\n    });\n\n    test(\"should throw error when lack end tag  \", () => {\n      expect(() => {\n        baseParse(\"<div><span></div>\");\n      }).toThrow(\"缺失结束标签：span\");\n    });\n  });\n});\n"
  },
  {
    "path": "packages/compiler-core/__tests__/transform.spec.ts",
    "content": "import { baseParse } from \"../src/parse\";\nimport { TO_DISPLAY_STRING } from \"../src/runtimeHelpers\";\nimport { transform } from \"../src/transform\";\ndescribe(\"Compiler: transform\", () => {\n  test(\"context state\", () => {\n    const ast = baseParse(`<div>hello {{ world }}</div>`);\n    console.log(ast);\n\n    // manually store call arguments because context is mutable and shared\n    // across calls\n    const calls: any[] = [];\n    const plugin = (node, context) => {\n      calls.push([node, { ...context }]);\n    };\n\n    transform(ast, {\n      nodeTransforms: [plugin],\n    });\n\n    const div = ast.children[0];\n    expect(calls.length).toBe(4);\n    expect(calls[0]).toMatchObject([\n      ast,\n      {},\n      // TODO\n      //       {\n      //         parent: null,\n      //         currentNode: ast,\n      //       },\n    ]);\n    expect(calls[1]).toMatchObject([\n      div,\n      {},\n      // TODO\n      //   {\n      //     parent: ast,\n      //     currentNode: div,\n      //   },\n    ]);\n    expect(calls[2]).toMatchObject([\n      div.children[0],\n      {},\n      //       {\n      //         parent: div,\n      //         currentNode: div.children[0],\n      //       },\n    ]);\n    expect(calls[3]).toMatchObject([\n      div.children[1],\n      {},\n      //   {\n      //     parent: div,\n      //     currentNode: div.children[1],\n      //   },\n    ]);\n  });\n\n  test(\"should inject toString helper for interpolations\", () => {\n    const ast = baseParse(`{{ foo }}`);\n    transform(ast, {});\n    expect(ast.helpers).toContain(TO_DISPLAY_STRING);\n  });\n});\n"
  },
  {
    "path": "packages/compiler-core/package.json",
    "content": "{\n  \"name\": \"@mini-vue/compiler-core\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/compiler-core\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@mini-vue/shared\": \"workspace:^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/compiler-core/src/ast.ts",
    "content": "import { CREATE_ELEMENT_VNODE } from \"./runtimeHelpers\";\n\nexport const enum NodeTypes {\n  TEXT,\n  ROOT,\n  INTERPOLATION,\n  SIMPLE_EXPRESSION,\n  ELEMENT,\n  COMPOUND_EXPRESSION\n}\n\nexport const enum ElementTypes {\n  ELEMENT,\n}\n\nexport function createSimpleExpression(content) {\n  return {\n    type: NodeTypes.SIMPLE_EXPRESSION,\n    content,\n  };\n}\n\nexport function createInterpolation(content) {\n  return {\n    type: NodeTypes.INTERPOLATION,\n    content: content,\n  };\n}\n\nexport function createVNodeCall(context, tag, props?, children?) {\n  if (context) {\n    context.helper(CREATE_ELEMENT_VNODE);\n  }\n\n  return {\n    // TODO vue3 里面这里的 type 是 VNODE_CALL\n    // 是为了 block 而 mini-vue 里面没有实现 block \n    // 所以创建的是 Element 类型就够用了\n    type: NodeTypes.ELEMENT,\n    tag,\n    props,\n    children,\n  };\n}\n"
  },
  {
    "path": "packages/compiler-core/src/codegen.ts",
    "content": "import { isString } from \"@mini-vue/shared\";\nimport { NodeTypes } from \"./ast\";\nimport {\n  CREATE_ELEMENT_VNODE,\n  helperNameMap,\n  TO_DISPLAY_STRING,\n} from \"./runtimeHelpers\";\n\nexport function generate(ast, options = {}) {\n  // 先生成 context\n  const context = createCodegenContext(ast, options);\n  const { push, mode } = context;\n\n  // 1. 先生成 preambleContext\n\n  if (mode === \"module\") {\n    genModulePreamble(ast, context);\n  } else {\n    genFunctionPreamble(ast, context);\n  }\n\n  const functionName = \"render\";\n\n  const args = [\"_ctx\"];\n\n  // _ctx,aaa,bbb,ccc\n  // 需要把 args 处理成 上面的 string\n  const signature = args.join(\", \");\n  push(`function ${functionName}(${signature}) {`);\n  // 这里需要生成具体的代码内容\n  // 开始生成 vnode tree 的表达式\n  push(\"return \");\n  genNode(ast.codegenNode, context);\n\n  push(\"}\");\n\n  return {\n    code: context.code,\n  };\n}\n\nfunction genFunctionPreamble(ast: any, context: any) {\n  const { runtimeGlobalName, push, newline } = context;\n  const VueBinging = runtimeGlobalName;\n\n  const aliasHelper = (s) => `${helperNameMap[s]} : _${helperNameMap[s]}`;\n\n  if (ast.helpers.length > 0) {\n    push(\n      `\n        const { ${ast.helpers.map(aliasHelper).join(\", \")}} = ${VueBinging} \n\n      `\n    );\n  }\n\n  newline();\n  push(`return `);\n}\n\nfunction genNode(node: any, context: any) {\n  // 生成代码的规则就是读取 node ，然后基于不同的 node 来生成对应的代码块\n  // 然后就是把代码快给拼接到一起就可以了\n\n  switch (node.type) {\n    case NodeTypes.INTERPOLATION:\n      genInterpolation(node, context);\n      break;\n    case NodeTypes.SIMPLE_EXPRESSION:\n      genExpression(node, context);\n      break;\n\n    case NodeTypes.ELEMENT:\n      genElement(node, context);\n      break;\n\n    case NodeTypes.COMPOUND_EXPRESSION:\n      genCompoundExpression(node, context);\n      break;\n\n    case NodeTypes.TEXT:\n      genText(node, context);\n      break;\n\n    default:\n      break;\n  }\n}\n\nfunction genCompoundExpression(node: any, context: any) {\n  const { push } = context;\n  for (let i = 0; i < node.children.length; i++) {\n    const child = node.children[i];\n    if (isString(child)) {\n      push(child);\n    } else {\n      genNode(child, context);\n    }\n  }\n}\n\nfunction genText(node: any, context: any) {\n  // Implement\n  const { push } = context;\n\n  push(`'${node.content}'`);\n}\n\nfunction genElement(node, context) {\n  const { push, helper } = context;\n  const { tag, props, children } = node;\n\n  push(`${helper(CREATE_ELEMENT_VNODE)}(`);\n\n  genNodeList(genNullableArgs([tag, props, children]), context);\n\n  push(`)`);\n}\n\nfunction genNodeList(nodes: any, context: any) {\n  const { push } = context;\n  for (let i = 0; i < nodes.length; i++) {\n    const node = nodes[i];\n\n    if (isString(node)) {\n      push(`${node}`);\n    } else {\n      genNode(node, context);\n    }\n    // node 和 node 之间需要加上 逗号(,)\n    // 但是最后一个不需要 \"div\", [props], [children]\n    if (i < nodes.length - 1) {\n      push(\", \");\n    }\n  }\n}\n\nfunction genNullableArgs(args) {\n  // 把末尾为null 的都删除掉\n  // vue3源码中，后面可能会包含 patchFlag、dynamicProps 等编译优化的信息\n  // 而这些信息有可能是不存在的，所以在这边的时候需要删除掉\n  let i = args.length;\n  // 这里 i-- 用的还是特别的巧妙的\n  // 当为0 的时候自然就退出循环了\n  while (i--) {\n    if (args[i] != null) break;\n  }\n\n  // 把为 falsy 的值都替换成 \"null\"\n  return args.slice(0, i + 1).map((arg) => arg || \"null\");\n}\n\nfunction genExpression(node: any, context: any) {\n  context.push(node.content, node);\n}\n\nfunction genInterpolation(node: any, context: any) {\n  const { push, helper } = context;\n  push(`${helper(TO_DISPLAY_STRING)}(`);\n  genNode(node.content, context);\n  push(\")\");\n}\n\nfunction genModulePreamble(ast, context) {\n  // preamble 就是 import 语句\n  const { push, newline, runtimeModuleName } = context;\n\n  if (ast.helpers.length) {\n    // 比如 ast.helpers 里面有个 [toDisplayString]\n    // 那么生成之后就是 import { toDisplayString as _toDisplayString } from \"vue\"\n    const code = `import {${ast.helpers\n      .map((s) => `${helperNameMap[s]} as _${helperNameMap[s]}`)\n      .join(\", \")} } from ${JSON.stringify(runtimeModuleName)}`;\n\n    push(code);\n  }\n\n  newline();\n  push(`export `);\n}\n\nfunction createCodegenContext(\n  ast: any,\n  { runtimeModuleName = \"vue\", runtimeGlobalName = \"Vue\", mode = \"function\" }\n): any {\n  const context = {\n    code: \"\",\n    mode,\n    runtimeModuleName,\n    runtimeGlobalName,\n    helper(key) {\n      return `_${helperNameMap[key]}`;\n    },\n    push(code) {\n      context.code += code;\n    },\n    newline() {\n      // 换新行\n      // TODO 需要额外处理缩进\n      context.code += \"\\n\";\n    },\n  };\n\n  return context;\n}\n"
  },
  {
    "path": "packages/compiler-core/src/compile.ts",
    "content": "import { generate } from \"./codegen\";\nimport { baseParse } from \"./parse\";\nimport { transform } from \"./transform\";\nimport { transformExpression } from \"./transforms/transformExpression\";\nimport { transformElement } from \"./transforms/transformElement\";\nimport { transformText } from \"./transforms/transformText\";\n\nexport function baseCompile(template, options) {\n  // 1. 先把 template 也就是字符串 parse 成 ast\n  const ast = baseParse(template);\n  // 2. 给 ast 加点料（- -#）\n  transform(\n    ast,\n    Object.assign(options, {\n      nodeTransforms: [transformElement, transformText, transformExpression],\n    })\n  );\n\n  // 3. 生成 render 函数代码\n  return generate(ast);\n}\n"
  },
  {
    "path": "packages/compiler-core/src/index.ts",
    "content": "export { baseCompile } from \"./compile\";\n"
  },
  {
    "path": "packages/compiler-core/src/parse.ts",
    "content": "import { ElementTypes, NodeTypes } from \"./ast\";\n\nconst enum TagType {\n  Start,\n  End,\n}\n\nexport function baseParse(content: string) {\n  const context = createParserContext(content);\n  return createRoot(parseChildren(context, []));\n}\n\nfunction createParserContext(content) {\n  console.log(\"创建 paserContext\");\n  return {\n    source: content,\n  };\n}\n\nfunction parseChildren(context, ancestors) {\n  console.log(\"开始解析 children\");\n  const nodes: any = [];\n\n  while (!isEnd(context, ancestors)) {\n    let node;\n    const s = context.source;\n\n    if (startsWith(s, \"{{\")) {\n      // 看看如果是 {{ 开头的话，那么就是一个插值， 那么去解析他\n      node = parseInterpolation(context);\n    } else if (s[0] === \"<\") {\n      if (s[1] === \"/\") {\n        // 这里属于 edge case 可以不用关心\n        // 处理结束标签\n        if (/[a-z]/i.test(s[2])) {\n          // 匹配 </div>\n          // 需要改变 context.source 的值 -> 也就是需要移动光标\n          parseTag(context, TagType.End);\n          // 结束标签就以为这都已经处理完了，所以就可以跳出本次循环了\n          continue;\n        }\n      } else if (/[a-z]/i.test(s[1])) {\n        node = parseElement(context, ancestors);\n      }\n    }\n\n    if (!node) {\n      node = parseText(context);\n    }\n\n    nodes.push(node);\n  }\n\n  return nodes;\n}\n\nfunction isEnd(context: any, ancestors) {\n  // 检测标签的节点\n  // 如果是结束标签的话，需要看看之前有没有开始标签，如果有的话，那么也应该结束\n  // 这里的一个 edge case 是 <div><span></div>\n  // 像这种情况下，其实就应该报错\n  const s = context.source;\n  if (context.source.startsWith(\"</\")) {\n    // 从后面往前面查\n    // 因为便签如果存在的话 应该是 ancestors 最后一个元素\n    for (let i = ancestors.length - 1; i >= 0; --i) {\n      if (startsWithEndTagOpen(s, ancestors[i].tag)) {\n        return true;\n      }\n    }\n  }\n\n  // 看看 context.source 还有没有值\n  return !context.source;\n}\n\nfunction parseElement(context, ancestors) {\n  // 应该如何解析 tag 呢\n  // <div></div>\n  // 先解析开始 tag\n  const element = parseTag(context, TagType.Start);\n\n  ancestors.push(element);\n  const children = parseChildren(context, ancestors);\n  ancestors.pop();\n\n  // 解析 end tag 是为了检测语法是不是正确的\n  // 检测是不是和 start tag 一致\n  if (startsWithEndTagOpen(context.source, element.tag)) {\n    parseTag(context, TagType.End);\n  } else {\n    throw new Error(`缺失结束标签：${element.tag}`);\n  }\n\n  element.children = children;\n\n  return element;\n}\n\nfunction startsWithEndTagOpen(source: string, tag: string) {\n  // 1. 头部 是不是以  </ 开头的\n  // 2. 看看是不是和 tag 一样\n  return (\n    startsWith(source, \"</\") &&\n    source.slice(2, 2 + tag.length).toLowerCase() === tag.toLowerCase()\n  );\n}\n\nfunction parseTag(context: any, type: TagType): any {\n  // 发现如果不是 > 的话，那么就把字符都收集起来 ->div\n  // 正则\n  const match: any = /^<\\/?([a-z][^\\r\\n\\t\\f />]*)/i.exec(context.source);\n  const tag = match[1];\n\n  // 移动光标\n  // <div\n  advanceBy(context, match[0].length);\n\n  // 暂时不处理 selfClose 标签的情况 ，所以可以直接 advanceBy 1个坐标 <  的下一个就是 >\n  advanceBy(context, 1);\n\n  if (type === TagType.End) return;\n\n  let tagType = ElementTypes.ELEMENT;\n\n  return {\n    type: NodeTypes.ELEMENT,\n    tag,\n    tagType,\n  };\n}\n\nfunction parseInterpolation(context: any) {\n  // 1. 先获取到结束的index\n  // 2. 通过 closeIndex - startIndex 获取到内容的长度 contextLength\n  // 3. 通过 slice 截取内容\n\n  // }} 是插值的关闭\n  // 优化点是从 {{ 后面搜索即可\n  const openDelimiter = \"{{\";\n  const closeDelimiter = \"}}\";\n\n  const closeIndex = context.source.indexOf(\n    closeDelimiter,\n    openDelimiter.length\n  );\n\n  // TODO closeIndex -1 需要报错的\n\n  // 让代码前进2个长度，可以把 {{ 干掉\n  advanceBy(context, 2);\n\n  const rawContentLength = closeIndex - openDelimiter.length;\n  const rawContent = context.source.slice(0, rawContentLength);\n\n  const preTrimContent = parseTextData(context, rawContent.length);\n  const content = preTrimContent.trim();\n\n  // 最后在让代码前进2个长度，可以把 }} 干掉\n  advanceBy(context, closeDelimiter.length);\n\n  return {\n    type: NodeTypes.INTERPOLATION,\n    content: {\n      type: NodeTypes.SIMPLE_EXPRESSION,\n      content,\n    },\n  };\n}\n\nfunction parseText(context): any {\n  console.log(\"解析 text\", context);\n\n  // endIndex 应该看看有没有对应的 <\n  // 比如 hello</div>\n  // 像这种情况下 endIndex 就应该是在 o 这里\n  // {\n  const endTokens = [\"<\", \"{{\"];\n  let endIndex = context.source.length;\n\n  for (let i = 0; i < endTokens.length; i++) {\n    const index = context.source.indexOf(endTokens[i]);\n    // endIndex > index 是需要要 endIndex 尽可能的小\n    // 比如说：\n    // hi, {{123}} <div></div>\n    // 那么这里就应该停到 {{ 这里，而不是停到 <div 这里\n    if (index !== -1 && endIndex > index) {\n      endIndex = index;\n    }\n  }\n\n  const content = parseTextData(context, endIndex);\n\n  return {\n    type: NodeTypes.TEXT,\n    content,\n  };\n}\n\nfunction parseTextData(context: any, length: number): any {\n  console.log(\"解析 textData\");\n  // 1. 直接返回 context.source\n  // 从 length 切的话，是为了可以获取到 text 的值（需要用一个范围来确定）\n  const rawText = context.source.slice(0, length);\n  // 2. 移动光标\n  advanceBy(context, length);\n\n  return rawText;\n}\n\nfunction advanceBy(context, numberOfCharacters) {\n  console.log(\"推进代码\", context, numberOfCharacters);\n  context.source = context.source.slice(numberOfCharacters);\n}\n\nfunction createRoot(children) {\n  return {\n    type: NodeTypes.ROOT,\n    children,\n    helpers: [],\n  };\n}\n\nfunction startsWith(source: string, searchString: string): boolean {\n  return source.startsWith(searchString);\n}\n"
  },
  {
    "path": "packages/compiler-core/src/runtimeHelpers.ts",
    "content": "export const TO_DISPLAY_STRING = Symbol(`toDisplayString`);\nexport const CREATE_ELEMENT_VNODE = Symbol(\"createElementVNode\");\n\nexport const helperNameMap = {\n  [TO_DISPLAY_STRING]: \"toDisplayString\",\n  [CREATE_ELEMENT_VNODE]: \"createElementVNode\"\n};\n"
  },
  {
    "path": "packages/compiler-core/src/transform.ts",
    "content": "import { NodeTypes } from \"./ast\";\nimport { TO_DISPLAY_STRING } from \"./runtimeHelpers\";\n\nexport function transform(root, options = {}) {\n  // 1. 创建 context\n\n  const context = createTransformContext(root, options);\n\n  // 2. 遍历 node\n  traverseNode(root, context);\n\n  createRootCodegen(root, context);\n\n  root.helpers.push(...context.helpers.keys());\n}\n\nfunction traverseNode(node: any, context) {\n  const type: NodeTypes = node.type;\n\n  // 遍历调用所有的 nodeTransforms\n  // 把 node 给到 transform\n  // 用户可以对 node 做处理\n  const nodeTransforms = context.nodeTransforms;\n  const exitFns: any = [];\n  for (let i = 0; i < nodeTransforms.length; i++) {\n    const transform = nodeTransforms[i];\n\n    const onExit = transform(node, context);\n    if (onExit) {\n      exitFns.push(onExit);\n    }\n  }\n\n  switch (type) {\n    case NodeTypes.INTERPOLATION:\n      // 插值的点，在于后续生成 render 代码的时候是获取变量的值\n      context.helper(TO_DISPLAY_STRING);\n      break;\n\n    case NodeTypes.ROOT:\n    case NodeTypes.ELEMENT:\n\n      traverseChildren(node, context);\n      break;\n\n    default:\n      break;\n  }\n\n\n\n  let i = exitFns.length;\n  // i-- 这个很巧妙\n  // 使用 while 是要比 for 快 (可以使用 https://jsbench.me/ 来测试一下)\n  while (i--) {\n    exitFns[i]();\n  }\n}\n\nfunction traverseChildren(parent: any, context: any) {\n  // node.children\n  parent.children.forEach((node) => {\n    // TODO 需要设置 context 的值\n    traverseNode(node, context);\n  });\n}\n\nfunction createTransformContext(root, options): any {\n  const context = {\n    root,\n    nodeTransforms: options.nodeTransforms || [],\n    helpers: new Map(),\n    helper(name) {\n      // 这里会收集调用的次数\n      // 收集次数是为了给删除做处理的， （当只有 count 为0 的时候才需要真的删除掉）\n      // helpers 数据会在后续生成代码的时候用到\n      const count = context.helpers.get(name) || 0;\n      context.helpers.set(name, count + 1);\n    },\n  };\n\n  return context;\n}\n\nfunction createRootCodegen(root: any, context: any) {\n  const { children } = root;\n\n  // 只支持有一个根节点\n  // 并且还是一个 single text node\n  const child = children[0];\n\n  // 如果是 element 类型的话 ， 那么我们需要把它的 codegenNode 赋值给 root\n  // root 其实是个空的什么数据都没有的节点\n  // 所以这里需要额外的处理 codegenNode\n  // codegenNode 的目的是专门为了 codegen 准备的  为的就是和 ast 的 node 分离开\n  if (child.type === NodeTypes.ELEMENT && child.codegenNode) {\n    const codegenNode = child.codegenNode;\n    root.codegenNode = codegenNode;\n  } else {\n    root.codegenNode = child;\n  }\n}\n"
  },
  {
    "path": "packages/compiler-core/src/transforms/transformElement.ts",
    "content": "import { createVNodeCall, NodeTypes } from \"../ast\";\n\nexport function transformElement(node, context) {\n  if (node.type === NodeTypes.ELEMENT) {\n    return () => {\n      // 没有实现 block  所以这里直接创建 element\n\n      // TODO\n      // 需要把之前的 props 和 children 等一系列的数据都处理\n      const vnodeTag = `'${node.tag}'`;\n      // TODO props 暂时不支持\n      const vnodeProps = null;\n      let vnodeChildren = null;\n      if (node.children.length > 0) {\n        if (node.children.length === 1) {\n          // 只有一个孩子节点 ，那么当生成 render 函数的时候就不用 [] 包裹\n          const child = node.children[0];\n          vnodeChildren = child;\n        }\n      }\n\n      // 创建一个新的 node 用于 codegen 的时候使用\n      node.codegenNode = createVNodeCall(\n        context,\n        vnodeTag,\n        vnodeProps,\n        vnodeChildren\n      );\n    };\n  }\n}\n"
  },
  {
    "path": "packages/compiler-core/src/transforms/transformExpression.ts",
    "content": "import { NodeTypes } from \"../ast\";\n\nexport function transformExpression(node) {\n  if (node.type === NodeTypes.INTERPOLATION) {\n    node.content = processExpression(node.content);\n  }\n}\n\nfunction processExpression(node) {\n  node.content = `_ctx.${node.content}`;\n\n  return node\n}\n"
  },
  {
    "path": "packages/compiler-core/src/transforms/transformText.ts",
    "content": "import { NodeTypes } from \"../ast\";\nimport { isText } from \"../utils\";\n\nexport function transformText(node, context) {\n  if (node.type === NodeTypes.ELEMENT) {\n    // 在 exit 的时期执行\n    // 下面的逻辑会改变 ast 树\n    // 有些逻辑是需要在改变之前做处理的\n    return () => {\n      // hi,{{msg}}\n      // 上面的模块会生成2个节点，一个是 text 一个是 interpolation 的话\n      // 生成的 render 函数应该为 \"hi,\" + _toDisplayString(_ctx.msg)\n      // 这里面就会涉及到添加一个 “+” 操作符\n      // 那这里的逻辑就是处理它\n\n      // 检测下一个节点是不是 text 类型，如果是的话， 那么会创建一个 COMPOUND 类型\n      // COMPOUND 类型把 2个 text || interpolation 包裹（相当于是父级容器）\n\n      const children = node.children;\n      let currentContainer;\n\n      for (let i = 0; i < children.length; i++) {\n        const child = children[i];\n\n        if (isText(child)) {\n          // 看看下一个节点是不是 text 类\n          for (let j = i + 1; j < children.length; j++) {\n            const next = children[j];\n            if (isText(next)) {\n              // currentContainer 的目的是把相邻的节点都放到一个 容器内\n              if (!currentContainer) {\n                currentContainer = children[i] = {\n                  type: NodeTypes.COMPOUND_EXPRESSION,\n                  loc: child.loc,\n                  children: [child],\n                };\n              }\n\n              currentContainer.children.push(` + `, next);\n              // 把当前的节点放到容器内, 然后删除掉j\n              children.splice(j, 1);\n              // 因为把 j 删除了，所以这里就少了一个元素，那么 j 需要 --\n              j--;\n            } else {\n              currentContainer = undefined;\n              break;\n            }\n          }\n        }\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "packages/compiler-core/src/utils.ts",
    "content": "import { NodeTypes } from \"./ast\";\n\nexport function isText(node) {\n  return node.type === NodeTypes.INTERPOLATION || node.type === NodeTypes.TEXT;\n}\n"
  },
  {
    "path": "packages/reactivity/__tests__/computed.spec.ts",
    "content": "import { computed } from \"../src/computed\";\nimport { reactive } from \"../src/reactive\";\nimport {vi} from 'vitest'\n\ndescribe(\"computed\", () => {\n  it(\"happy path\", () => {\n    const value = reactive({\n      foo: 1,\n    });\n\n    const getter = computed(() => {\n      return value.foo;\n    });\n\n    value.foo = 2;\n    expect(getter.value).toBe(2);\n  });\n\n  it(\"should compute lazily\", () => {\n    const value = reactive({\n      foo: 1,\n    });\n    const getter = vi.fn(() => {\n      return value.foo;\n    });\n    const cValue = computed(getter);\n\n    // lazy\n    expect(getter).not.toHaveBeenCalled();\n\n    expect(cValue.value).toBe(1);\n    expect(getter).toHaveBeenCalledTimes(1);\n\n    // should not compute again\n    cValue.value;\n    expect(getter).toHaveBeenCalledTimes(1);\n\n    // should not compute until needed\n    value.foo = 2;\n    expect(getter).toHaveBeenCalledTimes(1);\n\n    // now it should compute\n    expect(cValue.value).toBe(2);\n    expect(getter).toHaveBeenCalledTimes(2);\n\n    // should not compute again\n    cValue.value;\n    expect(getter).toHaveBeenCalledTimes(2);\n  });\n});\n"
  },
  {
    "path": "packages/reactivity/__tests__/dep.spec.ts",
    "content": "\ndescribe('Dep', () => {\n\n\n\tit('new Set ', () => {\n\t\t// const set = new Set([1])\n\n\t\t// console.log(set)\n\n\n\t});\n\n\n});"
  },
  {
    "path": "packages/reactivity/__tests__/effect.spec.ts",
    "content": "import { reactive } from \"../src/reactive\";\nimport { effect, stop } from \"../src/effect\";\nimport { vi } from \"vitest\";\n\ndescribe(\"effect\", () => {\n  it(\"should run the passed function once (wrapped by a effect)\", () => {\n    const fnSpy = vi.fn(() => {});\n    effect(fnSpy);\n    expect(fnSpy).toHaveBeenCalledTimes(1);\n  });\n\n  it(\"should observe basic properties\", () => {\n    let dummy;\n    const counter = reactive({ num: 0 });\n    effect(() => (dummy = counter.num));\n\n    expect(dummy).toBe(0);\n    counter.num = 7;\n    expect(dummy).toBe(7);\n  });\n\n  it(\"should observe multiple properties\", () => {\n    let dummy;\n    const counter = reactive({ num1: 0, num2: 0 });\n    effect(() => (dummy = counter.num1 + counter.num1 + counter.num2));\n\n    expect(dummy).toBe(0);\n    counter.num1 = counter.num2 = 7;\n    expect(dummy).toBe(21);\n  });\n  it(\"should handle multiple effects\", () => {\n    let dummy1, dummy2;\n    const counter = reactive({ num: 0 });\n    effect(() => (dummy1 = counter.num));\n    effect(() => (dummy2 = counter.num));\n\n    expect(dummy1).toBe(0);\n    expect(dummy2).toBe(0);\n    counter.num++;\n    expect(dummy1).toBe(1);\n    expect(dummy2).toBe(1);\n  });\n\n  it(\"should observe nested properties\", () => {\n    let dummy;\n    const counter = reactive({ nested: { num: 0 } });\n    effect(() => (dummy = counter.nested.num));\n\n    expect(dummy).toBe(0);\n    counter.nested.num = 8;\n    expect(dummy).toBe(8);\n  });\n\n  it(\"should observe function call chains\", () => {\n    let dummy;\n    const counter = reactive({ num: 0 });\n    effect(() => (dummy = getNum()));\n\n    function getNum() {\n      return counter.num;\n    }\n\n    expect(dummy).toBe(0);\n    counter.num = 2;\n    expect(dummy).toBe(2);\n  });\n  it(\"scheduler\", () => {\n    let dummy;\n    let run: any;\n    const scheduler = vi.fn(() => {\n      run = runner;\n    });\n    const obj = reactive({ foo: 1 });\n    const runner = effect(\n      () => {\n        dummy = obj.foo;\n      },\n      { scheduler }\n    );\n    expect(scheduler).not.toHaveBeenCalled();\n    expect(dummy).toBe(1);\n    // should be called on first trigger\n    obj.foo++;\n    expect(scheduler).toHaveBeenCalledTimes(1);\n    // // should not run yet\n    expect(dummy).toBe(1);\n    // // manually run\n    run();\n    // // should have run\n    expect(dummy).toBe(2);\n  });\n\n  it(\"stop\", () => {\n    let dummy;\n    const obj = reactive({ prop: 1 });\n    const runner = effect(() => {\n      dummy = obj.prop;\n    });\n    obj.prop = 2;\n    expect(dummy).toBe(2);\n    stop(runner);\n    // obj.prop = 3\n    obj.prop++;\n    expect(dummy).toBe(2);\n\n    // stopped effect should still be manually callable\n    runner();\n    expect(dummy).toBe(3);\n  });\n\n  it(\"events: onStop\", () => {\n    const onStop = vi.fn();\n    const runner = effect(() => {}, {\n      onStop,\n    });\n\n    stop(runner);\n    expect(onStop).toHaveBeenCalled();\n  });\n});\n"
  },
  {
    "path": "packages/reactivity/__tests__/reactive.spec.ts",
    "content": "import { reactive, isReactive, toRaw, reactiveMap } from \"../src/reactive\";\ndescribe(\"reactive\", () => {\n  test(\"Object\", () => {\n    const original = { foo: 1 };\n    const observed = reactive(original);\n    expect(observed).not.toBe(original);\n    expect(isReactive(observed)).toBe(true);\n    expect(isReactive(original)).toBe(false);\n    // get\n    expect(observed.foo).toBe(1);\n    //     // has\n    expect(\"foo\" in observed).toBe(true);\n    //     // ownKeys\n    expect(Object.keys(observed)).toEqual([\"foo\"]);\n  });\n\n  test(\"nested reactives\", () => {\n    const original = {\n      nested: {\n        foo: 1,\n      },\n      array: [{ bar: 2 }],\n    };\n    const observed = reactive(original);\n    expect(isReactive(observed.nested)).toBe(true);\n    expect(isReactive(observed.array)).toBe(true);\n    expect(isReactive(observed.array[0])).toBe(true);\n  });\n\n  test(\"toRaw\", () => {\n    const original = { foo: 1 };\n    const observed = reactive(original);\n    expect(toRaw(observed)).toBe(original);\n    expect(toRaw(original)).toBe(original);\n  });\n});\n"
  },
  {
    "path": "packages/reactivity/__tests__/readonly.spec.ts",
    "content": "import { isProxy, isReactive, isReadonly, readonly } from \"../src/reactive\";\n\ndescribe(\"readonly\", () => {\n  it(\"should make nested values readonly\", () => {\n    const original = { foo: 1, bar: { baz: 2 } };\n    const wrapped = readonly(original);\n    expect(wrapped).not.toBe(original);\n    expect(isProxy(wrapped)).toBe(true);\n    expect(isReactive(wrapped)).toBe(false);\n    expect(isReadonly(wrapped)).toBe(true);\n    expect(isReactive(original)).toBe(false);\n    expect(isReadonly(original)).toBe(false);\n    expect(isReactive(wrapped.bar)).toBe(false);\n    expect(isReadonly(wrapped.bar)).toBe(true);\n    expect(isReactive(original.bar)).toBe(false);\n    expect(isReadonly(original.bar)).toBe(false);\n    // get\n    expect(wrapped.foo).toBe(1);\n  });\n});\n"
  },
  {
    "path": "packages/reactivity/__tests__/ref.spec.ts",
    "content": "import { effect } from \"../src/effect\";\nimport { reactive } from \"../src/reactive\";\nimport { isRef, ref, unRef,proxyRefs } from \"../src/ref\";\ndescribe(\"ref\", () => {\n  it(\"should be reactive\", () => {\n    const a = ref(1);\n    let dummy;\n    let calls = 0;\n    effect(() => {\n      calls++;\n      dummy = a.value;\n    });\n    expect(calls).toBe(1);\n    expect(dummy).toBe(1);\n    a.value = 2;\n    expect(calls).toBe(2);\n    expect(dummy).toBe(2);\n    // same value should not trigger\n    a.value = 2;\n    expect(calls).toBe(2);\n    expect(dummy).toBe(2);\n  });\n\n  it(\"should make nested properties reactive\", () => {\n    const a = ref({\n      count: 1,\n    });\n    let dummy;\n    effect(() => {\n      dummy = a.value.count;\n    });\n    expect(dummy).toBe(1);\n    a.value.count = 2;\n    expect(dummy).toBe(2);\n  });\n\n  it(\"proxyRefs\", () => {\n    const user = {\n      age: ref(10),\n      name: \"xiaohong\",\n    };\n    const proxyUser = proxyRefs(user);\n    expect(user.age.value).toBe(10);\n    expect(proxyUser.age).toBe(10);\n    expect(proxyUser.name).toBe(\"xiaohong\");\n\n    (proxyUser as any).age = 20;\n    expect(proxyUser.age).toBe(20);\n    expect(user.age.value).toBe(20);\n\n    proxyUser.age = ref(10);\n    expect(proxyUser.age).toBe(10);\n    expect(user.age.value).toBe(10);\n  });\n\n  it(\"isRef\", () => {\n    const a = ref(1);\n    const user = reactive({\n      age: 1,\n    });\n    expect(isRef(a)).toBe(true);\n    expect(isRef(1)).toBe(false);\n    expect(isRef(user)).toBe(false);\n  });\n\n  it(\"unRef\", () => {\n    const a = ref(1);\n    expect(unRef(a)).toBe(1);\n    expect(unRef(1)).toBe(1);\n  });\n});\n"
  },
  {
    "path": "packages/reactivity/__tests__/shallowReadonly.spec.ts",
    "content": "import { isReactive, isReadonly, readonly, shallowReadonly } from \"../src/reactive\";\n\ndescribe(\"shallowReadonly\", () => {\n  test(\"should not make non-reactive properties reactive\", () => {\n    const props = shallowReadonly({ n: { foo: 1 } });\n    expect(isReactive(props.n)).toBe(false);\n  });\n  test(\"should differentiate from normal readonly calls\", async () => {\n    const original = { foo: {} };\n    const shallowProxy = shallowReadonly(original);\n    const reactiveProxy = readonly(original);\n    expect(shallowProxy).not.toBe(reactiveProxy);\n    expect(isReadonly(shallowProxy.foo)).toBe(false);\n    expect(isReadonly(reactiveProxy.foo)).toBe(true);\n  });\n});\n"
  },
  {
    "path": "packages/reactivity/package.json",
    "content": "{\n  \"name\": \"@mini-vue/reactivity\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/reactivity\",\n  \"scripts\": {\n    \"test\": \"jest\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@mini-vue/shared\": \"workspace:^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/reactivity/src/baseHandlers.ts",
    "content": "import { track, trigger } from \"./effect\";\nimport {\n  reactive,\n  ReactiveFlags,\n  reactiveMap,\n  readonly,\n  readonlyMap,\n  shallowReadonlyMap,\n} from \"./reactive\";\nimport { isObject } from \"@mini-vue/shared\";\n\nconst get = createGetter();\nconst set = createSetter();\nconst readonlyGet = createGetter(true);\nconst shallowReadonlyGet = createGetter(true, true);\n\nfunction createGetter(isReadonly = false, shallow = false) {\n  return function get(target, key, receiver) {\n    const isExistInReactiveMap = () =>\n      key === ReactiveFlags.RAW && receiver === reactiveMap.get(target);\n\n    const isExistInReadonlyMap = () =>\n      key === ReactiveFlags.RAW && receiver === readonlyMap.get(target);\n\n    const isExistInShallowReadonlyMap = () =>\n      key === ReactiveFlags.RAW && receiver === shallowReadonlyMap.get(target);\n\n    if (key === ReactiveFlags.IS_REACTIVE) {\n      return !isReadonly;\n    } else if (key === ReactiveFlags.IS_READONLY) {\n      return isReadonly;\n    } else if (\n      isExistInReactiveMap() ||\n      isExistInReadonlyMap() ||\n      isExistInShallowReadonlyMap()\n    ) {\n      return target;\n    }\n\n    const res = Reflect.get(target, key, receiver);\n\n    // 问题：为什么是 readonly 的时候不做依赖收集呢\n    // readonly 的话，是不可以被 set 的， 那不可以被 set 就意味着不会触发 trigger\n    // 所有就没有收集依赖的必要了\n\n    if (!isReadonly) {\n      // 在触发 get 的时候进行依赖收集\n      track(target, \"get\", key);\n    }\n\n    if (shallow) {\n      return res;\n    }\n\n    if (isObject(res)) {\n      // 把内部所有的是 object 的值都用 reactive 包裹，变成响应式对象\n      // 如果说这个 res 值是一个对象的话，那么我们需要把获取到的 res 也转换成 reactive\n      // res 等于 target[key]\n      return isReadonly ? readonly(res) : reactive(res);\n    }\n\n    return res;\n  };\n}\n\nfunction createSetter() {\n  return function set(target, key, value, receiver) {\n    const result = Reflect.set(target, key, value, receiver);\n\n    // 在触发 set 的时候进行触发依赖\n    trigger(target, \"set\", key);\n\n    return result;\n  };\n}\n\nexport const readonlyHandlers = {\n  get: readonlyGet,\n  set(target, key) {\n    // readonly 的响应式对象不可以修改值\n    console.warn(\n      `Set operation on key \"${String(key)}\" failed: target is readonly.`,\n      target\n    );\n    return true;\n  },\n};\n\nexport const mutableHandlers = {\n  get,\n  set,\n};\n\nexport const shallowReadonlyHandlers = {\n  get: shallowReadonlyGet,\n  set(target, key) {\n    // readonly 的响应式对象不可以修改值\n    console.warn(\n      `Set operation on key \"${String(key)}\" failed: target is readonly.`,\n      target\n    );\n    return true;\n  },\n};\n"
  },
  {
    "path": "packages/reactivity/src/computed.ts",
    "content": "import { createDep } from \"./dep\";\nimport { ReactiveEffect } from \"./effect\";\nimport { trackRefValue, triggerRefValue } from \"./ref\";\n\nexport class ComputedRefImpl {\n  public dep: any;\n  public effect: ReactiveEffect;\n\n  private _dirty: boolean;\n  private _value\n\n  constructor(getter) {\n    this._dirty = true;\n    this.dep = createDep();\n    this.effect = new ReactiveEffect(getter, () => {\n      // scheduler\n      // 只要触发了这个函数说明响应式对象的值发生改变了\n      // 那么就解锁，后续在调用 get 的时候就会重新执行，所以会得到最新的值\n      if (this._dirty) return;\n\n      this._dirty = true;\n      triggerRefValue(this);\n    });\n  }\n\n  get value() {\n    // 收集依赖\n    trackRefValue(this);\n    // 锁上，只可以调用一次\n    // 当数据改变的时候才会解锁\n    // 这里就是缓存实现的核心\n    // 解锁是在 scheduler 里面做的\n    if (this._dirty) {\n      this._dirty = false;\n      // 这里执行 run 的话，就是执行用户传入的 fn\n      this._value = this.effect.run();\n    }\n\n    return this._value;\n  }\n}\n\nexport function computed(getter) {\n  return new ComputedRefImpl(getter);\n}\n"
  },
  {
    "path": "packages/reactivity/src/dep.ts",
    "content": "// 用于存储所有的 effect 对象\nexport function createDep(effects?) {\n  const dep = new Set(effects);\n  return dep;\n}\n"
  },
  {
    "path": "packages/reactivity/src/effect.ts",
    "content": "import { createDep } from \"./dep\";\nimport { extend } from \"@mini-vue/shared\";\n\nlet activeEffect = void 0;\nlet shouldTrack = false;\nconst targetMap = new WeakMap();\n\n// 用于依赖收集\nexport class ReactiveEffect {\n  active = true;\n  deps = [];\n  public onStop?: () => void;\n  constructor(public fn, public scheduler?) {\n    console.log(\"创建 ReactiveEffect 对象\");\n  }\n\n  run() {\n    console.log(\"run\");\n    // 运行 run 的时候，可以控制 要不要执行后续收集依赖的一步\n    // 目前来看的话，只要执行了 fn 那么就默认执行了收集依赖\n    // 这里就需要控制了\n\n    // 是不是收集依赖的变量\n\n    // 执行 fn  但是不收集依赖\n    if (!this.active) {\n      return this.fn();\n    }\n\n    // 执行 fn  收集依赖\n    // 可以开始收集依赖了\n    shouldTrack = true;\n\n    // 执行的时候给全局的 activeEffect 赋值\n    // 利用全局属性来获取当前的 effect\n    activeEffect = this as any;\n    // 执行用户传入的 fn\n    console.log(\"执行用户传入的 fn\");\n    const result = this.fn();\n    // 重置\n    shouldTrack = false;\n    activeEffect = undefined;\n\n    return result;\n  }\n\n  stop() {\n    if (this.active) {\n      // 如果第一次执行 stop 后 active 就 false 了\n      // 这是为了防止重复的调用，执行 stop 逻辑\n      cleanupEffect(this);\n      if (this.onStop) {\n        this.onStop();\n      }\n      this.active = false;\n    }\n  }\n}\n\nfunction cleanupEffect(effect) {\n  // 找到所有依赖这个 effect 的响应式对象\n  // 从这些响应式对象里面把 effect 给删除掉\n  effect.deps.forEach((dep) => {\n    dep.delete(effect);\n  });\n\n  effect.deps.length = 0;\n}\n\nexport function effect(fn, options = {}) {\n  const _effect = new ReactiveEffect(fn);\n\n  // 把用户传过来的值合并到 _effect 对象上去\n  // 缺点就是不是显式的，看代码的时候并不知道有什么值\n  extend(_effect, options);\n  _effect.run();\n\n  // 把 _effect.run 这个方法返回\n  // 让用户可以自行选择调用的时机（调用 fn）\n  const runner: any = _effect.run.bind(_effect);\n  runner.effect = _effect;\n  return runner;\n}\n\nexport function stop(runner) {\n  runner.effect.stop();\n}\n\nexport function track(target, type, key) {\n  if (!isTracking()) {\n    return;\n  }\n  console.log(`触发 track -> target: ${target} type:${type} key:${key}`);\n  // 1. 先基于 target 找到对应的 dep\n  // 如果是第一次的话，那么就需要初始化\n  let depsMap = targetMap.get(target);\n  if (!depsMap) {\n    // 初始化 depsMap 的逻辑\n    depsMap = new Map();\n    targetMap.set(target, depsMap);\n  }\n\n  let dep = depsMap.get(key);\n\n  if (!dep) {\n    dep = createDep();\n\n    depsMap.set(key, dep);\n  }\n\n  trackEffects(dep);\n}\n\nexport function trackEffects(dep) {\n  // 用 dep 来存放所有的 effect\n\n  // TODO\n  // 这里是一个优化点\n  // 先看看这个依赖是不是已经收集了，\n  // 已经收集的话，那么就不需要在收集一次了\n  // 可能会影响 code path change 的情况\n  // 需要每次都 cleanupEffect\n  // shouldTrack = !dep.has(activeEffect!);\n  if (!dep.has(activeEffect)) {\n    dep.add(activeEffect);\n    (activeEffect as any).deps.push(dep);\n  }\n}\n\nexport function trigger(target, type, key) {\n  // 1. 先收集所有的 dep 放到 deps 里面，\n  // 后面会统一处理\n  let deps: Array<any> = [];\n  // dep\n\n  const depsMap = targetMap.get(target);\n\n  if (!depsMap) return;\n\n  // 暂时只实现了 GET 类型\n  // get 类型只需要取出来就可以\n  const dep = depsMap.get(key);\n\n  // 最后收集到 deps 内\n  deps.push(dep);\n\n  const effects: Array<any> = [];\n  deps.forEach((dep) => {\n    // 这里解构 dep 得到的是 dep 内部存储的 effect\n    effects.push(...dep);\n  });\n  // 这里的目的是只有一个 dep ，这个dep 里面包含所有的 effect\n  // 这里的目前应该是为了 triggerEffects 这个函数的复用\n  triggerEffects(createDep(effects));\n}\n\nexport function isTracking() {\n  return shouldTrack && activeEffect !== undefined;\n}\n\nexport function triggerEffects(dep) {\n  // 执行收集到的所有的 effect 的 run 方法\n  for (const effect of dep) {\n    if (effect.scheduler) {\n      // scheduler 可以让用户自己选择调用的时机\n      // 这样就可以灵活的控制调用了\n      // 在 runtime-core 中，就是使用了 scheduler 实现了在 next ticker 中调用的逻辑\n      effect.scheduler();\n    } else {\n      effect.run();\n    }\n  }\n}\n"
  },
  {
    "path": "packages/reactivity/src/index.ts",
    "content": "export {\n  reactive,\n  readonly,\n  shallowReadonly,\n  isReadonly,\n  isReactive,\n  isProxy,\n} from \"./reactive\";\n\nexport { ref, proxyRefs, unRef, isRef } from \"./ref\";\n\nexport { effect, stop, ReactiveEffect } from \"./effect\";\n\nexport { computed } from \"./computed\";\n"
  },
  {
    "path": "packages/reactivity/src/reactive.ts",
    "content": "import {\n  mutableHandlers,\n  readonlyHandlers,\n  shallowReadonlyHandlers,\n} from \"./baseHandlers\";\n\nexport const reactiveMap = new WeakMap();\nexport const readonlyMap = new WeakMap();\nexport const shallowReadonlyMap = new WeakMap();\n\nexport const enum ReactiveFlags {\n  IS_REACTIVE = \"__v_isReactive\",\n  IS_READONLY = \"__v_isReadonly\",\n  RAW = \"__v_raw\",\n}\n\nexport function reactive(target) {\n  return createReactiveObject(target, reactiveMap, mutableHandlers);\n}\n\nexport function readonly(target) {\n  return createReactiveObject(target, readonlyMap, readonlyHandlers);\n}\n\nexport function shallowReadonly(target) {\n  return createReactiveObject(\n    target,\n    shallowReadonlyMap,\n    shallowReadonlyHandlers\n  );\n}\n\nexport function isProxy(value) {\n  return isReactive(value) || isReadonly(value);\n}\n\nexport function isReadonly(value) {\n  return !!value[ReactiveFlags.IS_READONLY];\n}\n\nexport function isReactive(value) {\n  // 如果 value 是 proxy 的话\n  // 会触发 get 操作，而在 createGetter 里面会判断\n  // 如果 value 是普通对象的话\n  // 那么会返回 undefined ，那么就需要转换成布尔值\n  return !!value[ReactiveFlags.IS_REACTIVE];\n}\n\nexport function toRaw(value) {\n  // 如果 value 是 proxy 的话 ,那么直接返回就可以了\n  // 因为会触发 createGetter 内的逻辑\n  // 如果 value 是普通对象的话，\n  // 我们就应该返回普通对象\n  // 只要不是 proxy ，只要是得到了 undefined 的话，那么就一定是普通对象\n  // TODO 这里和源码里面实现的不一样，不确定后面会不会有问题\n  if (!value[ReactiveFlags.RAW]) {\n    return value;\n  }\n\n  return value[ReactiveFlags.RAW];\n}\n\nfunction createReactiveObject(target, proxyMap, baseHandlers) {\n  // 核心就是 proxy\n  // 目的是可以侦听到用户 get 或者 set 的动作\n\n  // 如果命中的话就直接返回就好了\n  // 使用缓存做的优化点\n  const existingProxy = proxyMap.get(target);\n  if (existingProxy) {\n    return existingProxy;\n  }\n\n  const proxy = new Proxy(target, baseHandlers);\n\n  // 把创建好的 proxy 给存起来，\n  proxyMap.set(target, proxy);\n  return proxy;\n}\n"
  },
  {
    "path": "packages/reactivity/src/ref.ts",
    "content": "import { trackEffects, triggerEffects, isTracking } from \"./effect\";\nimport { createDep } from \"./dep\";\nimport { isObject, hasChanged } from \"@mini-vue/shared\";\nimport { reactive } from \"./reactive\";\n\nexport class RefImpl {\n  private _rawValue: any;\n  private _value: any;\n  public dep;\n  public __v_isRef = true;\n\n  constructor(value) {\n    this._rawValue = value;\n    // 看看value 是不是一个对象，如果是一个对象的话\n    // 那么需要用 reactive 包裹一下\n    this._value = convert(value);\n    this.dep = createDep();\n  }\n\n  get value() {\n    // 收集依赖\n    trackRefValue(this);\n    return this._value;\n  }\n\n  set value(newValue) {\n    // 当新的值不等于老的值的话，\n    // 那么才需要触发依赖\n    if (hasChanged(newValue, this._rawValue)) {\n      // 更新值\n      this._value = convert(newValue);\n      this._rawValue = newValue;\n      // 触发依赖\n      triggerRefValue(this);\n    }\n  }\n}\n\nexport function ref(value) {\n  return createRef(value);\n}\n\nfunction convert(value) {\n  return isObject(value) ? reactive(value) : value;\n}\n\nfunction createRef(value) {\n  const refImpl = new RefImpl(value);\n\n  return refImpl;\n}\n\nexport function triggerRefValue(ref) {\n  triggerEffects(ref.dep);\n}\n\nexport function trackRefValue(ref) {\n  if (isTracking()) {\n    trackEffects(ref.dep);\n  }\n}\n\n// 这个函数的目的是\n// 帮助解构 ref\n// 比如在 template 中使用 ref 的时候，直接使用就可以了\n// 例如： const count = ref(0) -> 在 template 中使用的话 可以直接 count\n// 解决方案就是通过 proxy 来对 ref 做处理\n\nconst shallowUnwrapHandlers = {\n  get(target, key, receiver) {\n    // 如果里面是一个 ref 类型的话，那么就返回 .value\n    // 如果不是的话，那么直接返回value 就可以了\n    return unRef(Reflect.get(target, key, receiver));\n  },\n  set(target, key, value, receiver) {\n    const oldValue = target[key];\n    if (isRef(oldValue) && !isRef(value)) {\n      return (target[key].value = value);\n    } else {\n      return Reflect.set(target, key, value, receiver);\n    }\n  },\n};\n\n// 这里没有处理 objectWithRefs 是 reactive 类型的时候\n// TODO reactive 里面如果有 ref 类型的 key 的话， 那么也是不需要调用 ref.value 的\n// （but 这个逻辑在 reactive 里面没有实现）\nexport function proxyRefs(objectWithRefs) {\n  return new Proxy(objectWithRefs, shallowUnwrapHandlers);\n}\n\n// 把 ref 里面的值拿到\nexport function unRef(ref) {\n  return isRef(ref) ? ref.value : ref;\n}\n\nexport function isRef(value) {\n  return !!value.__v_isRef;\n}\n"
  },
  {
    "path": "packages/runtime-core/__tests__/apiWatch.spec.ts",
    "content": "import { reactive } from \"@mini-vue/reactivity\";\nimport { watchEffect } from \"../src/apiWatch\";\nimport { nextTick } from \"../src/scheduler\";\nimport { vi } from \"vitest\";\n\ndescribe(\"api: watch\", () => {\n  it(\"effect\", async () => {\n    const state = reactive({ count: 0 });\n    let dummy;\n    watchEffect(() => {\n      dummy = state.count;\n    });\n    expect(dummy).toBe(0);\n\n    state.count++;\n    await nextTick();\n    expect(dummy).toBe(1);\n  });\n\n  it(\"stopping the watcher (effect)\", async () => {\n    const state = reactive({ count: 0 });\n    let dummy;\n    const stop: any = watchEffect(() => {\n      dummy = state.count;\n    });\n    expect(dummy).toBe(0);\n\n    stop();\n    state.count++;\n    await nextTick();\n    // should not update\n    expect(dummy).toBe(0);\n  });\n\n  it(\"cleanup registration (effect)\", async () => {\n    const state = reactive({ count: 0 });\n    const cleanup = vi.fn();\n    let dummy;\n    const stop: any = watchEffect((onCleanup) => {\n      onCleanup(cleanup);\n      dummy = state.count;\n    });\n    expect(dummy).toBe(0);\n\n    state.count++;\n    await nextTick();\n    expect(cleanup).toHaveBeenCalledTimes(1);\n    expect(dummy).toBe(1);\n\n    stop();\n    expect(cleanup).toHaveBeenCalledTimes(2);\n  });\n});\n"
  },
  {
    "path": "packages/runtime-core/__tests__/componentEmits.spec.ts",
    "content": "import { nodeOps, render, h } from \"@mini-vue/runtime-test\";\nimport {vi} from 'vitest'\n\ndescribe(\"component: emits\", () => {\n  test(\"trigger handlers\", () => {\n    const Foo = {\n      render() {\n        return h(\"foo\");\n      },\n      setup(props, { emit }) {\n        // the `emit` function is bound on component instances\n        emit(\"foo\");\n        emit(\"bar\");\n      },\n    };\n\n    const onfoo = vi.fn();\n    const onBar = vi.fn();\n    const Comp = {\n      render() {\n        return h(Foo, { onfoo, onBar });\n      },\n    };\n    render(h(Comp), nodeOps.createElement(\"div\"));\n\n    expect(onfoo).not.toHaveBeenCalled();\n    // only capitalized or special chars are considered event listeners\n    expect(onBar).toHaveBeenCalled();\n  });\n\n  test(\"trigger camelCase handler\", () => {\n    const Foo = {\n      render() {\n        return h(\"foo\");\n      },\n      setup(props, { emit }) {\n        emit(\"test-event\");\n      },\n    };\n\n    const fooSpy = vi.fn();\n    const Comp = {\n      render() {\n        return h(Foo, { onTestEvent: fooSpy });\n      },\n    };\n    render(h(Comp), nodeOps.createElement(\"div\"));\n\n    expect(fooSpy).toHaveBeenCalledTimes(1);\n  });\n\n  test(\"trigger kebab-case handler\", () => {\n    const Foo = {\n      render() {\n        return h(\"foo\");\n      },\n      setup(props, { emit }) {\n        emit(\"test-event\");\n      },\n    };\n\n    const fooSpy = vi.fn();\n\n    const Comp = {\n      render() {\n        return h(Foo, { \"onTest-event\": fooSpy });\n      },\n    };\n    render(h(Comp), nodeOps.createElement(\"div\"));\n\n    expect(fooSpy).toHaveBeenCalledTimes(1);\n  });\n});\n"
  },
  {
    "path": "packages/runtime-core/__tests__/rendererComponent.spec.ts",
    "content": "import { h } from \"@mini-vue/runtime-dom\";\nimport { nodeOps, render, serializeInner } from \"@mini-vue/runtime-test\";\n\ndescribe(\"renderer: component\", () => {\n  it(\"should create an Component \", () => {\n    const Comp = {\n      render: () => {\n        return h(\"div\");\n      },\n    };\n    const root = nodeOps.createElement(\"div\");\n    render(h(Comp), root);\n    expect(serializeInner(root)).toBe(`<div></div>`);\n  });\n\n  it(\"should create an Component with direct text children\", () => {\n    const Comp = {\n      render: () => {\n        return h(\"div\", null, \"test\");\n      },\n    };\n    const root = nodeOps.createElement(\"div\");\n    render(h(Comp), root);\n    expect(serializeInner(root)).toBe(`<div>test</div>`);\n  });\n});\n"
  },
  {
    "path": "packages/runtime-core/__tests__/rendererElement.spec.ts",
    "content": "import { h } from \"@mini-vue/runtime-core\";\nimport { nodeOps, render, serializeInner as inner } from \"@mini-vue/runtime-test\";\n\ndescribe(\"renderer: element\", () => {\n  let root;\n\n  beforeEach(() => {\n    root = nodeOps.createElement(\"div\");\n  });\n\n  it(\"should create an element\", () => {\n    render(h(\"div\"), root);\n    expect(inner(root)).toBe(\"<div></div>\");\n  });\n\n  it('should create an element with props', () => {\n    render(h('div', { id: 'foo', class: 'bar' },[]), root)\n    expect(inner(root)).toBe('<div id=\"foo\" class=\"bar\"></div>')\n  })\n  it('should create an element with direct text children and props', () => {\n    render(h('div', { id: 'foo' }, \"bar\"), root)\n    expect(inner(root)).toBe('<div id=\"foo\">bar</div>')\n  })\n});\n\n\n"
  },
  {
    "path": "packages/runtime-core/package.json",
    "content": "{\n  \"name\": \"@mini-vue/runtime-core\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/runtime-core\",\n  \"scripts\": {\n    \"test\": \"jest\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@mini-vue/reactivity\": \"workspace:^1.0.0\",\n    \"@mini-vue/shared\": \"workspace:^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/runtime-core/src/.pnpm-debug.log",
    "content": "{\n  \"0 debug pnpm:scope\": {\n    \"selected\": 1,\n    \"workspacePrefix\": \"/Users/cxr/projects/mini-vue/code/mini-vue\"\n  },\n  \"1 error pnpm\": {\n    \"errno\": 1,\n    \"code\": \"ELIFECYCLE\",\n    \"pkgid\": \"@mini-vue/runtime-core@1.0.0\",\n    \"stage\": \"test\",\n    \"script\": \"jest \\\"runtime-core\\\"\",\n    \"pkgname\": \"@mini-vue/runtime-core\",\n    \"err\": {\n      \"name\": \"pnpm\",\n      \"message\": \"@mini-vue/runtime-core@1.0.0 test: `jest \\\"runtime-core\\\"`\\nExit status 1\",\n      \"code\": \"ELIFECYCLE\",\n      \"stack\": \"pnpm: @mini-vue/runtime-core@1.0.0 test: `jest \\\"runtime-core\\\"`\\nExit status 1\\n    at EventEmitter.<anonymous> (/opt/homebrew/Cellar/pnpm/6.32.4/libexec/lib/node_modules/pnpm/dist/pnpm.cjs:105736:20)\\n    at EventEmitter.emit (node:events:527:28)\\n    at ChildProcess.<anonymous> (/opt/homebrew/Cellar/pnpm/6.32.4/libexec/lib/node_modules/pnpm/dist/pnpm.cjs:92297:18)\\n    at ChildProcess.emit (node:events:527:28)\\n    at maybeClose (node:internal/child_process:1092:16)\\n    at Process.ChildProcess._handle.onexit (node:internal/child_process:302:5)\"\n    }\n  }\n}"
  },
  {
    "path": "packages/runtime-core/src/apiInject.ts",
    "content": "import { getCurrentInstance } from \"./component\";\n\nexport function provide(key, value) {\n  const currentInstance = getCurrentInstance();\n\n  if (currentInstance) {\n    let { provides } = currentInstance;\n\n    const parentProvides = currentInstance.parent?.provides;\n\n    // 这里要解决一个问题\n    // 当父级 key 和 爷爷级别的 key 重复的时候，对于子组件来讲，需要取最近的父级别组件的值\n    // 那这里的解决方案就是利用原型链来解决\n    // provides 初始化的时候是在 createComponent 时处理的，当时是直接把 parent.provides 赋值给组件的 provides 的\n    // 所以，如果说这里发现 provides 和 parentProvides 相等的话，那么就说明是第一次做 provide(对于当前组件来讲)\n    // 我们就可以把 parent.provides 作为 currentInstance.provides 的原型重新赋值\n    // 至于为什么不在 createComponent 的时候做这个处理，可能的好处是在这里初始化的话，是有个懒执行的效果（优化点，只有需要的时候在初始化）\n    if (parentProvides === provides) {\n      provides = currentInstance.provides = Object.create(parentProvides);\n    }\n\n    provides[key] = value;\n  }\n}\n\nexport function inject(key, defaultValue) {\n  const currentInstance = getCurrentInstance();\n\n  if (currentInstance) {\n    const provides = currentInstance.parent?.provides;\n\n    if (key in provides) {\n      return provides[key];\n    } else if (defaultValue) {\n      if (typeof defaultValue === \"function\") {\n        return defaultValue();\n      }\n      return defaultValue;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/runtime-core/src/apiWatch.ts",
    "content": "import { ReactiveEffect } from \"@mini-vue/reactivity\";\nimport { queuePreFlushCb } from \"./scheduler\";\n\n// Simple effect.\nexport function watchEffect(effect) {\n  return doWatch(effect);\n}\n\nfunction doWatch(source) {\n  // 把 job 添加到 pre flush 里面\n  // 也就是在视图更新完成之前进行渲染（待确认？）\n  // 当逻辑执行到这里的时候 就已经触发了 watchEffect\n  const job = () => {\n    effect.run();\n  };\n\n  // 当触发 trigger 的时候会调用 scheduler\n  // 这里用 scheduler 的目的就是在更新的时候\n  // 让回调可以在 render 前执行 变成一个异步的行为（这里也可以通过 flush 来改变）\n  const scheduler = () => queuePreFlushCb(job);\n\n  // cleanup 的作用是为了解决初始化的时候不调用 fn(用户传过来的 cleanup)\n  // 第一次执行 watchEffect 的时候 onCleanup 会被调用 而这时候只需要把 fn 赋值给 cleanup 就可以\n  // 当第二次执行 watchEffect 的时候就需要执行 fn 了 也就是 cleanup  \n  let cleanup;\n  const onCleanup = (fn) => {\n    // 当 effect stop 的时候也需要执行 cleanup \n    // 所以可以在 onStop 中直接执行 fn\n    cleanup = effect.onStop = () => {\n      fn();\n    };\n  };\n  // 这里是在执行 effect.run 的时候就会调用的\n  const getter = () => {\n    // 这个的检测就是初始化不执行 cleanup 的关键点\n    if (cleanup) {\n      cleanup();\n    }\n\n    source(onCleanup);\n  };\n\n  const effect = new ReactiveEffect(getter, scheduler);\n\n  // 这里执行的就是 getter\n  effect.run();\n\n  // 返回值为 StopHandle\n  // 只需要调用 stop 即可\n  return () => {\n    effect.stop();\n  };\n}\n"
  },
  {
    "path": "packages/runtime-core/src/component.ts",
    "content": "import { initProps } from \"./componentProps\";\nimport { initSlots } from \"./componentSlots\";\nimport { emit } from \"./componentEmits\";\nimport { PublicInstanceProxyHandlers } from \"./componentPublicInstance\";\nimport { proxyRefs, shallowReadonly } from \"@mini-vue/reactivity\";\nexport function createComponentInstance(vnode, parent) {\n  const instance = {\n    type: vnode.type,\n    vnode,\n    next: null, // 需要更新的 vnode，用于更新 component 类型的组件\n    props: {},\n    parent,\n    provides: parent ? parent.provides : {}, //  获取 parent 的 provides 作为当前组件的初始化值 这样就可以继承 parent.provides 的属性了\n    proxy: null,\n    isMounted: false,\n    attrs: {}, // 存放 attrs 的数据\n    slots: {}, // 存放插槽的数据\n    ctx: {}, // context 对象\n    setupState: {}, // 存储 setup 的返回值\n    emit: () => {},\n  };\n\n  // 在 prod 坏境下的 ctx 只是下面简单的结构\n  // 在 dev 环境下会更复杂\n  instance.ctx = {\n    _: instance,\n  };\n\n  // 赋值 emit\n  // 这里使用 bind 把 instance 进行绑定\n  // 后面用户使用的时候只需要给 event 和参数即可\n  instance.emit = emit.bind(null, instance) as any;\n\n  return instance;\n}\n\nexport function setupComponent(instance) {\n  // 1. 处理 props\n  // 取出存在 vnode 里面的 props\n  const { props, children } = instance.vnode;\n  initProps(instance, props);\n  // 2. 处理 slots\n  initSlots(instance, children);\n\n  // 源码里面有两种类型的 component\n  // 一种是基于 options 创建的\n  // 还有一种是 function 的\n  // 这里处理的是 options 创建的\n  // 叫做 stateful 类型\n  setupStatefulComponent(instance);\n}\n\nfunction setupStatefulComponent(instance) {\n  // todo\n  // 1. 先创建代理 proxy\n  console.log(\"创建 proxy\");\n\n  // proxy 对象其实是代理了 instance.ctx 对象\n  // 我们在使用的时候需要使用 instance.proxy 对象\n  // 因为 instance.ctx 在 prod 和 dev 坏境下是不同的\n  instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);\n  // 用户声明的对象就是 instance.type\n  // const Component = {setup(),render()} ....\n  const Component = instance.type;\n  // 2. 调用 setup\n\n  // 调用 setup 的时候传入 props\n  const { setup } = Component;\n  if (setup) {\n    // 设置当前 currentInstance 的值\n    // 必须要在调用 setup 之前\n    setCurrentInstance(instance);\n\n    const setupContext = createSetupContext(instance);\n    // 真实的处理场景里面应该是只在 dev 环境才会把 props 设置为只读的\n    const setupResult =\n      setup && setup(shallowReadonly(instance.props), setupContext);\n\n    setCurrentInstance(null);\n\n    // 3. 处理 setupResult\n    handleSetupResult(instance, setupResult);\n  } else {\n    finishComponentSetup(instance);\n  }\n}\n\nfunction createSetupContext(instance) {\n  console.log(\"初始化 setup context\");\n  return {\n    attrs: instance.attrs,\n    slots: instance.slots,\n    emit: instance.emit,\n    expose: () => {}, // TODO 实现 expose 函数逻辑\n  };\n}\n\nfunction handleSetupResult(instance, setupResult) {\n  // setup 返回值不一样的话，会有不同的处理\n  // 1. 看看 setupResult 是个什么\n  if (typeof setupResult === \"function\") {\n    // 如果返回的是 function 的话，那么绑定到 render 上\n    // 认为是 render 逻辑\n    // setup(){ return ()=>(h(\"div\")) }\n    instance.render = setupResult;\n  } else if (typeof setupResult === \"object\") {\n    // 返回的是一个对象的话\n    // 先存到 setupState 上\n    // 先使用 @vue/reactivity 里面的 proxyRefs\n    // 后面我们自己构建\n    // proxyRefs 的作用就是把 setupResult 对象做一层代理\n    // 方便用户直接访问 ref 类型的值\n    // 比如 setupResult 里面有个 count 是个 ref 类型的对象，用户使用的时候就可以直接使用 count 了，而不需要在 count.value\n    // 这里也就是官网里面说到的自动结构 Ref 类型\n    instance.setupState = proxyRefs(setupResult);\n  }\n\n  finishComponentSetup(instance);\n}\n\nfunction finishComponentSetup(instance) {\n  // 给 instance 设置 render\n\n  // 先取到用户设置的 component options\n  const Component = instance.type;\n\n  if (!instance.render) {\n    // 如果 compile 有值 并且当组件没有 render 函数，那么就需要把 template 编译成 render 函数\n    if (compile && !Component.render) {\n      if (Component.template) {\n        // 这里就是 runtime 模块和 compile 模块结合点\n        const template = Component.template;\n        Component.render = compile(template);\n      }\n    }\n\n    instance.render = Component.render;\n  }\n\n  // applyOptions()\n}\n\nfunction applyOptions() {\n  // 兼容 vue2.x\n  // todo\n  // options api\n}\n\nlet currentInstance = {};\n// 这个接口暴露给用户，用户可以在 setup 中获取组件实例 instance\nexport function getCurrentInstance(): any {\n  return currentInstance;\n}\n\nexport function setCurrentInstance(instance) {\n  currentInstance = instance;\n}\n\nlet compile;\nexport function registerRuntimeCompiler(_compile) {\n  compile = _compile;\n}\n"
  },
  {
    "path": "packages/runtime-core/src/componentEmits.ts",
    "content": "import { camelize, hyphenate, toHandlerKey } from \"@mini-vue/shared\";\nexport function emit(instance, event: string, ...rawArgs) {\n  // 1. emit 是基于 props 里面的 onXXX 的函数来进行匹配的\n  // 所以我们先从 props 中看看是否有对应的 event handler\n  const props = instance.props;\n  // ex: event -> click 那么这里取的就是 onClick\n  // 让事情变的复杂一点如果是烤肉串命名的话，需要转换成  change-page -> changePage\n  // 需要得到事件名称\n  let handler = props[toHandlerKey(camelize(event))];\n\n  // 如果上面没有匹配的话 那么在检测一下 event 是不是 kebab-case 类型\n  if (!handler) {\n    handler = props[(toHandlerKey(hyphenate(event)))]\n  }\n\n\n  if (handler) {\n    handler(...rawArgs);\n  }\n}\n"
  },
  {
    "path": "packages/runtime-core/src/componentProps.ts",
    "content": "export function initProps(instance, rawProps) {\n  console.log(\"initProps\");\n\n  // TODO\n  // 应该还有 attrs 的概念\n  // attrs\n  // 如果组件声明了 props 的话，那么才可以进入 props 属性内\n  // 不然的话是需要存储在 attrs 内\n  // 这里暂时直接赋值给 instance.props 即可\n  instance.props = rawProps;\n}\n"
  },
  {
    "path": "packages/runtime-core/src/componentPublicInstance.ts",
    "content": "import { hasOwn } from \"@mini-vue/shared\";\n\nconst publicPropertiesMap = {\n  // 当用户调用 instance.proxy.$emit 时就会触发这个函数\n  // i 就是 instance 的缩写 也就是组件实例对象\n  $el: (i) => i.vnode.el,\n  $emit: (i) => i.emit,\n  $slots: (i) => i.slots,\n  $props: (i) => i.props,\n};\n\n// todo 需要让用户可以直接在 render 函数内直接使用 this 来触发 proxy\nexport const PublicInstanceProxyHandlers = {\n  get({ _: instance }, key) {\n    // 用户访问 proxy[key]\n    // 这里就匹配一下看看是否有对应的 function\n    // 有的话就直接调用这个 function\n    const { setupState, props } = instance;\n    console.log(`触发 proxy hook , key -> : ${key}`);\n\n    if (key[0] !== \"$\") {\n      // 说明不是访问 public api\n      // 先检测访问的 key 是否存在于 setupState 中, 是的话直接返回\n      if (hasOwn(setupState, key)) {\n        return setupState[key];\n      } else if (hasOwn(props, key)) {\n        // 看看 key 是不是在 props 中\n        // 代理是可以访问到 props 中的 key 的\n        return props[key];\n      }\n    }\n\n    const publicGetter = publicPropertiesMap[key];\n\n    if (publicGetter) {\n      return publicGetter(instance);\n    }\n  },\n\n  set({ _: instance }, key, value) {\n    const { setupState } = instance;\n\n    if (hasOwn(setupState, key)) {\n      // 有的话 那么就直接赋值\n      setupState[key] = value;\n    }\n\n    return true\n  },\n};\n"
  },
  {
    "path": "packages/runtime-core/src/componentRenderUtils.ts",
    "content": "export function shouldUpdateComponent(prevVNode, nextVNode) {\n  const { props: prevProps } = prevVNode;\n  const { props: nextProps } = nextVNode;\n  //   const emits = component!.emitsOptions;\n\n  // 这里主要是检测组件的 props\n  // 核心：只要是 props 发生改变了，那么这个 component 就需要更新\n\n  // 1. props 没有变化，那么不需要更新\n  if (prevProps === nextProps) {\n    return false;\n  }\n  // 如果之前没有 props，那么就需要看看现在有没有 props 了\n  // 所以这里基于 nextProps 的值来决定是否更新\n  if (!prevProps) {\n    return !!nextProps;\n  }\n  // 之前有值，现在没值，那么肯定需要更新\n  if (!nextProps) {\n    return true;\n  }\n\n  // 以上都是比较明显的可以知道 props 是否是变化的\n  // 在 hasPropsChanged 会做更细致的对比检测\n  return hasPropsChanged(prevProps, nextProps);\n}\n\nfunction hasPropsChanged(prevProps, nextProps): boolean {\n  // 依次对比每一个 props.key\n\n  // 提前对比一下 length ，length 不一致肯定是需要更新的\n  const nextKeys = Object.keys(nextProps);\n  if (nextKeys.length !== Object.keys(prevProps).length) {\n    return true;\n  }\n\n  // 只要现在的 prop 和之前的 prop 不一样那么就需要更新\n  for (let i = 0; i < nextKeys.length; i++) {\n    const key = nextKeys[i];\n    if (nextProps[key] !== prevProps[key]) {\n      return true;\n    }\n  }\n  return false;\n}\n"
  },
  {
    "path": "packages/runtime-core/src/componentSlots.ts",
    "content": "import { ShapeFlags } from \"@mini-vue/shared\";\nexport function initSlots(instance, children) {\n  const { vnode } = instance;\n\n  console.log(\"初始化 slots\");\n\n  if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {\n    normalizeObjectSlots(children, (instance.slots = {}));\n  }\n}\n\nconst normalizeSlotValue = (value) => {\n  // 把 function 返回的值转换成 array ，这样 slot 就可以支持多个元素了\n  return Array.isArray(value) ? value : [value];\n};\n\nconst normalizeObjectSlots = (rawSlots, slots) => {\n  for (const key in rawSlots) {\n    const value = rawSlots[key];\n    if (typeof value === \"function\") {\n      // 把这个函数给到slots 对象上存起来\n      // 后续在 renderSlots 中调用\n      // TODO 这里没有对 value 做 normalize，\n      // 默认 slots 返回的就是一个 vnode 对象\n      slots[key] = (props) => normalizeSlotValue(value(props));\n    }\n  }\n};\n"
  },
  {
    "path": "packages/runtime-core/src/createApp.ts",
    "content": "import { createVNode } from \"./vnode\";\n\nexport function createAppAPI(render) {\n  return function createApp(rootComponent) {\n    const app = {\n      _component: rootComponent,\n      mount(rootContainer) {\n        console.log(\"基于根组件创建 vnode\");\n        const vnode = createVNode(rootComponent);\n        console.log(\"调用 render，基于 vnode 进行开箱\");\n        render(vnode, rootContainer);\n      },\n    };\n\n    return app;\n  };\n}\n"
  },
  {
    "path": "packages/runtime-core/src/h.ts",
    "content": "import { createVNode } from \"./vnode\";\nexport const h = (type: any , props: any = null, children: string | Array<any> = []) => {\n  return createVNode(type, props, children);\n};\n"
  },
  {
    "path": "packages/runtime-core/src/helpers/renderSlot.ts",
    "content": "import { createVNode, Fragment } from \"../vnode\";\n\n/**\n * Compiler runtime helper for rendering `<slot/>`\n * 用来 render slot 的\n * 之前是把 slot 的数据都存在 instance.slots 内(可以看 componentSlot.ts)，\n * 这里就是取数据然后渲染出来的点\n * 这个是由 compiler 模块直接渲染出来的 -可以参看这个 demo https://vue-next-template-explorer.netlify.app/#%7B%22src%22%3A%22%3Cdiv%3E%5Cn%20%20%3Cslot%3E%3C%2Fslot%3E%5Cn%3C%2Fdiv%3E%22%2C%22ssr%22%3Afalse%2C%22options%22%3A%7B%22mode%22%3A%22module%22%2C%22prefixIdentifiers%22%3Afalse%2C%22optimizeImports%22%3Afalse%2C%22hoistStatic%22%3Afalse%2C%22cacheHandlers%22%3Afalse%2C%22scopeId%22%3Anull%2C%22inline%22%3Afalse%2C%22ssrCssVars%22%3A%22%7B%20color%20%7D%22%2C%22bindingMetadata%22%3A%7B%22TestComponent%22%3A%22setup-const%22%2C%22setupRef%22%3A%22setup-ref%22%2C%22setupConst%22%3A%22setup-const%22%2C%22setupLet%22%3A%22setup-let%22%2C%22setupMaybeRef%22%3A%22setup-maybe-ref%22%2C%22setupProp%22%3A%22props%22%2C%22vMySetupDir%22%3A%22setup-const%22%7D%2C%22optimizeBindings%22%3Afalse%7D%7D\n * 其最终目的就是在 render 函数中调用 renderSlot 取 instance.slots 内的数据\n * TODO 这里应该是一个返回一个 block ,但是暂时还没有支持 block ，所以这个暂时只需要返回一个 vnode 即可\n * 因为 block 的本质就是返回一个 vnode\n *\n * @private\n */\nexport function renderSlot(slots, name: string, props = {}) {\n  const slot = slots[name];\n  console.log(`渲染插槽 slot -> ${name}`);\n  if (slot) {\n    // 因为 slot 是一个返回 vnode 的函数，我们只需要把这个结果返回出去即可\n    // slot 就是一个函数，所以就可以把当前组件的一些数据给传出去，这个就是作用域插槽\n    // 参数就是 props\n    const slotContent = slot(props);\n    return createVNode(Fragment, {}, slotContent);\n  }\n}\n"
  },
  {
    "path": "packages/runtime-core/src/index.ts",
    "content": "export * from \"./h\";\nexport * from \"./createApp\";\nexport { getCurrentInstance, registerRuntimeCompiler } from \"./component\";\nexport { inject, provide } from \"./apiInject\";\nexport { renderSlot } from \"./helpers/renderSlot\";\nexport { createTextVNode, createElementVNode } from \"./vnode\";\nexport { createRenderer } from \"./renderer\";\nexport { toDisplayString } from \"@mini-vue/shared\";\nexport { watchEffect } from \"./apiWatch\";\nexport {\n  // core\n  reactive,\n  ref,\n  readonly,\n  // utilities\n  unRef,\n  proxyRefs,\n  isReadonly,\n  isReactive,\n  isProxy,\n  isRef,\n  // advanced\n  shallowReadonly,\n  // effect\n  effect,\n  stop,\n  computed,\n} from \"@mini-vue/reactivity\";\n"
  },
  {
    "path": "packages/runtime-core/src/renderer.ts",
    "content": "import { ShapeFlags } from \"@mini-vue/shared\";\nimport { createComponentInstance } from \"./component\";\nimport { queueJob } from \"./scheduler\";\nimport { effect } from \"@mini-vue/reactivity\";\nimport { setupComponent } from \"./component\";\nimport { Fragment, normalizeVNode, Text } from \"./vnode\";\nimport { shouldUpdateComponent } from \"./componentRenderUtils\";\nimport { createAppAPI } from \"./createApp\";\n\nexport function createRenderer(options) {\n  const {\n    createElement: hostCreateElement,\n    setElementText: hostSetElementText,\n    patchProp: hostPatchProp,\n    insert: hostInsert,\n    remove: hostRemove,\n    setText: hostSetText,\n    createText: hostCreateText,\n  } = options;\n\n  const render = (vnode, container) => {\n    console.log(\"调用 patch\")\n    patch(null, vnode, container);\n  };\n\n  function patch(\n    n1,\n    n2,\n    container = null,\n    anchor = null,\n    parentComponent = null\n  ) {\n    // 基于 n2 的类型来判断\n    // 因为 n2 是新的 vnode\n    const { type, shapeFlag } = n2;\n    switch (type) {\n      case Text:\n        processText(n1, n2, container);\n        break;\n      // 其中还有几个类型比如： static fragment comment\n      case Fragment:\n        processFragment(n1, n2, container);\n        break;\n      default:\n        // 这里就基于 shapeFlag 来处理\n        if (shapeFlag & ShapeFlags.ELEMENT) {\n          console.log(\"处理 element\");\n          processElement(n1, n2, container, anchor, parentComponent);\n        } else if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {\n          console.log(\"处理 component\");\n          processComponent(n1, n2, container, parentComponent);\n        }\n    }\n  }\n\n  function processFragment(n1: any, n2: any, container: any) {\n    // 只需要渲染 children ，然后给添加到 container 内\n    if (!n1) {\n      // 初始化 Fragment 逻辑点\n      console.log(\"初始化 Fragment 类型的节点\");\n      mountChildren(n2.children, container);\n    }\n  }\n\n  function processText(n1, n2, container) {\n    console.log(\"处理 Text 节点\");\n    if (n1 === null) {\n      // n1 是 null 说明是 init 的阶段\n      // 基于 createText 创建出 text 节点，然后使用 insert 添加到 el 内\n      console.log(\"初始化 Text 类型的节点\");\n      hostInsert((n2.el = hostCreateText(n2.children as string)), container);\n    } else {\n      // update\n      // 先对比一下 updated 之后的内容是否和之前的不一样\n      // 在不一样的时候才需要 update text\n      // 这里抽离出来的接口是 setText\n      // 注意，这里一定要记得把 n1.el 赋值给 n2.el, 不然后续是找不到值的\n      const el = (n2.el = n1.el!);\n      if (n2.children !== n1.children) {\n        console.log(\"更新 Text 类型的节点\");\n        hostSetText(el, n2.children as string);\n      }\n    }\n  }\n\n  function processElement(n1, n2, container, anchor, parentComponent) {\n    if (!n1) {\n      mountElement(n2, container, anchor);\n    } else {\n      // todo\n      updateElement(n1, n2, container, anchor, parentComponent);\n    }\n  }\n\n  function updateElement(n1, n2, container, anchor, parentComponent) {\n    const oldProps = (n1 && n1.props) || {};\n    const newProps = n2.props || {};\n    // 应该更新 element\n    console.log(\"应该更新 element\");\n    console.log(\"旧的 vnode\", n1);\n    console.log(\"新的 vnode\", n2);\n\n    // 需要把 el 挂载到新的 vnode\n    const el = (n2.el = n1.el);\n\n    // 对比 props\n    patchProps(el, oldProps, newProps);\n\n    // 对比 children\n    patchChildren(n1, n2, el, anchor, parentComponent);\n  }\n\n  function patchProps(el, oldProps, newProps) {\n    // 对比 props 有以下几种情况\n    // 1. oldProps 有，newProps 也有，但是 val 值变更了\n    // 举个栗子\n    // 之前: oldProps.id = 1 ，更新后：newProps.id = 2\n\n    // key 存在 oldProps 里 也存在 newProps 内\n    // 以 newProps 作为基准\n    for (const key in newProps) {\n      const prevProp = oldProps[key];\n      const nextProp = newProps[key];\n      if (prevProp !== nextProp) {\n        // 对比属性\n        // 需要交给 host 来更新 key\n        hostPatchProp(el, key, prevProp, nextProp);\n      }\n    }\n\n    // 2. oldProps 有，而 newProps 没有了\n    // 之前： {id:1,tId:2}  更新后： {id:1}\n    // 这种情况下我们就应该以 oldProps 作为基准，因为在 newProps 里面是没有的 tId 的\n    // \b还需要注意一点，如果这个 key 在 newProps 里面已经存在了，说明已经处理过了，就不要在处理了\n    for (const key in oldProps) {\n      const prevProp = oldProps[key];\n      const nextProp = null;\n      if (!(key in newProps)) {\n        // 这里是以 oldProps 为基准来遍历，\n        // 而且得到的值是 newProps 内没有的\n        // 所以交给 host 更新的时候，把新的值设置为 null\n        hostPatchProp(el, key, prevProp, nextProp);\n      }\n    }\n  }\n\n  function patchChildren(n1, n2, container, anchor, parentComponent) {\n    const { shapeFlag: prevShapeFlag, children: c1 } = n1;\n    const { shapeFlag, children: c2 } = n2;\n\n    // 如果 n2 的 children 是 text 类型的话\n    // 就看看和之前的 n1 的 children 是不是一样的\n    // 如果不一样的话直接重新设置一下 text 即可\n    if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {\n      if (c2 !== c1) {\n        console.log(\"类型为 text_children, 当前需要更新\");\n        hostSetElementText(container, c2 as string);\n      }\n    } else {\n      // 看看之前的是不是 text\n      if (prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {\n        // 先清空\n        // 然后在把新的 children 给 mount 生成 element\n        hostSetElementText(container, \"\");\n        mountChildren(c2, container);\n      } else {\n        // array diff array\n      // 如果之前是 array_children\n      // 现在还是 array_children 的话\n      // 那么我们就需要对比两个 children 啦\n        patchKeyedChildren(c1, c2, container, parentComponent, anchor);\n      }\n\n    }\n  }\n\n  function patchKeyedChildren(\n    c1: any[],\n    c2: any[],\n    container,\n    parentAnchor,\n    parentComponent\n  ) {\n    let i = 0;\n    const l2 = c2.length;\n    let e1 = c1.length - 1;\n    let e2 = l2 - 1;\n\n    const isSameVNodeType = (n1, n2) => {\n      return n1.type === n2.type && n1.key === n2.key;\n    };\n\n    while (i <= e1 && i <= e2) {\n      const prevChild = c1[i];\n      const nextChild = c2[i];\n\n      if (!isSameVNodeType(prevChild, nextChild)) {\n        console.log(\"两个 child 不相等(从左往右比对)\");\n        console.log(`prevChild:${prevChild}`);\n        console.log(`nextChild:${nextChild}`);\n        break;\n      }\n\n      console.log(\"两个 child 相等，接下来对比这两个 child 节点(从左往右比对)\");\n      patch(prevChild, nextChild, container, parentAnchor, parentComponent);\n      i++;\n    }\n\n    while (i <= e1 && i <= e2) {\n      // 从右向左取值\n      const prevChild = c1[e1];\n      const nextChild = c2[e2];\n\n      if (!isSameVNodeType(prevChild, nextChild)) {\n        console.log(\"两个 child 不相等(从右往左比对)\");\n        console.log(`prevChild:${prevChild}`);\n        console.log(`nextChild:${nextChild}`);\n        break;\n      }\n      console.log(\"两个 child 相等，接下来对比这两个 child 节点(从右往左比对)\");\n      patch(prevChild, nextChild, container, parentAnchor, parentComponent);\n      e1--;\n      e2--;\n    }\n\n    if (i > e1 && i <= e2) {\n      // 如果是这种情况的话就说明 e2 也就是新节点的数量大于旧节点的数量\n      // 也就是说新增了 vnode\n      // 应该循环 c2\n      // 锚点的计算：新的节点有可能需要添加到尾部，也可能添加到头部，所以需要指定添加的问题\n      // 要添加的位置是当前的位置(e2 开始)+1\n      // 因为对于往左侧添加的话，应该获取到 c2 的第一个元素\n      // 所以我们需要从 e2 + 1 取到锚点的位置\n      const nextPos = e2 + 1;\n      const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;\n      while (i <= e2) {\n        console.log(`需要新创建一个 vnode: ${c2[i].key}`);\n        patch(null, c2[i], container, anchor, parentComponent);\n        i++;\n      }\n    } else if (i > e2 && i <= e1) {\n      // 这种情况的话说明新节点的数量是小于旧节点的数量的\n      // 那么我们就需要把多余的\n      while (i <= e1) {\n        console.log(`需要删除当前的 vnode: ${c1[i].key}`);\n        hostRemove(c1[i].el);\n        i++;\n      }\n    } else {\n      // 左右两边都比对完了，然后剩下的就是中间部位顺序变动的\n      // 例如下面的情况\n      // a,b,[c,d,e],f,g\n      // a,b,[e,c,d],f,g\n\n      let s1 = i;\n      let s2 = i;\n      const keyToNewIndexMap = new Map();\n      let moved = false;\n      let maxNewIndexSoFar = 0;\n      // 先把 key 和 newIndex 绑定好，方便后续基于 key 找到 newIndex\n      // 时间复杂度是 O(1)\n      for (let i = s2; i <= e2; i++) {\n        const nextChild = c2[i];\n        keyToNewIndexMap.set(nextChild.key, i);\n      }\n\n      // 需要处理新节点的数量\n      const toBePatched = e2 - s2 + 1;\n      let patched = 0;\n      // 初始化 从新的index映射为老的index\n      // 创建数组的时候给定数组的长度，这个是性能最快的写法\n      const newIndexToOldIndexMap = new Array(toBePatched);\n      // 初始化为 0 , 后面处理的时候 如果发现是 0 的话，那么就说明新值在老的里面不存在\n      for (let i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0;\n\n      // 遍历老节点\n      // 1. 需要找出老节点有，而新节点没有的 -> 需要把这个节点删除掉\n      // 2. 新老节点都有的，—> 需要 patch\n      for (i = s1; i <= e1; i++) {\n        const prevChild = c1[i];\n\n        // 优化点\n        // 如果老的节点大于新节点的数量的话，那么这里在处理老节点的时候就直接删除即可\n        if (patched >= toBePatched) {\n          hostRemove(prevChild.el);\n          continue;\n        }\n\n        let newIndex;\n        if (prevChild.key != null) {\n          // 这里就可以通过key快速的查找了， 看看在新的里面这个节点存在不存在\n          // 时间复杂度O(1)\n          newIndex = keyToNewIndexMap.get(prevChild.key);\n        } else {\n          // 如果没key 的话，那么只能是遍历所有的新节点来确定当前节点存在不存在了\n          // 时间复杂度O(n)\n          for (let j = s2; j <= e2; j++) {\n            if (isSameVNodeType(prevChild, c2[j])) {\n              newIndex = j;\n              break;\n            }\n          }\n        }\n\n        // 因为有可能 nextIndex 的值为0（0也是正常值）\n        // 所以需要通过值是不是 undefined 或者 null 来判断\n        if (newIndex === undefined) {\n          // 当前节点的key 不存在于 newChildren 中，需要把当前节点给删除掉\n          hostRemove(prevChild.el);\n        } else {\n          // 新老节点都存在\n          console.log(\"新老节点都存在\");\n          // 把新节点的索引和老的节点的索引建立映射关系\n          // i + 1 是因为 i 有可能是0 (0 的话会被认为新节点在老的节点中不存在)\n          newIndexToOldIndexMap[newIndex - s2] = i + 1;\n          // 来确定中间的节点是不是需要移动\n          // 新的 newIndex 如果一直是升序的话，那么就说明没有移动\n          // 所以我们可以记录最后一个节点在新的里面的索引，然后看看是不是升序\n          // 不是升序的话，我们就可以确定节点移动过了\n          if (newIndex >= maxNewIndexSoFar) {\n            maxNewIndexSoFar = newIndex;\n          } else {\n            moved = true;\n          }\n\n          patch(prevChild, c2[newIndex], container, null, parentComponent);\n          patched++;\n        }\n      }\n\n      // 利用最长递增子序列来优化移动逻辑\n      // 因为元素是升序的话，那么这些元素就是不需要移动的\n      // 而我们就可以通过最长递增子序列来获取到升序的列表\n      // 在移动的时候我们去对比这个列表，如果对比上的话，就说明当前元素不需要移动\n      // 通过 moved 来进行优化，如果没有移动过的话 那么就不需要执行算法\n      // getSequence 返回的是 newIndexToOldIndexMap 的索引值\n      // 所以后面我们可以直接遍历索引值来处理，也就是直接使用 toBePatched 即可\n      const increasingNewIndexSequence = moved\n        ? getSequence(newIndexToOldIndexMap)\n        : [];\n      let j = increasingNewIndexSequence.length - 1;\n\n      // 遍历新节点\n      // 1. 需要找出老节点没有，而新节点有的 -> 需要把这个节点创建\n      // 2. 最后需要移动一下位置，比如 [c,d,e] -> [e,c,d]\n\n      // 这里倒循环是因为在 insert 的时候，需要保证锚点是处理完的节点（也就是已经确定位置了）\n      // 因为 insert 逻辑是使用的 insertBefore()\n      for (let i = toBePatched - 1; i >= 0; i--) {\n        // 确定当前要处理的节点索引\n        const nextIndex = s2 + i;\n        const nextChild = c2[nextIndex];\n        // 锚点等于当前节点索引+1\n        // 也就是当前节点的后面一个节点(又因为是倒遍历，所以锚点是位置确定的节点)\n        const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;\n\n        if (newIndexToOldIndexMap[i] === 0) {\n          // 说明新节点在老的里面不存在\n          // 需要创建\n          patch(null, nextChild, container, anchor, parentComponent);\n        } else if (moved) {\n          // 需要移动\n          // 1. j 已经没有了 说明剩下的都需要移动了\n          // 2. 最长子序列里面的值和当前的值匹配不上， 说明当前元素需要移动\n          if (j < 0 || increasingNewIndexSequence[j] !== i) {\n            // 移动的话使用 insert 即可\n            hostInsert(nextChild.el, container, anchor);\n          } else {\n            // 这里就是命中了  index 和 最长递增子序列的值\n            // 所以可以移动指针了\n            j--;\n          }\n        }\n      }\n    }\n  }\n\n  function mountElement(vnode, container, anchor) {\n    const { shapeFlag, props } = vnode;\n    // 1. 先创建 element\n    // 基于可扩展的渲染 api\n    const el = (vnode.el = hostCreateElement(vnode.type));\n\n    // 支持单子组件和多子组件的创建\n    if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {\n      // 举个栗子\n      // render(){\n      //     return h(\"div\",{},\"test\")\n      // }\n      // 这里 children 就是 test ，只需要渲染一下就完事了\n      console.log(`处理文本:${vnode.children}`);\n      hostSetElementText(el, vnode.children);\n    } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {\n      // 举个栗子\n      // render(){\n      // Hello 是个 component\n      //     return h(\"div\",{},[h(\"p\"),h(Hello)])\n      // }\n      // 这里 children 就是个数组了，就需要依次调用 patch 递归来处理\n      mountChildren(vnode.children, el);\n    }\n\n    // 处理 props\n    if (props) {\n      for (const key in props) {\n        // todo\n        // 需要过滤掉vue自身用的key\n        // 比如生命周期相关的 key: beforeMount、mounted\n        const nextVal = props[key];\n        hostPatchProp(el, key, null, nextVal);\n      }\n    }\n\n    // todo\n    // 触发 beforeMount() 钩子\n    console.log(\"vnodeHook  -> onVnodeBeforeMount\");\n    console.log(\"DirectiveHook  -> beforeMount\");\n    console.log(\"transition  -> beforeEnter\");\n\n    // 插入\n    hostInsert(el, container, anchor);\n\n    // todo\n    // 触发 mounted() 钩子\n    console.log(\"vnodeHook  -> onVnodeMounted\");\n    console.log(\"DirectiveHook  -> mounted\");\n    console.log(\"transition  -> enter\");\n  }\n\n  function mountChildren(children, container) {\n    children.forEach((VNodeChild) => {\n      // todo\n      // 这里应该需要处理一下 vnodeChild\n      // 因为有可能不是 vnode 类型\n      console.log(\"mountChildren:\", VNodeChild);\n      patch(null, VNodeChild, container);\n    });\n  }\n\n  function processComponent(n1, n2, container, parentComponent) {\n    // 如果 n1 没有值的话，那么就是 mount\n    if (!n1) {\n      // 初始化 component\n      mountComponent(n2, container, parentComponent);\n    } else {\n      updateComponent(n1, n2, container);\n    }\n  }\n\n  // 组件的更新\n  function updateComponent(n1, n2, container) {\n    console.log(\"更新组件\", n1, n2);\n    // 更新组件实例引用\n    const instance = (n2.component = n1.component);\n    // 先看看这个组件是否应该更新\n    if (shouldUpdateComponent(n1, n2)) {\n      console.log(`组件需要更新: ${instance}`);\n      // 那么 next 就是新的 vnode 了（也就是 n2）\n      instance.next = n2;\n      // 这里的 update 是在 setupRenderEffect 里面初始化的，update 函数除了当内部的响应式对象发生改变的时候会调用\n      // 还可以直接主动的调用(这是属于 effect 的特性)\n      // 调用 update 再次更新调用 patch 逻辑\n      // 在update 中调用的 next 就变成了 n2了\n      // ps：可以详细的看看 update 中 next 的应用\n      // TODO 需要在 update 中处理支持 next 的逻辑\n      instance.update();\n    } else {\n      console.log(`组件不需要更新: ${instance}`);\n      // 不需要更新的话，那么只需要覆盖下面的属性即可\n      n2.component = n1.component;\n      n2.el = n1.el;\n      instance.vnode = n2;\n    }\n  }\n\n  function mountComponent(initialVNode, container, parentComponent) {\n    // 1. 先创建一个 component instance\n    const instance = (initialVNode.component = createComponentInstance(\n      initialVNode,\n      parentComponent\n    ));\n    console.log(`创建组件实例:${instance.type.name}`);\n    // 2. 给 instance 加工加工\n    setupComponent(instance);\n\n    setupRenderEffect(instance, initialVNode, container);\n  }\n\n  function setupRenderEffect(instance, initialVNode, container) {\n    // 调用 render\n    // 应该传入 ctx 也就是 proxy\n    // ctx 可以选择暴露给用户的 api\n    // 源代码里面是调用的 renderComponentRoot 函数\n    // 这里为了简化直接调用 render\n\n    // obj.name  = \"111\"\n    // obj.name = \"2222\"\n    // 从哪里做一些事\n    // 收集数据改变之后要做的事 (函数)\n    // 依赖收集   effect 函数\n    // 触发依赖\n    function componentUpdateFn() {\n      if (!instance.isMounted) {\n        // 组件初始化的时候会执行这里\n        // 为什么要在这里调用 render 函数呢\n        // 是因为在 effect 内调用 render 才能触发依赖收集\n        // 等到后面响应式的值变更后会再次触发这个函数\n        console.log(`${instance.type.name}:调用 render,获取 subTree`);\n        const proxyToUse = instance.proxy;\n        // 可在 render 函数中通过 this 来使用 proxy\n        const subTree = (instance.subTree = normalizeVNode(\n          instance.render.call(proxyToUse, proxyToUse)\n        ));\n        console.log(\"subTree\", subTree);\n\n        // todo\n        console.log(`${instance.type.name}:触发 beforeMount hook`);\n        console.log(`${instance.type.name}:触发 onVnodeBeforeMount hook`);\n\n        // 这里基于 subTree 再次调用 patch\n        // 基于 render 返回的 vnode ，再次进行渲染\n        // 这里我把这个行为隐喻成开箱\n        // 一个组件就是一个箱子\n        // 里面有可能是 element （也就是可以直接渲染的）\n        // 也有可能还是 component\n        // 这里就是递归的开箱\n        // 而 subTree 就是当前的这个箱子（组件）装的东西\n        // 箱子（组件）只是个概念，它实际是不需要渲染的\n        // 要渲染的是箱子里面的 subTree\n        patch(null, subTree, container, null, instance);\n        // 把 root element 赋值给 组件的vnode.el ，为后续调用 $el 的时候获取值\n        initialVNode.el = subTree.el;\n\n        console.log(`${instance.type.name}:触发 mounted hook`);\n        instance.isMounted = true;\n      } else {\n        // 响应式的值变更后会从这里执行逻辑\n        // 主要就是拿到新的 vnode ，然后和之前的 vnode 进行对比\n        console.log(`${instance.type.name}:调用更新逻辑`);\n        // 拿到最新的 subTree\n        const { next, vnode } = instance;\n\n        // 如果有 next 的话， 说明需要更新组件的数据（props，slots 等）\n        // 先更新组件的数据，然后更新完成后，在继续对比当前组件的子元素\n        if (next) {\n          // 问题是 next 和 vnode 的区别是什么\n          next.el = vnode.el;\n          updateComponentPreRender(instance, next);\n        }\n\n        const proxyToUse = instance.proxy;\n        const nextTree = normalizeVNode(\n          instance.render.call(proxyToUse, proxyToUse)\n        );\n        // 替换之前的 subTree\n        const prevTree = instance.subTree;\n        instance.subTree = nextTree;\n\n        // 触发 beforeUpdated hook\n        console.log(`${instance.type.name}:触发 beforeUpdated hook`);\n        console.log(`${instance.type.name}:触发 onVnodeBeforeUpdate hook`);\n\n        // 用旧的 vnode 和新的 vnode 交给 patch 来处理\n        patch(prevTree, nextTree, prevTree.el, null, instance);\n\n        // 触发 updated hook\n        console.log(`${instance.type.name}:触发 updated hook`);\n        console.log(`${instance.type.name}:触发 onVnodeUpdated hook`);\n      }\n    }\n\n    // 在 vue3.2 版本里面是使用的 new ReactiveEffect\n    // 至于为什么不直接用 effect ，是因为需要一个 scope  参数来收集所有的 effect\n    // 而 effect 这个函数是对外的 api ，是不可以轻易改变参数的，所以会使用  new ReactiveEffect\n    // 因为 ReactiveEffect 是内部对象，加一个参数是无所谓的\n    // 后面如果要实现 scope 的逻辑的时候 需要改过来\n    // 现在就先算了\n    instance.update = effect(componentUpdateFn, {\n      scheduler: () => {\n        // 把 effect 推到微任务的时候在执行\n        // queueJob(effect);\n        queueJob(instance.update);\n      },\n    });\n  }\n\n  function updateComponentPreRender(instance, nextVNode) {\n    // 更新 nextVNode 的组件实例\n    // 现在 instance.vnode 是组件实例更新前的\n    // 所以之前的 props 就是基于 instance.vnode.props 来获取\n    // 接着需要更新 vnode ，方便下一次更新的时候获取到正确的值\n    nextVNode.component = instance;\n    // TODO 后面更新 props 的时候需要对比\n    // const prevProps = instance.vnode.props;\n    instance.vnode = nextVNode;\n    instance.next = null;\n\n    const { props } = nextVNode;\n    console.log(\"更新组件的 props\", props);\n    instance.props = props;\n    console.log(\"更新组件的 slots\");\n    // TODO 更新组件的 slots\n    // 需要重置 vnode\n  }\n\n  return {\n    render,\n    createApp: createAppAPI(render),\n  };\n}\n\nfunction getSequence(arr: number[]): number[] {\n  const p = arr.slice();\n  const result = [0];\n  let i, j, u, v, c;\n  const len = arr.length;\n  for (i = 0; i < len; i++) {\n    const arrI = arr[i];\n    if (arrI !== 0) {\n      j = result[result.length - 1];\n      if (arr[j] < arrI) {\n        p[i] = j;\n        result.push(i);\n        continue;\n      }\n      u = 0;\n      v = result.length - 1;\n      while (u < v) {\n        c = (u + v) >> 1;\n        if (arr[result[c]] < arrI) {\n          u = c + 1;\n        } else {\n          v = c;\n        }\n      }\n      if (arrI < arr[result[u]]) {\n        if (u > 0) {\n          p[i] = result[u - 1];\n        }\n        result[u] = i;\n      }\n    }\n  }\n  u = result.length;\n  v = result[u - 1];\n  while (u-- > 0) {\n    result[u] = v;\n    v = p[v];\n  }\n  return result;\n}\n"
  },
  {
    "path": "packages/runtime-core/src/scheduler.ts",
    "content": "const queue: any[] = [];\nconst activePreFlushCbs: any = [];\n\nconst p = Promise.resolve();\nlet isFlushPending = false;\n\nexport function nextTick(fn?) {\n  return fn ? p.then(fn) : p;\n}\n\nexport function queueJob(job) {\n  if (!queue.includes(job)) {\n    queue.push(job);\n    // 执行所有的 job\n    queueFlush();\n  }\n}\n\nfunction queueFlush() {\n  // 如果同时触发了两个组件的更新的话\n  // 这里就会触发两次 then （微任务逻辑）\n  // 但是着是没有必要的\n  // 我们只需要触发一次即可处理完所有的 job 调用\n  // 所以需要判断一下 如果已经触发过 nextTick 了\n  // 那么后面就不需要再次触发一次 nextTick 逻辑了\n  if (isFlushPending) return;\n  isFlushPending = true;\n  nextTick(flushJobs);\n}\n\nexport function queuePreFlushCb(cb) {\n  queueCb(cb, activePreFlushCbs);\n}\n\nfunction queueCb(cb, activeQueue) {\n  // 直接添加到对应的列表内就ok\n  // todo 这里没有考虑 activeQueue 是否已经存在 cb 的情况\n  // 然后在执行 flushJobs 的时候就可以调用 activeQueue 了\n  activeQueue.push(cb);\n\n  // 然后执行队列里面所有的 job\n  queueFlush()\n}\n\nfunction flushJobs() {\n  isFlushPending = false;\n\n  // 先执行 pre 类型的 job\n  // 所以这里执行的job 是在渲染前的\n  // 也就意味着执行这里的 job 的时候 页面还没有渲染\n  flushPreFlushCbs();\n\n  // 这里是执行 queueJob 的\n  // 比如 render 渲染就是属于这个类型的 job\n  let job;\n  while ((job = queue.shift())) {\n    if (job) {\n      job();\n    }\n  }\n}\n\nfunction flushPreFlushCbs() {\n  // 执行所有的 pre 类型的 job\n  for (let i = 0; i < activePreFlushCbs.length; i++) {\n    activePreFlushCbs[i]();\n  }\n}\n"
  },
  {
    "path": "packages/runtime-core/src/vnode.ts",
    "content": "import { ShapeFlags } from \"@mini-vue/shared\";\n\nexport { createVNode as createElementVNode }\n\nexport const createVNode = function (\n  type: any,\n  props?: any,\n  children?: string | Array<any>\n) {\n  // 注意 type 有可能是 string 也有可能是对象\n  // 如果是对象的话，那么就是用户设置的 options\n  // type 为 string 的时候\n  // createVNode(\"div\")\n  // type 为组件对象的时候\n  // createVNode(App)\n  const vnode = {\n    el: null,\n    component: null,\n    key: props?.key,\n    type,\n    props: props || {},\n    children,\n    shapeFlag: getShapeFlag(type),\n  };\n\n  // 基于 children 再次设置 shapeFlag\n  if (Array.isArray(children)) {\n    vnode.shapeFlag |= ShapeFlags.ARRAY_CHILDREN;\n  } else if (typeof children === \"string\") {\n    vnode.shapeFlag |= ShapeFlags.TEXT_CHILDREN;\n  }\n\n  normalizeChildren(vnode, children);\n\n  return vnode;\n};\n\nexport function normalizeChildren(vnode, children) {\n  if (typeof children === \"object\") {\n    // 暂时主要是为了标识出 slots_children 这个类型来\n    // 暂时我们只有 element 类型和 component 类型的组件\n    // 所以我们这里除了 element ，那么只要是 component 的话，那么children 肯定就是 slots 了\n    if (vnode.shapeFlag & ShapeFlags.ELEMENT) {\n      // 如果是 element 类型的话，那么 children 肯定不是 slots\n    } else {\n      // 这里就必然是 component 了,\n      vnode.shapeFlag |= ShapeFlags.SLOTS_CHILDREN;\n    }\n  }\n}\n// 用 symbol 作为唯一标识\nexport const Text = Symbol(\"Text\");\nexport const Fragment = Symbol(\"Fragment\");\n\n/**\n * @private\n */\nexport function createTextVNode(text: string = \" \") {\n  return createVNode(Text, {}, text);\n}\n\n// 标准化 vnode 的格式\n// 其目的是为了让 child 支持多种格式\nexport function normalizeVNode(child) {\n  // 暂时只支持处理 child 为 string 和 number 的情况\n  if (typeof child === \"string\" || typeof child === \"number\") {\n    return createVNode(Text, null, String(child));\n  } else {\n    return child;\n  }\n}\n\n// 基于 type 来判断是什么类型的组件\nfunction getShapeFlag(type: any) {\n  return typeof type === \"string\"\n    ? ShapeFlags.ELEMENT\n    : ShapeFlags.STATEFUL_COMPONENT;\n}\n"
  },
  {
    "path": "packages/runtime-dom/package.json",
    "content": "{\n  \"name\": \"@mini-vue/runtime-dom\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/runtime-dom\",\n  \"module\": \"dist/shared.esm-bundler.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@mini-vue/runtime-core\": \"workspace:^1.0.0\",\n    \"@mini-vue/shared\": \"workspace:^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/runtime-dom/src/index.ts",
    "content": "// 源码里面这些接口是由 runtime-dom 来实现\n// 这里先简单实现\n\nimport { isOn } from \"@mini-vue/shared\";\nimport { createRenderer } from \"@mini-vue/runtime-core\";\n\n// 后面也修改成和源码一样的实现\nfunction createElement(type) {\n  console.log(\"CreateElement\", type);\n  const element = document.createElement(type);\n  return element;\n}\n\nfunction createText(text) {\n  return document.createTextNode(text);\n}\n\nfunction setText(node, text) {\n  node.nodeValue = text;\n}\n\nfunction setElementText(el, text) {\n  console.log(\"SetElementText\", el, text);\n  el.textContent = text;\n}\n\nfunction patchProp(el, key, preValue, nextValue) {\n  // preValue 之前的值\n  // 为了之后 update 做准备的值\n  // nextValue 当前的值\n  console.log(`PatchProp 设置属性:${key} 值:${nextValue}`);\n  console.log(`key: ${key} 之前的值是:${preValue}`);\n\n  if (isOn(key)) {\n    // 添加事件处理函数的时候需要注意一下\n    // 1. 添加的和删除的必须是一个函数，不然的话 删除不掉\n    //    那么就需要把之前 add 的函数给存起来，后面删除的时候需要用到\n    // 2. nextValue 有可能是匿名函数，当对比发现不一样的时候也可以通过缓存的机制来避免注册多次\n    // 存储所有的事件函数\n    const invokers = el._vei || (el._vei = {});\n    const existingInvoker = invokers[key];\n    if (nextValue && existingInvoker) {\n      // patch\n      // 直接修改函数的值即可\n      existingInvoker.value = nextValue;\n    } else {\n      const eventName = key.slice(2).toLowerCase();\n      if (nextValue) {\n        const invoker = (invokers[key] = nextValue);\n        el.addEventListener(eventName, invoker);\n      } else {\n        el.removeEventListener(eventName, existingInvoker);\n        invokers[key] = undefined;\n      }\n    }\n  } else {\n    if (nextValue === null || nextValue === \"\") {\n      el.removeAttribute(key);\n    } else {\n      el.setAttribute(key, nextValue);\n    }\n  }\n}\n\nfunction insert(child, parent, anchor = null) {\n  console.log(\"Insert\");\n  parent.insertBefore(child, anchor);\n}\n\nfunction remove(child) {\n  const parent = child.parentNode;\n  if (parent) {\n    parent.removeChild(child);\n  }\n}\n\nlet renderer;\n\nfunction ensureRenderer() {\n  // 如果 renderer 有值的话，那么以后都不会初始化了\n  return (\n    renderer ||\n    (renderer = createRenderer({\n      createElement,\n      createText,\n      setText,\n      setElementText,\n      patchProp,\n      insert,\n      remove,\n    }))\n  );\n}\n\nexport const createApp = (...args) => {\n  return ensureRenderer().createApp(...args);\n};\n\nexport * from \"@mini-vue/runtime-core\"\n"
  },
  {
    "path": "packages/runtime-test/src/index.ts",
    "content": "// todo\n// 实现 render 的渲染接口\n// 实现序列化\nimport { createRenderer } from \"@mini-vue/runtime-core\";\nimport { extend } from \"@mini-vue/shared\";\nimport { nodeOps } from \"./nodeOps\";\nimport { patchProp } from \"./patchProp\";\n\nexport const { render } = createRenderer(extend({ patchProp }, nodeOps));\n\nexport * from \"./nodeOps\";\nexport * from \"./serialize\"\nexport * from '@mini-vue/runtime-core'"
  },
  {
    "path": "packages/runtime-test/src/nodeOps.ts",
    "content": "export const enum NodeTypes {\n  ELEMENT = \"element\",\n  TEXT = \"TEXT\",\n}\n\nlet nodeId = 0;\n// 这个函数会在 runtime-core 初始化 element 的时候调用\nfunction createElement(tag: string) {\n  // 如果是基于 dom 的话 那么这里会返回 dom 元素\n  // 这里是为了测试 所以只需要反正一个对象就可以了\n  // 后面的话 通过这个对象来做测试\n  const node = {\n    tag,\n    id: nodeId++,\n    type: NodeTypes.ELEMENT,\n    props: {},\n    children: [],\n    parentNode: null,\n  };\n\n  return node;\n}\n\nfunction insert(child, parent) {\n  parent.children.push(child);\n  child.parentNode = parent;\n}\n\nfunction parentNode(node) {\n  return node.parentNode;\n}\n\nfunction setElementText(el, text) {\n  el.children = [\n    {\n      id: nodeId++,\n      type: NodeTypes.TEXT,\n      text,\n      parentNode: el,\n    },\n  ];\n}\n\nexport const nodeOps = { createElement, insert, parentNode, setElementText };\n"
  },
  {
    "path": "packages/runtime-test/src/patchProp.ts",
    "content": "export function patchProp(el, key, prevValue, nextValue) {\n  el.props[key] = nextValue;\n}\n"
  },
  {
    "path": "packages/runtime-test/src/serialize.ts",
    "content": "// 把 node 给序列化\n// 测试的时候好对比\n\nimport { NodeTypes } from \"./nodeOps\";\n\n// 序列化： 把一个对象给处理成 string （进行流化）\nexport function serialize(node) {\n  if (node.type === NodeTypes.ELEMENT) {\n    return serializeElement(node);\n  } else {\n    return serializeText(node);\n  }\n}\n\nfunction serializeText(node) {\n  return node.text;\n}\n\nexport function serializeInner(node) {\n  // 把所有节点变成一个string\n  return node.children.map((c) => serialize(c)).join(``);\n}\n\nfunction serializeElement(node) {\n  // 把 props 处理成字符串\n  // 规则：\n  // 如果 value 是 null 的话 那么直接返回 ``\n  // 如果 value 是 `` 的话，那么返回 key\n  // 不然的话返回 key = value（这里的值需要字符串化）\n  const props = Object.keys(node.props)\n    .map((key) => {\n      const value = node.props[key];\n      return value == null\n        ? ``\n        : value === ``\n        ? key\n        : `${key}=${JSON.stringify(value)}`;\n    })\n    .filter(Boolean)\n    .join(\" \");\n\n  console.log(\"node---------\", node.children);\n  return `<${node.tag}${props ? ` ${props}` : ``}>${serializeInner(node)}</${\n    node.tag\n  }>`;\n}\n"
  },
  {
    "path": "packages/shared/package.json",
    "content": "{\n  \"name\": \"@mini-vue/shared\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/shared\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "packages/shared/src/index.ts",
    "content": "export * from \"../src/shapeFlags\";\nexport * from \"../src/toDisplayString\";\n\nexport const isObject = (val) => {\n  return val !== null && typeof val === \"object\";\n};\n\nexport const isString = (val) => typeof val === \"string\";\n\nconst camelizeRE = /-(\\w)/g;\n/**\n * @private\n * 把烤肉串命名方式转换成驼峰命名方式\n */\nexport const camelize = (str: string): string => {\n  return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : \"\"));\n};\n\nexport const extend = Object.assign;\n\n// 必须是 on+一个大写字母的格式开头\nexport const isOn = (key) => /^on[A-Z]/.test(key);\n\nexport function hasChanged(value, oldValue) {\n  return !Object.is(value, oldValue);\n}\n\nexport function hasOwn(val, key) {\n  return Object.prototype.hasOwnProperty.call(val, key);\n}\n\n/**\n * @private\n * 首字母大写\n */\nexport const capitalize = (str: string) =>\n  str.charAt(0).toUpperCase() + str.slice(1);\n\n/**\n * @private\n * 添加 on 前缀，并且首字母大写\n */\nexport const toHandlerKey = (str: string) =>\n  str ? `on${capitalize(str)}` : ``;\n\n// 用来匹配 kebab-case 的情况\n// 比如 onTest-event 可以匹配到 T\n// 然后取到 T 在前面加一个 - 就可以\n// \\BT 就可以匹配到 T 前面是字母的位置\nconst hyphenateRE = /\\B([A-Z])/g;\n/**\n * @private\n */\nexport const hyphenate = (str: string) =>\n  str.replace(hyphenateRE, \"-$1\").toLowerCase();\n"
  },
  {
    "path": "packages/shared/src/shapeFlags.ts",
    "content": "// 组件的类型\nexport const enum ShapeFlags {\n    // 最后要渲染的 element 类型\n    ELEMENT = 1,\n    // 组件类型\n    STATEFUL_COMPONENT = 1 << 2,\n    // vnode 的 children 为 string 类型\n    TEXT_CHILDREN = 1 << 3,\n    // vnode 的 children 为数组类型\n    ARRAY_CHILDREN = 1 << 4,\n    // vnode 的 children 为 slots 类型\n    SLOTS_CHILDREN = 1 << 5\n  }\n  "
  },
  {
    "path": "packages/shared/src/toDisplayString.ts",
    "content": "export const toDisplayString = (val) => {\n  return String(val);\n};\n"
  },
  {
    "path": "packages/vue/cypress/e2e/apiInject.cy.js",
    "content": "describe(\"apiInject\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/apiInject/\");\n    cy.contains(\"apiInject\")\n    cy.contains(\"fooOverride-bar-baz\")\n  });\n});"
  },
  {
    "path": "packages/vue/cypress/e2e/componentEmit.cy.js",
    "content": "describe(\"componentEmit\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/componentEmit/\");\n    cy.contains(\"你好\")\n    cy.contains(\"child\")\n  });\n});\n"
  },
  {
    "path": "packages/vue/cypress/e2e/componentSlots.cy.js",
    "content": "describe(\"componentSlots\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/componentSlots/\");\n    cy.contains(\"你好\");\n    cy.get(\"[data-test='child']\").within(() => {\n      cy.contains(\"child\");\n      cy.contains(\"我是通过 slot 渲染出来的第一个元素\");\n      cy.contains(\"我是通过 slot 渲染出来的第一个元素\");\n      cy.contains(\"我可以接收到\");\n      cy.contains(\"age: 16\");\n    });\n  });\n});\n\n\n\n"
  },
  {
    "path": "packages/vue/cypress/e2e/componentUpdate.cy.js",
    "content": "describe(\"componentUpdate\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/componentUpdate/\");\n    cy.contains(\"child123\")\n    cy.get(\"button\").click()\n    cy.contains(\"child456\")\n  });\n});"
  },
  {
    "path": "packages/vue/cypress/e2e/customRenderer.cy.js",
    "content": "describe(\"customRenderer\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/customRenderer/\");\n    cy.get(\"canvas\").should(\"exist\")\n  });\n});"
  },
  {
    "path": "packages/vue/cypress/e2e/getCurrentInstance.cy.js",
    "content": "describe(\"currentInstance\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/getCurrentInstance/\");\n    cy.contains(\"getCurrentInstance\")\n  });\n});"
  },
  {
    "path": "packages/vue/cypress/e2e/helloworld.cy.js",
    "content": "describe(\"helloworld\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/helloWorld/\");\n    cy.contains(\"主页\");\n    cy.contains(\"hello world:\");\n    cy.contains(\"count: 0\");\n  });\n});\n"
  },
  {
    "path": "packages/vue/cypress/e2e/nextTicker.cy.js",
    "content": "describe(\"nextTicker\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/nextTicker/\");\n    cy.contains(\"主页\")\n    cy.contains(\"child1\")\n    cy.contains(\"count:1\")\n    cy.contains(\"child2\")\n    cy.contains(\"count:1\")\n  });\n});"
  },
  {
    "path": "packages/vue/cypress/e2e/patchChildren.cy.js",
    "content": "describe(\"patchChildren\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/patchChildren/\");\n\n    cy.get(\"[data-cy='contain']\").should(\"text\", \"ABCEFG\");\n\n    cy.get(\"button\").click();\n    \n    cy.get(\"[data-cy='contain']\").should(\"text\", \"ABECDFG\");\n\n    cy.get(\"button\").click();\n\n    cy.get(\"[data-cy='contain']\").should(\"text\", \"ABCEFG\");\n  });\n});\n"
  },
  {
    "path": "packages/vue/cypress/fixtures/example.json",
    "content": "{\n  \"name\": \"Using fixtures to represent data\",\n  \"email\": \"hello@cypress.io\",\n  \"body\": \"Fixtures are a great way to mock data for responses to routes\"\n}\n"
  },
  {
    "path": "packages/vue/cypress/support/commands.js",
    "content": "// ***********************************************\n// This example commands.js shows you how to\n// create various custom commands and overwrite\n// existing commands.\n//\n// For more comprehensive examples of custom\n// commands please read more here:\n// https://on.cypress.io/custom-commands\n// ***********************************************\n//\n//\n// -- This is a parent command --\n// Cypress.Commands.add('login', (email, password) => { ... })\n//\n//\n// -- This is a child command --\n// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })\n//\n//\n// -- This is a dual command --\n// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })\n//\n//\n// -- This will overwrite an existing command --\n// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })"
  },
  {
    "path": "packages/vue/cypress/support/e2e.js",
    "content": "// ***********************************************************\n// This example support/e2e.js is processed and\n// loaded automatically before your test files.\n//\n// This is a great place to put global configuration and\n// behavior that modifies Cypress.\n//\n// You can change the location of this file or turn off\n// automatically serving support files with the\n// 'supportFile' configuration option.\n//\n// You can read more here:\n// https://on.cypress.io/configuration\n// ***********************************************************\n\n// Import commands.js using ES2015 syntax:\nimport './commands'\n\n// Alternatively you can use CommonJS syntax:\n// require('./commands')"
  },
  {
    "path": "packages/vue/cypress.config.js",
    "content": "const { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  e2e: {\n    setupNodeEvents(on, config) {\n      // implement node event listeners here\n    },\n    video:false\n  }\n})"
  },
  {
    "path": "packages/vue/dist/mini-vue.cjs.js",
    "content": "'use strict';\n\nObject.defineProperty(exports, '__esModule', { value: true });\n\nvar ShapeFlags;\n(function (ShapeFlags) {\n    ShapeFlags[ShapeFlags[\"ELEMENT\"] = 1] = \"ELEMENT\";\n    ShapeFlags[ShapeFlags[\"STATEFUL_COMPONENT\"] = 4] = \"STATEFUL_COMPONENT\";\n    ShapeFlags[ShapeFlags[\"TEXT_CHILDREN\"] = 8] = \"TEXT_CHILDREN\";\n    ShapeFlags[ShapeFlags[\"ARRAY_CHILDREN\"] = 16] = \"ARRAY_CHILDREN\";\n    ShapeFlags[ShapeFlags[\"SLOTS_CHILDREN\"] = 32] = \"SLOTS_CHILDREN\";\n})(ShapeFlags || (ShapeFlags = {}));\n\nconst toDisplayString = (val) => {\n    return String(val);\n};\n\nconst isObject = (val) => {\n    return val !== null && typeof val === \"object\";\n};\nconst isString = (val) => typeof val === \"string\";\nconst camelizeRE = /-(\\w)/g;\nconst camelize = (str) => {\n    return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : \"\"));\n};\nconst extend = Object.assign;\nconst isOn = (key) => /^on[A-Z]/.test(key);\nfunction hasChanged(value, oldValue) {\n    return !Object.is(value, oldValue);\n}\nfunction hasOwn(val, key) {\n    return Object.prototype.hasOwnProperty.call(val, key);\n}\nconst capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);\nconst toHandlerKey = (str) => str ? `on${capitalize(str)}` : ``;\nconst hyphenateRE = /\\B([A-Z])/g;\nconst hyphenate = (str) => str.replace(hyphenateRE, \"-$1\").toLowerCase();\n\nconst createVNode = function (type, props, children) {\n    const vnode = {\n        el: null,\n        component: null,\n        key: props === null || props === void 0 ? void 0 : props.key,\n        type,\n        props: props || {},\n        children,\n        shapeFlag: getShapeFlag(type),\n    };\n    if (Array.isArray(children)) {\n        vnode.shapeFlag |= 16;\n    }\n    else if (typeof children === \"string\") {\n        vnode.shapeFlag |= 8;\n    }\n    normalizeChildren(vnode, children);\n    return vnode;\n};\nfunction normalizeChildren(vnode, children) {\n    if (typeof children === \"object\") {\n        if (vnode.shapeFlag & 1) ;\n        else {\n            vnode.shapeFlag |= 32;\n        }\n    }\n}\nconst Text = Symbol(\"Text\");\nconst Fragment = Symbol(\"Fragment\");\nfunction createTextVNode(text = \" \") {\n    return createVNode(Text, {}, text);\n}\nfunction normalizeVNode(child) {\n    if (typeof child === \"string\" || typeof child === \"number\") {\n        return createVNode(Text, null, String(child));\n    }\n    else {\n        return child;\n    }\n}\nfunction getShapeFlag(type) {\n    return typeof type === \"string\"\n        ? 1\n        : 4;\n}\n\nconst h = (type, props = null, children = []) => {\n    return createVNode(type, props, children);\n};\n\nfunction createAppAPI(render) {\n    return function createApp(rootComponent) {\n        const app = {\n            _component: rootComponent,\n            mount(rootContainer) {\n                console.log(\"基于根组件创建 vnode\");\n                const vnode = createVNode(rootComponent);\n                console.log(\"调用 render，基于 vnode 进行开箱\");\n                render(vnode, rootContainer);\n            },\n        };\n        return app;\n    };\n}\n\nfunction initProps(instance, rawProps) {\n    console.log(\"initProps\");\n    instance.props = rawProps;\n}\n\nfunction initSlots(instance, children) {\n    const { vnode } = instance;\n    console.log(\"初始化 slots\");\n    if (vnode.shapeFlag & 32) {\n        normalizeObjectSlots(children, (instance.slots = {}));\n    }\n}\nconst normalizeSlotValue = (value) => {\n    return Array.isArray(value) ? value : [value];\n};\nconst normalizeObjectSlots = (rawSlots, slots) => {\n    for (const key in rawSlots) {\n        const value = rawSlots[key];\n        if (typeof value === \"function\") {\n            slots[key] = (props) => normalizeSlotValue(value(props));\n        }\n    }\n};\n\nfunction emit(instance, event, ...rawArgs) {\n    const props = instance.props;\n    let handler = props[toHandlerKey(camelize(event))];\n    if (!handler) {\n        handler = props[(toHandlerKey(hyphenate(event)))];\n    }\n    if (handler) {\n        handler(...rawArgs);\n    }\n}\n\nconst publicPropertiesMap = {\n    $el: (i) => i.vnode.el,\n    $emit: (i) => i.emit,\n    $slots: (i) => i.slots,\n    $props: (i) => i.props,\n};\nconst PublicInstanceProxyHandlers = {\n    get({ _: instance }, key) {\n        const { setupState, props } = instance;\n        console.log(`触发 proxy hook , key -> : ${key}`);\n        if (key[0] !== \"$\") {\n            if (hasOwn(setupState, key)) {\n                return setupState[key];\n            }\n            else if (hasOwn(props, key)) {\n                return props[key];\n            }\n        }\n        const publicGetter = publicPropertiesMap[key];\n        if (publicGetter) {\n            return publicGetter(instance);\n        }\n    },\n    set({ _: instance }, key, value) {\n        const { setupState } = instance;\n        if (hasOwn(setupState, key)) {\n            setupState[key] = value;\n        }\n        return true;\n    },\n};\n\nfunction createDep(effects) {\n    const dep = new Set(effects);\n    return dep;\n}\n\nlet activeEffect = void 0;\nlet shouldTrack = false;\nconst targetMap = new WeakMap();\nclass ReactiveEffect {\n    constructor(fn, scheduler) {\n        this.fn = fn;\n        this.scheduler = scheduler;\n        this.active = true;\n        this.deps = [];\n        console.log(\"创建 ReactiveEffect 对象\");\n    }\n    run() {\n        console.log(\"run\");\n        if (!this.active) {\n            return this.fn();\n        }\n        shouldTrack = true;\n        activeEffect = this;\n        console.log(\"执行用户传入的 fn\");\n        const result = this.fn();\n        shouldTrack = false;\n        activeEffect = undefined;\n        return result;\n    }\n    stop() {\n        if (this.active) {\n            cleanupEffect(this);\n            if (this.onStop) {\n                this.onStop();\n            }\n            this.active = false;\n        }\n    }\n}\nfunction cleanupEffect(effect) {\n    effect.deps.forEach((dep) => {\n        dep.delete(effect);\n    });\n    effect.deps.length = 0;\n}\nfunction effect(fn, options = {}) {\n    const _effect = new ReactiveEffect(fn);\n    extend(_effect, options);\n    _effect.run();\n    const runner = _effect.run.bind(_effect);\n    runner.effect = _effect;\n    return runner;\n}\nfunction stop(runner) {\n    runner.effect.stop();\n}\nfunction track(target, type, key) {\n    if (!isTracking()) {\n        return;\n    }\n    console.log(`触发 track -> target: ${target} type:${type} key:${key}`);\n    let depsMap = targetMap.get(target);\n    if (!depsMap) {\n        depsMap = new Map();\n        targetMap.set(target, depsMap);\n    }\n    let dep = depsMap.get(key);\n    if (!dep) {\n        dep = createDep();\n        depsMap.set(key, dep);\n    }\n    trackEffects(dep);\n}\nfunction trackEffects(dep) {\n    if (!dep.has(activeEffect)) {\n        dep.add(activeEffect);\n        activeEffect.deps.push(dep);\n    }\n}\nfunction trigger(target, type, key) {\n    let deps = [];\n    const depsMap = targetMap.get(target);\n    if (!depsMap)\n        return;\n    const dep = depsMap.get(key);\n    deps.push(dep);\n    const effects = [];\n    deps.forEach((dep) => {\n        effects.push(...dep);\n    });\n    triggerEffects(createDep(effects));\n}\nfunction isTracking() {\n    return shouldTrack && activeEffect !== undefined;\n}\nfunction triggerEffects(dep) {\n    for (const effect of dep) {\n        if (effect.scheduler) {\n            effect.scheduler();\n        }\n        else {\n            effect.run();\n        }\n    }\n}\n\nconst get = createGetter();\nconst set = createSetter();\nconst readonlyGet = createGetter(true);\nconst shallowReadonlyGet = createGetter(true, true);\nfunction createGetter(isReadonly = false, shallow = false) {\n    return function get(target, key, receiver) {\n        const isExistInReactiveMap = () => key === \"__v_raw\" && receiver === reactiveMap.get(target);\n        const isExistInReadonlyMap = () => key === \"__v_raw\" && receiver === readonlyMap.get(target);\n        const isExistInShallowReadonlyMap = () => key === \"__v_raw\" && receiver === shallowReadonlyMap.get(target);\n        if (key === \"__v_isReactive\") {\n            return !isReadonly;\n        }\n        else if (key === \"__v_isReadonly\") {\n            return isReadonly;\n        }\n        else if (isExistInReactiveMap() ||\n            isExistInReadonlyMap() ||\n            isExistInShallowReadonlyMap()) {\n            return target;\n        }\n        const res = Reflect.get(target, key, receiver);\n        if (!isReadonly) {\n            track(target, \"get\", key);\n        }\n        if (shallow) {\n            return res;\n        }\n        if (isObject(res)) {\n            return isReadonly ? readonly(res) : reactive(res);\n        }\n        return res;\n    };\n}\nfunction createSetter() {\n    return function set(target, key, value, receiver) {\n        const result = Reflect.set(target, key, value, receiver);\n        trigger(target, \"set\", key);\n        return result;\n    };\n}\nconst readonlyHandlers = {\n    get: readonlyGet,\n    set(target, key) {\n        console.warn(`Set operation on key \"${String(key)}\" failed: target is readonly.`, target);\n        return true;\n    },\n};\nconst mutableHandlers = {\n    get,\n    set,\n};\nconst shallowReadonlyHandlers = {\n    get: shallowReadonlyGet,\n    set(target, key) {\n        console.warn(`Set operation on key \"${String(key)}\" failed: target is readonly.`, target);\n        return true;\n    },\n};\n\nconst reactiveMap = new WeakMap();\nconst readonlyMap = new WeakMap();\nconst shallowReadonlyMap = new WeakMap();\nvar ReactiveFlags;\n(function (ReactiveFlags) {\n    ReactiveFlags[\"IS_REACTIVE\"] = \"__v_isReactive\";\n    ReactiveFlags[\"IS_READONLY\"] = \"__v_isReadonly\";\n    ReactiveFlags[\"RAW\"] = \"__v_raw\";\n})(ReactiveFlags || (ReactiveFlags = {}));\nfunction reactive(target) {\n    return createReactiveObject(target, reactiveMap, mutableHandlers);\n}\nfunction readonly(target) {\n    return createReactiveObject(target, readonlyMap, readonlyHandlers);\n}\nfunction shallowReadonly(target) {\n    return createReactiveObject(target, shallowReadonlyMap, shallowReadonlyHandlers);\n}\nfunction isProxy(value) {\n    return isReactive(value) || isReadonly(value);\n}\nfunction isReadonly(value) {\n    return !!value[\"__v_isReadonly\"];\n}\nfunction isReactive(value) {\n    return !!value[\"__v_isReactive\"];\n}\nfunction createReactiveObject(target, proxyMap, baseHandlers) {\n    const existingProxy = proxyMap.get(target);\n    if (existingProxy) {\n        return existingProxy;\n    }\n    const proxy = new Proxy(target, baseHandlers);\n    proxyMap.set(target, proxy);\n    return proxy;\n}\n\nclass RefImpl {\n    constructor(value) {\n        this.__v_isRef = true;\n        this._rawValue = value;\n        this._value = convert(value);\n        this.dep = createDep();\n    }\n    get value() {\n        trackRefValue(this);\n        return this._value;\n    }\n    set value(newValue) {\n        if (hasChanged(newValue, this._rawValue)) {\n            this._value = convert(newValue);\n            this._rawValue = newValue;\n            triggerRefValue(this);\n        }\n    }\n}\nfunction ref(value) {\n    return createRef(value);\n}\nfunction convert(value) {\n    return isObject(value) ? reactive(value) : value;\n}\nfunction createRef(value) {\n    const refImpl = new RefImpl(value);\n    return refImpl;\n}\nfunction triggerRefValue(ref) {\n    triggerEffects(ref.dep);\n}\nfunction trackRefValue(ref) {\n    if (isTracking()) {\n        trackEffects(ref.dep);\n    }\n}\nconst shallowUnwrapHandlers = {\n    get(target, key, receiver) {\n        return unRef(Reflect.get(target, key, receiver));\n    },\n    set(target, key, value, receiver) {\n        const oldValue = target[key];\n        if (isRef(oldValue) && !isRef(value)) {\n            return (target[key].value = value);\n        }\n        else {\n            return Reflect.set(target, key, value, receiver);\n        }\n    },\n};\nfunction proxyRefs(objectWithRefs) {\n    return new Proxy(objectWithRefs, shallowUnwrapHandlers);\n}\nfunction unRef(ref) {\n    return isRef(ref) ? ref.value : ref;\n}\nfunction isRef(value) {\n    return !!value.__v_isRef;\n}\n\nclass ComputedRefImpl {\n    constructor(getter) {\n        this._dirty = true;\n        this.dep = createDep();\n        this.effect = new ReactiveEffect(getter, () => {\n            if (this._dirty)\n                return;\n            this._dirty = true;\n            triggerRefValue(this);\n        });\n    }\n    get value() {\n        trackRefValue(this);\n        if (this._dirty) {\n            this._dirty = false;\n            this._value = this.effect.run();\n        }\n        return this._value;\n    }\n}\nfunction computed(getter) {\n    return new ComputedRefImpl(getter);\n}\n\nfunction createComponentInstance(vnode, parent) {\n    const instance = {\n        type: vnode.type,\n        vnode,\n        next: null,\n        props: {},\n        parent,\n        provides: parent ? parent.provides : {},\n        proxy: null,\n        isMounted: false,\n        attrs: {},\n        slots: {},\n        ctx: {},\n        setupState: {},\n        emit: () => { },\n    };\n    instance.ctx = {\n        _: instance,\n    };\n    instance.emit = emit.bind(null, instance);\n    return instance;\n}\nfunction setupComponent(instance) {\n    const { props, children } = instance.vnode;\n    initProps(instance, props);\n    initSlots(instance, children);\n    setupStatefulComponent(instance);\n}\nfunction setupStatefulComponent(instance) {\n    console.log(\"创建 proxy\");\n    instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);\n    const Component = instance.type;\n    const { setup } = Component;\n    if (setup) {\n        setCurrentInstance(instance);\n        const setupContext = createSetupContext(instance);\n        const setupResult = setup && setup(shallowReadonly(instance.props), setupContext);\n        setCurrentInstance(null);\n        handleSetupResult(instance, setupResult);\n    }\n    else {\n        finishComponentSetup(instance);\n    }\n}\nfunction createSetupContext(instance) {\n    console.log(\"初始化 setup context\");\n    return {\n        attrs: instance.attrs,\n        slots: instance.slots,\n        emit: instance.emit,\n        expose: () => { },\n    };\n}\nfunction handleSetupResult(instance, setupResult) {\n    if (typeof setupResult === \"function\") {\n        instance.render = setupResult;\n    }\n    else if (typeof setupResult === \"object\") {\n        instance.setupState = proxyRefs(setupResult);\n    }\n    finishComponentSetup(instance);\n}\nfunction finishComponentSetup(instance) {\n    const Component = instance.type;\n    if (!instance.render) {\n        if (compile && !Component.render) {\n            if (Component.template) {\n                const template = Component.template;\n                Component.render = compile(template);\n            }\n        }\n        instance.render = Component.render;\n    }\n}\nlet currentInstance = {};\nfunction getCurrentInstance() {\n    return currentInstance;\n}\nfunction setCurrentInstance(instance) {\n    currentInstance = instance;\n}\nlet compile;\nfunction registerRuntimeCompiler(_compile) {\n    compile = _compile;\n}\n\nfunction provide(key, value) {\n    var _a;\n    const currentInstance = getCurrentInstance();\n    if (currentInstance) {\n        let { provides } = currentInstance;\n        const parentProvides = (_a = currentInstance.parent) === null || _a === void 0 ? void 0 : _a.provides;\n        if (parentProvides === provides) {\n            provides = currentInstance.provides = Object.create(parentProvides);\n        }\n        provides[key] = value;\n    }\n}\nfunction inject(key, defaultValue) {\n    var _a;\n    const currentInstance = getCurrentInstance();\n    if (currentInstance) {\n        const provides = (_a = currentInstance.parent) === null || _a === void 0 ? void 0 : _a.provides;\n        if (key in provides) {\n            return provides[key];\n        }\n        else if (defaultValue) {\n            if (typeof defaultValue === \"function\") {\n                return defaultValue();\n            }\n            return defaultValue;\n        }\n    }\n}\n\nfunction renderSlot(slots, name, props = {}) {\n    const slot = slots[name];\n    console.log(`渲染插槽 slot -> ${name}`);\n    if (slot) {\n        const slotContent = slot(props);\n        return createVNode(Fragment, {}, slotContent);\n    }\n}\n\nconst queue = [];\nconst activePreFlushCbs = [];\nconst p = Promise.resolve();\nlet isFlushPending = false;\nfunction nextTick(fn) {\n    return fn ? p.then(fn) : p;\n}\nfunction queueJob(job) {\n    if (!queue.includes(job)) {\n        queue.push(job);\n        queueFlush();\n    }\n}\nfunction queueFlush() {\n    if (isFlushPending)\n        return;\n    isFlushPending = true;\n    nextTick(flushJobs);\n}\nfunction queuePreFlushCb(cb) {\n    queueCb(cb, activePreFlushCbs);\n}\nfunction queueCb(cb, activeQueue) {\n    activeQueue.push(cb);\n    queueFlush();\n}\nfunction flushJobs() {\n    isFlushPending = false;\n    flushPreFlushCbs();\n    let job;\n    while ((job = queue.shift())) {\n        if (job) {\n            job();\n        }\n    }\n}\nfunction flushPreFlushCbs() {\n    for (let i = 0; i < activePreFlushCbs.length; i++) {\n        activePreFlushCbs[i]();\n    }\n}\n\nfunction shouldUpdateComponent(prevVNode, nextVNode) {\n    const { props: prevProps } = prevVNode;\n    const { props: nextProps } = nextVNode;\n    if (prevProps === nextProps) {\n        return false;\n    }\n    if (!prevProps) {\n        return !!nextProps;\n    }\n    if (!nextProps) {\n        return true;\n    }\n    return hasPropsChanged(prevProps, nextProps);\n}\nfunction hasPropsChanged(prevProps, nextProps) {\n    const nextKeys = Object.keys(nextProps);\n    if (nextKeys.length !== Object.keys(prevProps).length) {\n        return true;\n    }\n    for (let i = 0; i < nextKeys.length; i++) {\n        const key = nextKeys[i];\n        if (nextProps[key] !== prevProps[key]) {\n            return true;\n        }\n    }\n    return false;\n}\n\nfunction createRenderer(options) {\n    const { createElement: hostCreateElement, setElementText: hostSetElementText, patchProp: hostPatchProp, insert: hostInsert, remove: hostRemove, setText: hostSetText, createText: hostCreateText, } = options;\n    const render = (vnode, container) => {\n        console.log(\"调用 patch\");\n        patch(null, vnode, container);\n    };\n    function patch(n1, n2, container = null, anchor = null, parentComponent = null) {\n        const { type, shapeFlag } = n2;\n        switch (type) {\n            case Text:\n                processText(n1, n2, container);\n                break;\n            case Fragment:\n                processFragment(n1, n2, container);\n                break;\n            default:\n                if (shapeFlag & 1) {\n                    console.log(\"处理 element\");\n                    processElement(n1, n2, container, anchor, parentComponent);\n                }\n                else if (shapeFlag & 4) {\n                    console.log(\"处理 component\");\n                    processComponent(n1, n2, container, parentComponent);\n                }\n        }\n    }\n    function processFragment(n1, n2, container) {\n        if (!n1) {\n            console.log(\"初始化 Fragment 类型的节点\");\n            mountChildren(n2.children, container);\n        }\n    }\n    function processText(n1, n2, container) {\n        console.log(\"处理 Text 节点\");\n        if (n1 === null) {\n            console.log(\"初始化 Text 类型的节点\");\n            hostInsert((n2.el = hostCreateText(n2.children)), container);\n        }\n        else {\n            const el = (n2.el = n1.el);\n            if (n2.children !== n1.children) {\n                console.log(\"更新 Text 类型的节点\");\n                hostSetText(el, n2.children);\n            }\n        }\n    }\n    function processElement(n1, n2, container, anchor, parentComponent) {\n        if (!n1) {\n            mountElement(n2, container, anchor);\n        }\n        else {\n            updateElement(n1, n2, container, anchor, parentComponent);\n        }\n    }\n    function updateElement(n1, n2, container, anchor, parentComponent) {\n        const oldProps = (n1 && n1.props) || {};\n        const newProps = n2.props || {};\n        console.log(\"应该更新 element\");\n        console.log(\"旧的 vnode\", n1);\n        console.log(\"新的 vnode\", n2);\n        const el = (n2.el = n1.el);\n        patchProps(el, oldProps, newProps);\n        patchChildren(n1, n2, el, anchor, parentComponent);\n    }\n    function patchProps(el, oldProps, newProps) {\n        for (const key in newProps) {\n            const prevProp = oldProps[key];\n            const nextProp = newProps[key];\n            if (prevProp !== nextProp) {\n                hostPatchProp(el, key, prevProp, nextProp);\n            }\n        }\n        for (const key in oldProps) {\n            const prevProp = oldProps[key];\n            const nextProp = null;\n            if (!(key in newProps)) {\n                hostPatchProp(el, key, prevProp, nextProp);\n            }\n        }\n    }\n    function patchChildren(n1, n2, container, anchor, parentComponent) {\n        const { shapeFlag: prevShapeFlag, children: c1 } = n1;\n        const { shapeFlag, children: c2 } = n2;\n        if (shapeFlag & 8) {\n            if (c2 !== c1) {\n                console.log(\"类型为 text_children, 当前需要更新\");\n                hostSetElementText(container, c2);\n            }\n        }\n        else {\n            if (prevShapeFlag & 8) {\n                hostSetElementText(container, \"\");\n                mountChildren(c2, container);\n            }\n            else {\n                patchKeyedChildren(c1, c2, container, parentComponent, anchor);\n            }\n        }\n    }\n    function patchKeyedChildren(c1, c2, container, parentAnchor, parentComponent) {\n        let i = 0;\n        const l2 = c2.length;\n        let e1 = c1.length - 1;\n        let e2 = l2 - 1;\n        const isSameVNodeType = (n1, n2) => {\n            return n1.type === n2.type && n1.key === n2.key;\n        };\n        while (i <= e1 && i <= e2) {\n            const prevChild = c1[i];\n            const nextChild = c2[i];\n            if (!isSameVNodeType(prevChild, nextChild)) {\n                console.log(\"两个 child 不相等(从左往右比对)\");\n                console.log(`prevChild:${prevChild}`);\n                console.log(`nextChild:${nextChild}`);\n                break;\n            }\n            console.log(\"两个 child 相等，接下来对比这两个 child 节点(从左往右比对)\");\n            patch(prevChild, nextChild, container, parentAnchor, parentComponent);\n            i++;\n        }\n        while (i <= e1 && i <= e2) {\n            const prevChild = c1[e1];\n            const nextChild = c2[e2];\n            if (!isSameVNodeType(prevChild, nextChild)) {\n                console.log(\"两个 child 不相等(从右往左比对)\");\n                console.log(`prevChild:${prevChild}`);\n                console.log(`nextChild:${nextChild}`);\n                break;\n            }\n            console.log(\"两个 child 相等，接下来对比这两个 child 节点(从右往左比对)\");\n            patch(prevChild, nextChild, container, parentAnchor, parentComponent);\n            e1--;\n            e2--;\n        }\n        if (i > e1 && i <= e2) {\n            const nextPos = e2 + 1;\n            const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;\n            while (i <= e2) {\n                console.log(`需要新创建一个 vnode: ${c2[i].key}`);\n                patch(null, c2[i], container, anchor, parentComponent);\n                i++;\n            }\n        }\n        else if (i > e2 && i <= e1) {\n            while (i <= e1) {\n                console.log(`需要删除当前的 vnode: ${c1[i].key}`);\n                hostRemove(c1[i].el);\n                i++;\n            }\n        }\n        else {\n            let s1 = i;\n            let s2 = i;\n            const keyToNewIndexMap = new Map();\n            let moved = false;\n            let maxNewIndexSoFar = 0;\n            for (let i = s2; i <= e2; i++) {\n                const nextChild = c2[i];\n                keyToNewIndexMap.set(nextChild.key, i);\n            }\n            const toBePatched = e2 - s2 + 1;\n            let patched = 0;\n            const newIndexToOldIndexMap = new Array(toBePatched);\n            for (let i = 0; i < toBePatched; i++)\n                newIndexToOldIndexMap[i] = 0;\n            for (i = s1; i <= e1; i++) {\n                const prevChild = c1[i];\n                if (patched >= toBePatched) {\n                    hostRemove(prevChild.el);\n                    continue;\n                }\n                let newIndex;\n                if (prevChild.key != null) {\n                    newIndex = keyToNewIndexMap.get(prevChild.key);\n                }\n                else {\n                    for (let j = s2; j <= e2; j++) {\n                        if (isSameVNodeType(prevChild, c2[j])) {\n                            newIndex = j;\n                            break;\n                        }\n                    }\n                }\n                if (newIndex === undefined) {\n                    hostRemove(prevChild.el);\n                }\n                else {\n                    console.log(\"新老节点都存在\");\n                    newIndexToOldIndexMap[newIndex - s2] = i + 1;\n                    if (newIndex >= maxNewIndexSoFar) {\n                        maxNewIndexSoFar = newIndex;\n                    }\n                    else {\n                        moved = true;\n                    }\n                    patch(prevChild, c2[newIndex], container, null, parentComponent);\n                    patched++;\n                }\n            }\n            const increasingNewIndexSequence = moved\n                ? getSequence(newIndexToOldIndexMap)\n                : [];\n            let j = increasingNewIndexSequence.length - 1;\n            for (let i = toBePatched - 1; i >= 0; i--) {\n                const nextIndex = s2 + i;\n                const nextChild = c2[nextIndex];\n                const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;\n                if (newIndexToOldIndexMap[i] === 0) {\n                    patch(null, nextChild, container, anchor, parentComponent);\n                }\n                else if (moved) {\n                    if (j < 0 || increasingNewIndexSequence[j] !== i) {\n                        hostInsert(nextChild.el, container, anchor);\n                    }\n                    else {\n                        j--;\n                    }\n                }\n            }\n        }\n    }\n    function mountElement(vnode, container, anchor) {\n        const { shapeFlag, props } = vnode;\n        const el = (vnode.el = hostCreateElement(vnode.type));\n        if (shapeFlag & 8) {\n            console.log(`处理文本:${vnode.children}`);\n            hostSetElementText(el, vnode.children);\n        }\n        else if (shapeFlag & 16) {\n            mountChildren(vnode.children, el);\n        }\n        if (props) {\n            for (const key in props) {\n                const nextVal = props[key];\n                hostPatchProp(el, key, null, nextVal);\n            }\n        }\n        console.log(\"vnodeHook  -> onVnodeBeforeMount\");\n        console.log(\"DirectiveHook  -> beforeMount\");\n        console.log(\"transition  -> beforeEnter\");\n        hostInsert(el, container, anchor);\n        console.log(\"vnodeHook  -> onVnodeMounted\");\n        console.log(\"DirectiveHook  -> mounted\");\n        console.log(\"transition  -> enter\");\n    }\n    function mountChildren(children, container) {\n        children.forEach((VNodeChild) => {\n            console.log(\"mountChildren:\", VNodeChild);\n            patch(null, VNodeChild, container);\n        });\n    }\n    function processComponent(n1, n2, container, parentComponent) {\n        if (!n1) {\n            mountComponent(n2, container, parentComponent);\n        }\n        else {\n            updateComponent(n1, n2);\n        }\n    }\n    function updateComponent(n1, n2, container) {\n        console.log(\"更新组件\", n1, n2);\n        const instance = (n2.component = n1.component);\n        if (shouldUpdateComponent(n1, n2)) {\n            console.log(`组件需要更新: ${instance}`);\n            instance.next = n2;\n            instance.update();\n        }\n        else {\n            console.log(`组件不需要更新: ${instance}`);\n            n2.component = n1.component;\n            n2.el = n1.el;\n            instance.vnode = n2;\n        }\n    }\n    function mountComponent(initialVNode, container, parentComponent) {\n        const instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent));\n        console.log(`创建组件实例:${instance.type.name}`);\n        setupComponent(instance);\n        setupRenderEffect(instance, initialVNode, container);\n    }\n    function setupRenderEffect(instance, initialVNode, container) {\n        function componentUpdateFn() {\n            if (!instance.isMounted) {\n                console.log(`${instance.type.name}:调用 render,获取 subTree`);\n                const proxyToUse = instance.proxy;\n                const subTree = (instance.subTree = normalizeVNode(instance.render.call(proxyToUse, proxyToUse)));\n                console.log(\"subTree\", subTree);\n                console.log(`${instance.type.name}:触发 beforeMount hook`);\n                console.log(`${instance.type.name}:触发 onVnodeBeforeMount hook`);\n                patch(null, subTree, container, null, instance);\n                initialVNode.el = subTree.el;\n                console.log(`${instance.type.name}:触发 mounted hook`);\n                instance.isMounted = true;\n            }\n            else {\n                console.log(`${instance.type.name}:调用更新逻辑`);\n                const { next, vnode } = instance;\n                if (next) {\n                    next.el = vnode.el;\n                    updateComponentPreRender(instance, next);\n                }\n                const proxyToUse = instance.proxy;\n                const nextTree = normalizeVNode(instance.render.call(proxyToUse, proxyToUse));\n                const prevTree = instance.subTree;\n                instance.subTree = nextTree;\n                console.log(`${instance.type.name}:触发 beforeUpdated hook`);\n                console.log(`${instance.type.name}:触发 onVnodeBeforeUpdate hook`);\n                patch(prevTree, nextTree, prevTree.el, null, instance);\n                console.log(`${instance.type.name}:触发 updated hook`);\n                console.log(`${instance.type.name}:触发 onVnodeUpdated hook`);\n            }\n        }\n        instance.update = effect(componentUpdateFn, {\n            scheduler: () => {\n                queueJob(instance.update);\n            },\n        });\n    }\n    function updateComponentPreRender(instance, nextVNode) {\n        nextVNode.component = instance;\n        instance.vnode = nextVNode;\n        instance.next = null;\n        const { props } = nextVNode;\n        console.log(\"更新组件的 props\", props);\n        instance.props = props;\n        console.log(\"更新组件的 slots\");\n    }\n    return {\n        render,\n        createApp: createAppAPI(render),\n    };\n}\nfunction getSequence(arr) {\n    const p = arr.slice();\n    const result = [0];\n    let i, j, u, v, c;\n    const len = arr.length;\n    for (i = 0; i < len; i++) {\n        const arrI = arr[i];\n        if (arrI !== 0) {\n            j = result[result.length - 1];\n            if (arr[j] < arrI) {\n                p[i] = j;\n                result.push(i);\n                continue;\n            }\n            u = 0;\n            v = result.length - 1;\n            while (u < v) {\n                c = (u + v) >> 1;\n                if (arr[result[c]] < arrI) {\n                    u = c + 1;\n                }\n                else {\n                    v = c;\n                }\n            }\n            if (arrI < arr[result[u]]) {\n                if (u > 0) {\n                    p[i] = result[u - 1];\n                }\n                result[u] = i;\n            }\n        }\n    }\n    u = result.length;\n    v = result[u - 1];\n    while (u-- > 0) {\n        result[u] = v;\n        v = p[v];\n    }\n    return result;\n}\n\nfunction watchEffect(effect) {\n    return doWatch(effect);\n}\nfunction doWatch(source) {\n    const job = () => {\n        effect.run();\n    };\n    const scheduler = () => queuePreFlushCb(job);\n    let cleanup;\n    const onCleanup = (fn) => {\n        cleanup = effect.onStop = () => {\n            fn();\n        };\n    };\n    const getter = () => {\n        if (cleanup) {\n            cleanup();\n        }\n        source(onCleanup);\n    };\n    const effect = new ReactiveEffect(getter, scheduler);\n    effect.run();\n    return () => {\n        effect.stop();\n    };\n}\n\nfunction createElement(type) {\n    console.log(\"CreateElement\", type);\n    const element = document.createElement(type);\n    return element;\n}\nfunction createText(text) {\n    return document.createTextNode(text);\n}\nfunction setText(node, text) {\n    node.nodeValue = text;\n}\nfunction setElementText(el, text) {\n    console.log(\"SetElementText\", el, text);\n    el.textContent = text;\n}\nfunction patchProp(el, key, preValue, nextValue) {\n    console.log(`PatchProp 设置属性:${key} 值:${nextValue}`);\n    console.log(`key: ${key} 之前的值是:${preValue}`);\n    if (isOn(key)) {\n        const invokers = el._vei || (el._vei = {});\n        const existingInvoker = invokers[key];\n        if (nextValue && existingInvoker) {\n            existingInvoker.value = nextValue;\n        }\n        else {\n            const eventName = key.slice(2).toLowerCase();\n            if (nextValue) {\n                const invoker = (invokers[key] = nextValue);\n                el.addEventListener(eventName, invoker);\n            }\n            else {\n                el.removeEventListener(eventName, existingInvoker);\n                invokers[key] = undefined;\n            }\n        }\n    }\n    else {\n        if (nextValue === null || nextValue === \"\") {\n            el.removeAttribute(key);\n        }\n        else {\n            el.setAttribute(key, nextValue);\n        }\n    }\n}\nfunction insert(child, parent, anchor = null) {\n    console.log(\"Insert\");\n    parent.insertBefore(child, anchor);\n}\nfunction remove(child) {\n    const parent = child.parentNode;\n    if (parent) {\n        parent.removeChild(child);\n    }\n}\nlet renderer;\nfunction ensureRenderer() {\n    return (renderer ||\n        (renderer = createRenderer({\n            createElement,\n            createText,\n            setText,\n            setElementText,\n            patchProp,\n            insert,\n            remove,\n        })));\n}\nconst createApp = (...args) => {\n    return ensureRenderer().createApp(...args);\n};\n\nvar runtimeDom = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    createApp: createApp,\n    getCurrentInstance: getCurrentInstance,\n    registerRuntimeCompiler: registerRuntimeCompiler,\n    inject: inject,\n    provide: provide,\n    renderSlot: renderSlot,\n    createTextVNode: createTextVNode,\n    createElementVNode: createVNode,\n    createRenderer: createRenderer,\n    toDisplayString: toDisplayString,\n    watchEffect: watchEffect,\n    reactive: reactive,\n    ref: ref,\n    readonly: readonly,\n    unRef: unRef,\n    proxyRefs: proxyRefs,\n    isReadonly: isReadonly,\n    isReactive: isReactive,\n    isProxy: isProxy,\n    isRef: isRef,\n    shallowReadonly: shallowReadonly,\n    effect: effect,\n    stop: stop,\n    computed: computed,\n    h: h,\n    createAppAPI: createAppAPI\n});\n\nconst TO_DISPLAY_STRING = Symbol(`toDisplayString`);\nconst CREATE_ELEMENT_VNODE = Symbol(\"createElementVNode\");\nconst helperNameMap = {\n    [TO_DISPLAY_STRING]: \"toDisplayString\",\n    [CREATE_ELEMENT_VNODE]: \"createElementVNode\"\n};\n\nfunction generate(ast, options = {}) {\n    const context = createCodegenContext(ast, options);\n    const { push, mode } = context;\n    if (mode === \"module\") {\n        genModulePreamble(ast, context);\n    }\n    else {\n        genFunctionPreamble(ast, context);\n    }\n    const functionName = \"render\";\n    const args = [\"_ctx\"];\n    const signature = args.join(\", \");\n    push(`function ${functionName}(${signature}) {`);\n    push(\"return \");\n    genNode(ast.codegenNode, context);\n    push(\"}\");\n    return {\n        code: context.code,\n    };\n}\nfunction genFunctionPreamble(ast, context) {\n    const { runtimeGlobalName, push, newline } = context;\n    const VueBinging = runtimeGlobalName;\n    const aliasHelper = (s) => `${helperNameMap[s]} : _${helperNameMap[s]}`;\n    if (ast.helpers.length > 0) {\n        push(`\n        const { ${ast.helpers.map(aliasHelper).join(\", \")}} = ${VueBinging} \n\n      `);\n    }\n    newline();\n    push(`return `);\n}\nfunction genNode(node, context) {\n    switch (node.type) {\n        case 2:\n            genInterpolation(node, context);\n            break;\n        case 3:\n            genExpression(node, context);\n            break;\n        case 4:\n            genElement(node, context);\n            break;\n        case 5:\n            genCompoundExpression(node, context);\n            break;\n        case 0:\n            genText(node, context);\n            break;\n    }\n}\nfunction genCompoundExpression(node, context) {\n    const { push } = context;\n    for (let i = 0; i < node.children.length; i++) {\n        const child = node.children[i];\n        if (isString(child)) {\n            push(child);\n        }\n        else {\n            genNode(child, context);\n        }\n    }\n}\nfunction genText(node, context) {\n    const { push } = context;\n    push(`'${node.content}'`);\n}\nfunction genElement(node, context) {\n    const { push, helper } = context;\n    const { tag, props, children } = node;\n    push(`${helper(CREATE_ELEMENT_VNODE)}(`);\n    genNodeList(genNullableArgs([tag, props, children]), context);\n    push(`)`);\n}\nfunction genNodeList(nodes, context) {\n    const { push } = context;\n    for (let i = 0; i < nodes.length; i++) {\n        const node = nodes[i];\n        if (isString(node)) {\n            push(`${node}`);\n        }\n        else {\n            genNode(node, context);\n        }\n        if (i < nodes.length - 1) {\n            push(\", \");\n        }\n    }\n}\nfunction genNullableArgs(args) {\n    let i = args.length;\n    while (i--) {\n        if (args[i] != null)\n            break;\n    }\n    return args.slice(0, i + 1).map((arg) => arg || \"null\");\n}\nfunction genExpression(node, context) {\n    context.push(node.content, node);\n}\nfunction genInterpolation(node, context) {\n    const { push, helper } = context;\n    push(`${helper(TO_DISPLAY_STRING)}(`);\n    genNode(node.content, context);\n    push(\")\");\n}\nfunction genModulePreamble(ast, context) {\n    const { push, newline, runtimeModuleName } = context;\n    if (ast.helpers.length) {\n        const code = `import {${ast.helpers\n            .map((s) => `${helperNameMap[s]} as _${helperNameMap[s]}`)\n            .join(\", \")} } from ${JSON.stringify(runtimeModuleName)}`;\n        push(code);\n    }\n    newline();\n    push(`export `);\n}\nfunction createCodegenContext(ast, { runtimeModuleName = \"vue\", runtimeGlobalName = \"Vue\", mode = \"function\" }) {\n    const context = {\n        code: \"\",\n        mode,\n        runtimeModuleName,\n        runtimeGlobalName,\n        helper(key) {\n            return `_${helperNameMap[key]}`;\n        },\n        push(code) {\n            context.code += code;\n        },\n        newline() {\n            context.code += \"\\n\";\n        },\n    };\n    return context;\n}\n\nvar TagType;\n(function (TagType) {\n    TagType[TagType[\"Start\"] = 0] = \"Start\";\n    TagType[TagType[\"End\"] = 1] = \"End\";\n})(TagType || (TagType = {}));\nfunction baseParse(content) {\n    const context = createParserContext(content);\n    return createRoot(parseChildren(context, []));\n}\nfunction createParserContext(content) {\n    console.log(\"创建 paserContext\");\n    return {\n        source: content,\n    };\n}\nfunction parseChildren(context, ancestors) {\n    console.log(\"开始解析 children\");\n    const nodes = [];\n    while (!isEnd(context, ancestors)) {\n        let node;\n        const s = context.source;\n        if (startsWith(s, \"{{\")) {\n            node = parseInterpolation(context);\n        }\n        else if (s[0] === \"<\") {\n            if (s[1] === \"/\") {\n                if (/[a-z]/i.test(s[2])) {\n                    parseTag(context, 1);\n                    continue;\n                }\n            }\n            else if (/[a-z]/i.test(s[1])) {\n                node = parseElement(context, ancestors);\n            }\n        }\n        if (!node) {\n            node = parseText(context);\n        }\n        nodes.push(node);\n    }\n    return nodes;\n}\nfunction isEnd(context, ancestors) {\n    const s = context.source;\n    if (context.source.startsWith(\"</\")) {\n        for (let i = ancestors.length - 1; i >= 0; --i) {\n            if (startsWithEndTagOpen(s, ancestors[i].tag)) {\n                return true;\n            }\n        }\n    }\n    return !context.source;\n}\nfunction parseElement(context, ancestors) {\n    const element = parseTag(context, 0);\n    ancestors.push(element);\n    const children = parseChildren(context, ancestors);\n    ancestors.pop();\n    if (startsWithEndTagOpen(context.source, element.tag)) {\n        parseTag(context, 1);\n    }\n    else {\n        throw new Error(`缺失结束标签：${element.tag}`);\n    }\n    element.children = children;\n    return element;\n}\nfunction startsWithEndTagOpen(source, tag) {\n    return (startsWith(source, \"</\") &&\n        source.slice(2, 2 + tag.length).toLowerCase() === tag.toLowerCase());\n}\nfunction parseTag(context, type) {\n    const match = /^<\\/?([a-z][^\\r\\n\\t\\f />]*)/i.exec(context.source);\n    const tag = match[1];\n    advanceBy(context, match[0].length);\n    advanceBy(context, 1);\n    if (type === 1)\n        return;\n    let tagType = 0;\n    return {\n        type: 4,\n        tag,\n        tagType,\n    };\n}\nfunction parseInterpolation(context) {\n    const openDelimiter = \"{{\";\n    const closeDelimiter = \"}}\";\n    const closeIndex = context.source.indexOf(closeDelimiter, openDelimiter.length);\n    advanceBy(context, 2);\n    const rawContentLength = closeIndex - openDelimiter.length;\n    const rawContent = context.source.slice(0, rawContentLength);\n    const preTrimContent = parseTextData(context, rawContent.length);\n    const content = preTrimContent.trim();\n    advanceBy(context, closeDelimiter.length);\n    return {\n        type: 2,\n        content: {\n            type: 3,\n            content,\n        },\n    };\n}\nfunction parseText(context) {\n    console.log(\"解析 text\", context);\n    const endTokens = [\"<\", \"{{\"];\n    let endIndex = context.source.length;\n    for (let i = 0; i < endTokens.length; i++) {\n        const index = context.source.indexOf(endTokens[i]);\n        if (index !== -1 && endIndex > index) {\n            endIndex = index;\n        }\n    }\n    const content = parseTextData(context, endIndex);\n    return {\n        type: 0,\n        content,\n    };\n}\nfunction parseTextData(context, length) {\n    console.log(\"解析 textData\");\n    const rawText = context.source.slice(0, length);\n    advanceBy(context, length);\n    return rawText;\n}\nfunction advanceBy(context, numberOfCharacters) {\n    console.log(\"推进代码\", context, numberOfCharacters);\n    context.source = context.source.slice(numberOfCharacters);\n}\nfunction createRoot(children) {\n    return {\n        type: 1,\n        children,\n        helpers: [],\n    };\n}\nfunction startsWith(source, searchString) {\n    return source.startsWith(searchString);\n}\n\nfunction transform(root, options = {}) {\n    const context = createTransformContext(root, options);\n    traverseNode(root, context);\n    createRootCodegen(root);\n    root.helpers.push(...context.helpers.keys());\n}\nfunction traverseNode(node, context) {\n    const type = node.type;\n    const nodeTransforms = context.nodeTransforms;\n    const exitFns = [];\n    for (let i = 0; i < nodeTransforms.length; i++) {\n        const transform = nodeTransforms[i];\n        const onExit = transform(node, context);\n        if (onExit) {\n            exitFns.push(onExit);\n        }\n    }\n    switch (type) {\n        case 2:\n            context.helper(TO_DISPLAY_STRING);\n            break;\n        case 1:\n        case 4:\n            traverseChildren(node, context);\n            break;\n    }\n    let i = exitFns.length;\n    while (i--) {\n        exitFns[i]();\n    }\n}\nfunction traverseChildren(parent, context) {\n    parent.children.forEach((node) => {\n        traverseNode(node, context);\n    });\n}\nfunction createTransformContext(root, options) {\n    const context = {\n        root,\n        nodeTransforms: options.nodeTransforms || [],\n        helpers: new Map(),\n        helper(name) {\n            const count = context.helpers.get(name) || 0;\n            context.helpers.set(name, count + 1);\n        },\n    };\n    return context;\n}\nfunction createRootCodegen(root, context) {\n    const { children } = root;\n    const child = children[0];\n    if (child.type === 4 && child.codegenNode) {\n        const codegenNode = child.codegenNode;\n        root.codegenNode = codegenNode;\n    }\n    else {\n        root.codegenNode = child;\n    }\n}\n\nfunction transformExpression(node) {\n    if (node.type === 2) {\n        node.content = processExpression(node.content);\n    }\n}\nfunction processExpression(node) {\n    node.content = `_ctx.${node.content}`;\n    return node;\n}\n\nvar NodeTypes;\n(function (NodeTypes) {\n    NodeTypes[NodeTypes[\"TEXT\"] = 0] = \"TEXT\";\n    NodeTypes[NodeTypes[\"ROOT\"] = 1] = \"ROOT\";\n    NodeTypes[NodeTypes[\"INTERPOLATION\"] = 2] = \"INTERPOLATION\";\n    NodeTypes[NodeTypes[\"SIMPLE_EXPRESSION\"] = 3] = \"SIMPLE_EXPRESSION\";\n    NodeTypes[NodeTypes[\"ELEMENT\"] = 4] = \"ELEMENT\";\n    NodeTypes[NodeTypes[\"COMPOUND_EXPRESSION\"] = 5] = \"COMPOUND_EXPRESSION\";\n})(NodeTypes || (NodeTypes = {}));\nvar ElementTypes;\n(function (ElementTypes) {\n    ElementTypes[ElementTypes[\"ELEMENT\"] = 0] = \"ELEMENT\";\n})(ElementTypes || (ElementTypes = {}));\nfunction createVNodeCall(context, tag, props, children) {\n    if (context) {\n        context.helper(CREATE_ELEMENT_VNODE);\n    }\n    return {\n        type: 4,\n        tag,\n        props,\n        children,\n    };\n}\n\nfunction transformElement(node, context) {\n    if (node.type === 4) {\n        return () => {\n            const vnodeTag = `'${node.tag}'`;\n            const vnodeProps = null;\n            let vnodeChildren = null;\n            if (node.children.length > 0) {\n                if (node.children.length === 1) {\n                    const child = node.children[0];\n                    vnodeChildren = child;\n                }\n            }\n            node.codegenNode = createVNodeCall(context, vnodeTag, vnodeProps, vnodeChildren);\n        };\n    }\n}\n\nfunction isText(node) {\n    return node.type === 2 || node.type === 0;\n}\n\nfunction transformText(node, context) {\n    if (node.type === 4) {\n        return () => {\n            const children = node.children;\n            let currentContainer;\n            for (let i = 0; i < children.length; i++) {\n                const child = children[i];\n                if (isText(child)) {\n                    for (let j = i + 1; j < children.length; j++) {\n                        const next = children[j];\n                        if (isText(next)) {\n                            if (!currentContainer) {\n                                currentContainer = children[i] = {\n                                    type: 5,\n                                    loc: child.loc,\n                                    children: [child],\n                                };\n                            }\n                            currentContainer.children.push(` + `, next);\n                            children.splice(j, 1);\n                            j--;\n                        }\n                        else {\n                            currentContainer = undefined;\n                            break;\n                        }\n                    }\n                }\n            }\n        };\n    }\n}\n\nfunction baseCompile(template, options) {\n    const ast = baseParse(template);\n    transform(ast, Object.assign(options, {\n        nodeTransforms: [transformElement, transformText, transformExpression],\n    }));\n    return generate(ast);\n}\n\nfunction compileToFunction(template, options = {}) {\n    const { code } = baseCompile(template, options);\n    const render = new Function(\"Vue\", code)(runtimeDom);\n    return render;\n}\nregisterRuntimeCompiler(compileToFunction);\n\nexports.computed = computed;\nexports.createApp = createApp;\nexports.createAppAPI = createAppAPI;\nexports.createElementVNode = createVNode;\nexports.createRenderer = createRenderer;\nexports.createTextVNode = createTextVNode;\nexports.effect = effect;\nexports.getCurrentInstance = getCurrentInstance;\nexports.h = h;\nexports.inject = inject;\nexports.isProxy = isProxy;\nexports.isReactive = isReactive;\nexports.isReadonly = isReadonly;\nexports.isRef = isRef;\nexports.provide = provide;\nexports.proxyRefs = proxyRefs;\nexports.reactive = reactive;\nexports.readonly = readonly;\nexports.ref = ref;\nexports.registerRuntimeCompiler = registerRuntimeCompiler;\nexports.renderSlot = renderSlot;\nexports.shallowReadonly = shallowReadonly;\nexports.stop = stop;\nexports.toDisplayString = toDisplayString;\nexports.unRef = unRef;\nexports.watchEffect = watchEffect;\n//# sourceMappingURL=mini-vue.cjs.js.map\n"
  },
  {
    "path": "packages/vue/dist/mini-vue.esm-bundler.js",
    "content": "var ShapeFlags;\n(function (ShapeFlags) {\n    ShapeFlags[ShapeFlags[\"ELEMENT\"] = 1] = \"ELEMENT\";\n    ShapeFlags[ShapeFlags[\"STATEFUL_COMPONENT\"] = 4] = \"STATEFUL_COMPONENT\";\n    ShapeFlags[ShapeFlags[\"TEXT_CHILDREN\"] = 8] = \"TEXT_CHILDREN\";\n    ShapeFlags[ShapeFlags[\"ARRAY_CHILDREN\"] = 16] = \"ARRAY_CHILDREN\";\n    ShapeFlags[ShapeFlags[\"SLOTS_CHILDREN\"] = 32] = \"SLOTS_CHILDREN\";\n})(ShapeFlags || (ShapeFlags = {}));\n\nconst toDisplayString = (val) => {\n    return String(val);\n};\n\nconst isObject = (val) => {\n    return val !== null && typeof val === \"object\";\n};\nconst isString = (val) => typeof val === \"string\";\nconst camelizeRE = /-(\\w)/g;\nconst camelize = (str) => {\n    return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : \"\"));\n};\nconst extend = Object.assign;\nconst isOn = (key) => /^on[A-Z]/.test(key);\nfunction hasChanged(value, oldValue) {\n    return !Object.is(value, oldValue);\n}\nfunction hasOwn(val, key) {\n    return Object.prototype.hasOwnProperty.call(val, key);\n}\nconst capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);\nconst toHandlerKey = (str) => str ? `on${capitalize(str)}` : ``;\nconst hyphenateRE = /\\B([A-Z])/g;\nconst hyphenate = (str) => str.replace(hyphenateRE, \"-$1\").toLowerCase();\n\nconst createVNode = function (type, props, children) {\n    const vnode = {\n        el: null,\n        component: null,\n        key: props === null || props === void 0 ? void 0 : props.key,\n        type,\n        props: props || {},\n        children,\n        shapeFlag: getShapeFlag(type),\n    };\n    if (Array.isArray(children)) {\n        vnode.shapeFlag |= 16;\n    }\n    else if (typeof children === \"string\") {\n        vnode.shapeFlag |= 8;\n    }\n    normalizeChildren(vnode, children);\n    return vnode;\n};\nfunction normalizeChildren(vnode, children) {\n    if (typeof children === \"object\") {\n        if (vnode.shapeFlag & 1) ;\n        else {\n            vnode.shapeFlag |= 32;\n        }\n    }\n}\nconst Text = Symbol(\"Text\");\nconst Fragment = Symbol(\"Fragment\");\nfunction createTextVNode(text = \" \") {\n    return createVNode(Text, {}, text);\n}\nfunction normalizeVNode(child) {\n    if (typeof child === \"string\" || typeof child === \"number\") {\n        return createVNode(Text, null, String(child));\n    }\n    else {\n        return child;\n    }\n}\nfunction getShapeFlag(type) {\n    return typeof type === \"string\"\n        ? 1\n        : 4;\n}\n\nconst h = (type, props = null, children = []) => {\n    return createVNode(type, props, children);\n};\n\nfunction createAppAPI(render) {\n    return function createApp(rootComponent) {\n        const app = {\n            _component: rootComponent,\n            mount(rootContainer) {\n                console.log(\"基于根组件创建 vnode\");\n                const vnode = createVNode(rootComponent);\n                console.log(\"调用 render，基于 vnode 进行开箱\");\n                render(vnode, rootContainer);\n            },\n        };\n        return app;\n    };\n}\n\nfunction initProps(instance, rawProps) {\n    console.log(\"initProps\");\n    instance.props = rawProps;\n}\n\nfunction initSlots(instance, children) {\n    const { vnode } = instance;\n    console.log(\"初始化 slots\");\n    if (vnode.shapeFlag & 32) {\n        normalizeObjectSlots(children, (instance.slots = {}));\n    }\n}\nconst normalizeSlotValue = (value) => {\n    return Array.isArray(value) ? value : [value];\n};\nconst normalizeObjectSlots = (rawSlots, slots) => {\n    for (const key in rawSlots) {\n        const value = rawSlots[key];\n        if (typeof value === \"function\") {\n            slots[key] = (props) => normalizeSlotValue(value(props));\n        }\n    }\n};\n\nfunction emit(instance, event, ...rawArgs) {\n    const props = instance.props;\n    let handler = props[toHandlerKey(camelize(event))];\n    if (!handler) {\n        handler = props[(toHandlerKey(hyphenate(event)))];\n    }\n    if (handler) {\n        handler(...rawArgs);\n    }\n}\n\nconst publicPropertiesMap = {\n    $el: (i) => i.vnode.el,\n    $emit: (i) => i.emit,\n    $slots: (i) => i.slots,\n    $props: (i) => i.props,\n};\nconst PublicInstanceProxyHandlers = {\n    get({ _: instance }, key) {\n        const { setupState, props } = instance;\n        console.log(`触发 proxy hook , key -> : ${key}`);\n        if (key[0] !== \"$\") {\n            if (hasOwn(setupState, key)) {\n                return setupState[key];\n            }\n            else if (hasOwn(props, key)) {\n                return props[key];\n            }\n        }\n        const publicGetter = publicPropertiesMap[key];\n        if (publicGetter) {\n            return publicGetter(instance);\n        }\n    },\n    set({ _: instance }, key, value) {\n        const { setupState } = instance;\n        if (hasOwn(setupState, key)) {\n            setupState[key] = value;\n        }\n        return true;\n    },\n};\n\nfunction createDep(effects) {\n    const dep = new Set(effects);\n    return dep;\n}\n\nlet activeEffect = void 0;\nlet shouldTrack = false;\nconst targetMap = new WeakMap();\nclass ReactiveEffect {\n    constructor(fn, scheduler) {\n        this.fn = fn;\n        this.scheduler = scheduler;\n        this.active = true;\n        this.deps = [];\n        console.log(\"创建 ReactiveEffect 对象\");\n    }\n    run() {\n        console.log(\"run\");\n        if (!this.active) {\n            return this.fn();\n        }\n        shouldTrack = true;\n        activeEffect = this;\n        console.log(\"执行用户传入的 fn\");\n        const result = this.fn();\n        shouldTrack = false;\n        activeEffect = undefined;\n        return result;\n    }\n    stop() {\n        if (this.active) {\n            cleanupEffect(this);\n            if (this.onStop) {\n                this.onStop();\n            }\n            this.active = false;\n        }\n    }\n}\nfunction cleanupEffect(effect) {\n    effect.deps.forEach((dep) => {\n        dep.delete(effect);\n    });\n    effect.deps.length = 0;\n}\nfunction effect(fn, options = {}) {\n    const _effect = new ReactiveEffect(fn);\n    extend(_effect, options);\n    _effect.run();\n    const runner = _effect.run.bind(_effect);\n    runner.effect = _effect;\n    return runner;\n}\nfunction stop(runner) {\n    runner.effect.stop();\n}\nfunction track(target, type, key) {\n    if (!isTracking()) {\n        return;\n    }\n    console.log(`触发 track -> target: ${target} type:${type} key:${key}`);\n    let depsMap = targetMap.get(target);\n    if (!depsMap) {\n        depsMap = new Map();\n        targetMap.set(target, depsMap);\n    }\n    let dep = depsMap.get(key);\n    if (!dep) {\n        dep = createDep();\n        depsMap.set(key, dep);\n    }\n    trackEffects(dep);\n}\nfunction trackEffects(dep) {\n    if (!dep.has(activeEffect)) {\n        dep.add(activeEffect);\n        activeEffect.deps.push(dep);\n    }\n}\nfunction trigger(target, type, key) {\n    let deps = [];\n    const depsMap = targetMap.get(target);\n    if (!depsMap)\n        return;\n    const dep = depsMap.get(key);\n    deps.push(dep);\n    const effects = [];\n    deps.forEach((dep) => {\n        effects.push(...dep);\n    });\n    triggerEffects(createDep(effects));\n}\nfunction isTracking() {\n    return shouldTrack && activeEffect !== undefined;\n}\nfunction triggerEffects(dep) {\n    for (const effect of dep) {\n        if (effect.scheduler) {\n            effect.scheduler();\n        }\n        else {\n            effect.run();\n        }\n    }\n}\n\nconst get = createGetter();\nconst set = createSetter();\nconst readonlyGet = createGetter(true);\nconst shallowReadonlyGet = createGetter(true, true);\nfunction createGetter(isReadonly = false, shallow = false) {\n    return function get(target, key, receiver) {\n        const isExistInReactiveMap = () => key === \"__v_raw\" && receiver === reactiveMap.get(target);\n        const isExistInReadonlyMap = () => key === \"__v_raw\" && receiver === readonlyMap.get(target);\n        const isExistInShallowReadonlyMap = () => key === \"__v_raw\" && receiver === shallowReadonlyMap.get(target);\n        if (key === \"__v_isReactive\") {\n            return !isReadonly;\n        }\n        else if (key === \"__v_isReadonly\") {\n            return isReadonly;\n        }\n        else if (isExistInReactiveMap() ||\n            isExistInReadonlyMap() ||\n            isExistInShallowReadonlyMap()) {\n            return target;\n        }\n        const res = Reflect.get(target, key, receiver);\n        if (!isReadonly) {\n            track(target, \"get\", key);\n        }\n        if (shallow) {\n            return res;\n        }\n        if (isObject(res)) {\n            return isReadonly ? readonly(res) : reactive(res);\n        }\n        return res;\n    };\n}\nfunction createSetter() {\n    return function set(target, key, value, receiver) {\n        const result = Reflect.set(target, key, value, receiver);\n        trigger(target, \"set\", key);\n        return result;\n    };\n}\nconst readonlyHandlers = {\n    get: readonlyGet,\n    set(target, key) {\n        console.warn(`Set operation on key \"${String(key)}\" failed: target is readonly.`, target);\n        return true;\n    },\n};\nconst mutableHandlers = {\n    get,\n    set,\n};\nconst shallowReadonlyHandlers = {\n    get: shallowReadonlyGet,\n    set(target, key) {\n        console.warn(`Set operation on key \"${String(key)}\" failed: target is readonly.`, target);\n        return true;\n    },\n};\n\nconst reactiveMap = new WeakMap();\nconst readonlyMap = new WeakMap();\nconst shallowReadonlyMap = new WeakMap();\nvar ReactiveFlags;\n(function (ReactiveFlags) {\n    ReactiveFlags[\"IS_REACTIVE\"] = \"__v_isReactive\";\n    ReactiveFlags[\"IS_READONLY\"] = \"__v_isReadonly\";\n    ReactiveFlags[\"RAW\"] = \"__v_raw\";\n})(ReactiveFlags || (ReactiveFlags = {}));\nfunction reactive(target) {\n    return createReactiveObject(target, reactiveMap, mutableHandlers);\n}\nfunction readonly(target) {\n    return createReactiveObject(target, readonlyMap, readonlyHandlers);\n}\nfunction shallowReadonly(target) {\n    return createReactiveObject(target, shallowReadonlyMap, shallowReadonlyHandlers);\n}\nfunction isProxy(value) {\n    return isReactive(value) || isReadonly(value);\n}\nfunction isReadonly(value) {\n    return !!value[\"__v_isReadonly\"];\n}\nfunction isReactive(value) {\n    return !!value[\"__v_isReactive\"];\n}\nfunction createReactiveObject(target, proxyMap, baseHandlers) {\n    const existingProxy = proxyMap.get(target);\n    if (existingProxy) {\n        return existingProxy;\n    }\n    const proxy = new Proxy(target, baseHandlers);\n    proxyMap.set(target, proxy);\n    return proxy;\n}\n\nclass RefImpl {\n    constructor(value) {\n        this.__v_isRef = true;\n        this._rawValue = value;\n        this._value = convert(value);\n        this.dep = createDep();\n    }\n    get value() {\n        trackRefValue(this);\n        return this._value;\n    }\n    set value(newValue) {\n        if (hasChanged(newValue, this._rawValue)) {\n            this._value = convert(newValue);\n            this._rawValue = newValue;\n            triggerRefValue(this);\n        }\n    }\n}\nfunction ref(value) {\n    return createRef(value);\n}\nfunction convert(value) {\n    return isObject(value) ? reactive(value) : value;\n}\nfunction createRef(value) {\n    const refImpl = new RefImpl(value);\n    return refImpl;\n}\nfunction triggerRefValue(ref) {\n    triggerEffects(ref.dep);\n}\nfunction trackRefValue(ref) {\n    if (isTracking()) {\n        trackEffects(ref.dep);\n    }\n}\nconst shallowUnwrapHandlers = {\n    get(target, key, receiver) {\n        return unRef(Reflect.get(target, key, receiver));\n    },\n    set(target, key, value, receiver) {\n        const oldValue = target[key];\n        if (isRef(oldValue) && !isRef(value)) {\n            return (target[key].value = value);\n        }\n        else {\n            return Reflect.set(target, key, value, receiver);\n        }\n    },\n};\nfunction proxyRefs(objectWithRefs) {\n    return new Proxy(objectWithRefs, shallowUnwrapHandlers);\n}\nfunction unRef(ref) {\n    return isRef(ref) ? ref.value : ref;\n}\nfunction isRef(value) {\n    return !!value.__v_isRef;\n}\n\nclass ComputedRefImpl {\n    constructor(getter) {\n        this._dirty = true;\n        this.dep = createDep();\n        this.effect = new ReactiveEffect(getter, () => {\n            if (this._dirty)\n                return;\n            this._dirty = true;\n            triggerRefValue(this);\n        });\n    }\n    get value() {\n        trackRefValue(this);\n        if (this._dirty) {\n            this._dirty = false;\n            this._value = this.effect.run();\n        }\n        return this._value;\n    }\n}\nfunction computed(getter) {\n    return new ComputedRefImpl(getter);\n}\n\nfunction createComponentInstance(vnode, parent) {\n    const instance = {\n        type: vnode.type,\n        vnode,\n        next: null,\n        props: {},\n        parent,\n        provides: parent ? parent.provides : {},\n        proxy: null,\n        isMounted: false,\n        attrs: {},\n        slots: {},\n        ctx: {},\n        setupState: {},\n        emit: () => { },\n    };\n    instance.ctx = {\n        _: instance,\n    };\n    instance.emit = emit.bind(null, instance);\n    return instance;\n}\nfunction setupComponent(instance) {\n    const { props, children } = instance.vnode;\n    initProps(instance, props);\n    initSlots(instance, children);\n    setupStatefulComponent(instance);\n}\nfunction setupStatefulComponent(instance) {\n    console.log(\"创建 proxy\");\n    instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);\n    const Component = instance.type;\n    const { setup } = Component;\n    if (setup) {\n        setCurrentInstance(instance);\n        const setupContext = createSetupContext(instance);\n        const setupResult = setup && setup(shallowReadonly(instance.props), setupContext);\n        setCurrentInstance(null);\n        handleSetupResult(instance, setupResult);\n    }\n    else {\n        finishComponentSetup(instance);\n    }\n}\nfunction createSetupContext(instance) {\n    console.log(\"初始化 setup context\");\n    return {\n        attrs: instance.attrs,\n        slots: instance.slots,\n        emit: instance.emit,\n        expose: () => { },\n    };\n}\nfunction handleSetupResult(instance, setupResult) {\n    if (typeof setupResult === \"function\") {\n        instance.render = setupResult;\n    }\n    else if (typeof setupResult === \"object\") {\n        instance.setupState = proxyRefs(setupResult);\n    }\n    finishComponentSetup(instance);\n}\nfunction finishComponentSetup(instance) {\n    const Component = instance.type;\n    if (!instance.render) {\n        if (compile && !Component.render) {\n            if (Component.template) {\n                const template = Component.template;\n                Component.render = compile(template);\n            }\n        }\n        instance.render = Component.render;\n    }\n}\nlet currentInstance = {};\nfunction getCurrentInstance() {\n    return currentInstance;\n}\nfunction setCurrentInstance(instance) {\n    currentInstance = instance;\n}\nlet compile;\nfunction registerRuntimeCompiler(_compile) {\n    compile = _compile;\n}\n\nfunction provide(key, value) {\n    var _a;\n    const currentInstance = getCurrentInstance();\n    if (currentInstance) {\n        let { provides } = currentInstance;\n        const parentProvides = (_a = currentInstance.parent) === null || _a === void 0 ? void 0 : _a.provides;\n        if (parentProvides === provides) {\n            provides = currentInstance.provides = Object.create(parentProvides);\n        }\n        provides[key] = value;\n    }\n}\nfunction inject(key, defaultValue) {\n    var _a;\n    const currentInstance = getCurrentInstance();\n    if (currentInstance) {\n        const provides = (_a = currentInstance.parent) === null || _a === void 0 ? void 0 : _a.provides;\n        if (key in provides) {\n            return provides[key];\n        }\n        else if (defaultValue) {\n            if (typeof defaultValue === \"function\") {\n                return defaultValue();\n            }\n            return defaultValue;\n        }\n    }\n}\n\nfunction renderSlot(slots, name, props = {}) {\n    const slot = slots[name];\n    console.log(`渲染插槽 slot -> ${name}`);\n    if (slot) {\n        const slotContent = slot(props);\n        return createVNode(Fragment, {}, slotContent);\n    }\n}\n\nconst queue = [];\nconst activePreFlushCbs = [];\nconst p = Promise.resolve();\nlet isFlushPending = false;\nfunction nextTick(fn) {\n    return fn ? p.then(fn) : p;\n}\nfunction queueJob(job) {\n    if (!queue.includes(job)) {\n        queue.push(job);\n        queueFlush();\n    }\n}\nfunction queueFlush() {\n    if (isFlushPending)\n        return;\n    isFlushPending = true;\n    nextTick(flushJobs);\n}\nfunction queuePreFlushCb(cb) {\n    queueCb(cb, activePreFlushCbs);\n}\nfunction queueCb(cb, activeQueue) {\n    activeQueue.push(cb);\n    queueFlush();\n}\nfunction flushJobs() {\n    isFlushPending = false;\n    flushPreFlushCbs();\n    let job;\n    while ((job = queue.shift())) {\n        if (job) {\n            job();\n        }\n    }\n}\nfunction flushPreFlushCbs() {\n    for (let i = 0; i < activePreFlushCbs.length; i++) {\n        activePreFlushCbs[i]();\n    }\n}\n\nfunction shouldUpdateComponent(prevVNode, nextVNode) {\n    const { props: prevProps } = prevVNode;\n    const { props: nextProps } = nextVNode;\n    if (prevProps === nextProps) {\n        return false;\n    }\n    if (!prevProps) {\n        return !!nextProps;\n    }\n    if (!nextProps) {\n        return true;\n    }\n    return hasPropsChanged(prevProps, nextProps);\n}\nfunction hasPropsChanged(prevProps, nextProps) {\n    const nextKeys = Object.keys(nextProps);\n    if (nextKeys.length !== Object.keys(prevProps).length) {\n        return true;\n    }\n    for (let i = 0; i < nextKeys.length; i++) {\n        const key = nextKeys[i];\n        if (nextProps[key] !== prevProps[key]) {\n            return true;\n        }\n    }\n    return false;\n}\n\nfunction createRenderer(options) {\n    const { createElement: hostCreateElement, setElementText: hostSetElementText, patchProp: hostPatchProp, insert: hostInsert, remove: hostRemove, setText: hostSetText, createText: hostCreateText, } = options;\n    const render = (vnode, container) => {\n        console.log(\"调用 patch\");\n        patch(null, vnode, container);\n    };\n    function patch(n1, n2, container = null, anchor = null, parentComponent = null) {\n        const { type, shapeFlag } = n2;\n        switch (type) {\n            case Text:\n                processText(n1, n2, container);\n                break;\n            case Fragment:\n                processFragment(n1, n2, container);\n                break;\n            default:\n                if (shapeFlag & 1) {\n                    console.log(\"处理 element\");\n                    processElement(n1, n2, container, anchor, parentComponent);\n                }\n                else if (shapeFlag & 4) {\n                    console.log(\"处理 component\");\n                    processComponent(n1, n2, container, parentComponent);\n                }\n        }\n    }\n    function processFragment(n1, n2, container) {\n        if (!n1) {\n            console.log(\"初始化 Fragment 类型的节点\");\n            mountChildren(n2.children, container);\n        }\n    }\n    function processText(n1, n2, container) {\n        console.log(\"处理 Text 节点\");\n        if (n1 === null) {\n            console.log(\"初始化 Text 类型的节点\");\n            hostInsert((n2.el = hostCreateText(n2.children)), container);\n        }\n        else {\n            const el = (n2.el = n1.el);\n            if (n2.children !== n1.children) {\n                console.log(\"更新 Text 类型的节点\");\n                hostSetText(el, n2.children);\n            }\n        }\n    }\n    function processElement(n1, n2, container, anchor, parentComponent) {\n        if (!n1) {\n            mountElement(n2, container, anchor);\n        }\n        else {\n            updateElement(n1, n2, container, anchor, parentComponent);\n        }\n    }\n    function updateElement(n1, n2, container, anchor, parentComponent) {\n        const oldProps = (n1 && n1.props) || {};\n        const newProps = n2.props || {};\n        console.log(\"应该更新 element\");\n        console.log(\"旧的 vnode\", n1);\n        console.log(\"新的 vnode\", n2);\n        const el = (n2.el = n1.el);\n        patchProps(el, oldProps, newProps);\n        patchChildren(n1, n2, el, anchor, parentComponent);\n    }\n    function patchProps(el, oldProps, newProps) {\n        for (const key in newProps) {\n            const prevProp = oldProps[key];\n            const nextProp = newProps[key];\n            if (prevProp !== nextProp) {\n                hostPatchProp(el, key, prevProp, nextProp);\n            }\n        }\n        for (const key in oldProps) {\n            const prevProp = oldProps[key];\n            const nextProp = null;\n            if (!(key in newProps)) {\n                hostPatchProp(el, key, prevProp, nextProp);\n            }\n        }\n    }\n    function patchChildren(n1, n2, container, anchor, parentComponent) {\n        const { shapeFlag: prevShapeFlag, children: c1 } = n1;\n        const { shapeFlag, children: c2 } = n2;\n        if (shapeFlag & 8) {\n            if (c2 !== c1) {\n                console.log(\"类型为 text_children, 当前需要更新\");\n                hostSetElementText(container, c2);\n            }\n        }\n        else {\n            if (prevShapeFlag & 8) {\n                hostSetElementText(container, \"\");\n                mountChildren(c2, container);\n            }\n            else {\n                patchKeyedChildren(c1, c2, container, parentComponent, anchor);\n            }\n        }\n    }\n    function patchKeyedChildren(c1, c2, container, parentAnchor, parentComponent) {\n        let i = 0;\n        const l2 = c2.length;\n        let e1 = c1.length - 1;\n        let e2 = l2 - 1;\n        const isSameVNodeType = (n1, n2) => {\n            return n1.type === n2.type && n1.key === n2.key;\n        };\n        while (i <= e1 && i <= e2) {\n            const prevChild = c1[i];\n            const nextChild = c2[i];\n            if (!isSameVNodeType(prevChild, nextChild)) {\n                console.log(\"两个 child 不相等(从左往右比对)\");\n                console.log(`prevChild:${prevChild}`);\n                console.log(`nextChild:${nextChild}`);\n                break;\n            }\n            console.log(\"两个 child 相等，接下来对比这两个 child 节点(从左往右比对)\");\n            patch(prevChild, nextChild, container, parentAnchor, parentComponent);\n            i++;\n        }\n        while (i <= e1 && i <= e2) {\n            const prevChild = c1[e1];\n            const nextChild = c2[e2];\n            if (!isSameVNodeType(prevChild, nextChild)) {\n                console.log(\"两个 child 不相等(从右往左比对)\");\n                console.log(`prevChild:${prevChild}`);\n                console.log(`nextChild:${nextChild}`);\n                break;\n            }\n            console.log(\"两个 child 相等，接下来对比这两个 child 节点(从右往左比对)\");\n            patch(prevChild, nextChild, container, parentAnchor, parentComponent);\n            e1--;\n            e2--;\n        }\n        if (i > e1 && i <= e2) {\n            const nextPos = e2 + 1;\n            const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;\n            while (i <= e2) {\n                console.log(`需要新创建一个 vnode: ${c2[i].key}`);\n                patch(null, c2[i], container, anchor, parentComponent);\n                i++;\n            }\n        }\n        else if (i > e2 && i <= e1) {\n            while (i <= e1) {\n                console.log(`需要删除当前的 vnode: ${c1[i].key}`);\n                hostRemove(c1[i].el);\n                i++;\n            }\n        }\n        else {\n            let s1 = i;\n            let s2 = i;\n            const keyToNewIndexMap = new Map();\n            let moved = false;\n            let maxNewIndexSoFar = 0;\n            for (let i = s2; i <= e2; i++) {\n                const nextChild = c2[i];\n                keyToNewIndexMap.set(nextChild.key, i);\n            }\n            const toBePatched = e2 - s2 + 1;\n            let patched = 0;\n            const newIndexToOldIndexMap = new Array(toBePatched);\n            for (let i = 0; i < toBePatched; i++)\n                newIndexToOldIndexMap[i] = 0;\n            for (i = s1; i <= e1; i++) {\n                const prevChild = c1[i];\n                if (patched >= toBePatched) {\n                    hostRemove(prevChild.el);\n                    continue;\n                }\n                let newIndex;\n                if (prevChild.key != null) {\n                    newIndex = keyToNewIndexMap.get(prevChild.key);\n                }\n                else {\n                    for (let j = s2; j <= e2; j++) {\n                        if (isSameVNodeType(prevChild, c2[j])) {\n                            newIndex = j;\n                            break;\n                        }\n                    }\n                }\n                if (newIndex === undefined) {\n                    hostRemove(prevChild.el);\n                }\n                else {\n                    console.log(\"新老节点都存在\");\n                    newIndexToOldIndexMap[newIndex - s2] = i + 1;\n                    if (newIndex >= maxNewIndexSoFar) {\n                        maxNewIndexSoFar = newIndex;\n                    }\n                    else {\n                        moved = true;\n                    }\n                    patch(prevChild, c2[newIndex], container, null, parentComponent);\n                    patched++;\n                }\n            }\n            const increasingNewIndexSequence = moved\n                ? getSequence(newIndexToOldIndexMap)\n                : [];\n            let j = increasingNewIndexSequence.length - 1;\n            for (let i = toBePatched - 1; i >= 0; i--) {\n                const nextIndex = s2 + i;\n                const nextChild = c2[nextIndex];\n                const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;\n                if (newIndexToOldIndexMap[i] === 0) {\n                    patch(null, nextChild, container, anchor, parentComponent);\n                }\n                else if (moved) {\n                    if (j < 0 || increasingNewIndexSequence[j] !== i) {\n                        hostInsert(nextChild.el, container, anchor);\n                    }\n                    else {\n                        j--;\n                    }\n                }\n            }\n        }\n    }\n    function mountElement(vnode, container, anchor) {\n        const { shapeFlag, props } = vnode;\n        const el = (vnode.el = hostCreateElement(vnode.type));\n        if (shapeFlag & 8) {\n            console.log(`处理文本:${vnode.children}`);\n            hostSetElementText(el, vnode.children);\n        }\n        else if (shapeFlag & 16) {\n            mountChildren(vnode.children, el);\n        }\n        if (props) {\n            for (const key in props) {\n                const nextVal = props[key];\n                hostPatchProp(el, key, null, nextVal);\n            }\n        }\n        console.log(\"vnodeHook  -> onVnodeBeforeMount\");\n        console.log(\"DirectiveHook  -> beforeMount\");\n        console.log(\"transition  -> beforeEnter\");\n        hostInsert(el, container, anchor);\n        console.log(\"vnodeHook  -> onVnodeMounted\");\n        console.log(\"DirectiveHook  -> mounted\");\n        console.log(\"transition  -> enter\");\n    }\n    function mountChildren(children, container) {\n        children.forEach((VNodeChild) => {\n            console.log(\"mountChildren:\", VNodeChild);\n            patch(null, VNodeChild, container);\n        });\n    }\n    function processComponent(n1, n2, container, parentComponent) {\n        if (!n1) {\n            mountComponent(n2, container, parentComponent);\n        }\n        else {\n            updateComponent(n1, n2);\n        }\n    }\n    function updateComponent(n1, n2, container) {\n        console.log(\"更新组件\", n1, n2);\n        const instance = (n2.component = n1.component);\n        if (shouldUpdateComponent(n1, n2)) {\n            console.log(`组件需要更新: ${instance}`);\n            instance.next = n2;\n            instance.update();\n        }\n        else {\n            console.log(`组件不需要更新: ${instance}`);\n            n2.component = n1.component;\n            n2.el = n1.el;\n            instance.vnode = n2;\n        }\n    }\n    function mountComponent(initialVNode, container, parentComponent) {\n        const instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent));\n        console.log(`创建组件实例:${instance.type.name}`);\n        setupComponent(instance);\n        setupRenderEffect(instance, initialVNode, container);\n    }\n    function setupRenderEffect(instance, initialVNode, container) {\n        function componentUpdateFn() {\n            if (!instance.isMounted) {\n                console.log(`${instance.type.name}:调用 render,获取 subTree`);\n                const proxyToUse = instance.proxy;\n                const subTree = (instance.subTree = normalizeVNode(instance.render.call(proxyToUse, proxyToUse)));\n                console.log(\"subTree\", subTree);\n                console.log(`${instance.type.name}:触发 beforeMount hook`);\n                console.log(`${instance.type.name}:触发 onVnodeBeforeMount hook`);\n                patch(null, subTree, container, null, instance);\n                initialVNode.el = subTree.el;\n                console.log(`${instance.type.name}:触发 mounted hook`);\n                instance.isMounted = true;\n            }\n            else {\n                console.log(`${instance.type.name}:调用更新逻辑`);\n                const { next, vnode } = instance;\n                if (next) {\n                    next.el = vnode.el;\n                    updateComponentPreRender(instance, next);\n                }\n                const proxyToUse = instance.proxy;\n                const nextTree = normalizeVNode(instance.render.call(proxyToUse, proxyToUse));\n                const prevTree = instance.subTree;\n                instance.subTree = nextTree;\n                console.log(`${instance.type.name}:触发 beforeUpdated hook`);\n                console.log(`${instance.type.name}:触发 onVnodeBeforeUpdate hook`);\n                patch(prevTree, nextTree, prevTree.el, null, instance);\n                console.log(`${instance.type.name}:触发 updated hook`);\n                console.log(`${instance.type.name}:触发 onVnodeUpdated hook`);\n            }\n        }\n        instance.update = effect(componentUpdateFn, {\n            scheduler: () => {\n                queueJob(instance.update);\n            },\n        });\n    }\n    function updateComponentPreRender(instance, nextVNode) {\n        nextVNode.component = instance;\n        instance.vnode = nextVNode;\n        instance.next = null;\n        const { props } = nextVNode;\n        console.log(\"更新组件的 props\", props);\n        instance.props = props;\n        console.log(\"更新组件的 slots\");\n    }\n    return {\n        render,\n        createApp: createAppAPI(render),\n    };\n}\nfunction getSequence(arr) {\n    const p = arr.slice();\n    const result = [0];\n    let i, j, u, v, c;\n    const len = arr.length;\n    for (i = 0; i < len; i++) {\n        const arrI = arr[i];\n        if (arrI !== 0) {\n            j = result[result.length - 1];\n            if (arr[j] < arrI) {\n                p[i] = j;\n                result.push(i);\n                continue;\n            }\n            u = 0;\n            v = result.length - 1;\n            while (u < v) {\n                c = (u + v) >> 1;\n                if (arr[result[c]] < arrI) {\n                    u = c + 1;\n                }\n                else {\n                    v = c;\n                }\n            }\n            if (arrI < arr[result[u]]) {\n                if (u > 0) {\n                    p[i] = result[u - 1];\n                }\n                result[u] = i;\n            }\n        }\n    }\n    u = result.length;\n    v = result[u - 1];\n    while (u-- > 0) {\n        result[u] = v;\n        v = p[v];\n    }\n    return result;\n}\n\nfunction watchEffect(effect) {\n    return doWatch(effect);\n}\nfunction doWatch(source) {\n    const job = () => {\n        effect.run();\n    };\n    const scheduler = () => queuePreFlushCb(job);\n    let cleanup;\n    const onCleanup = (fn) => {\n        cleanup = effect.onStop = () => {\n            fn();\n        };\n    };\n    const getter = () => {\n        if (cleanup) {\n            cleanup();\n        }\n        source(onCleanup);\n    };\n    const effect = new ReactiveEffect(getter, scheduler);\n    effect.run();\n    return () => {\n        effect.stop();\n    };\n}\n\nfunction createElement(type) {\n    console.log(\"CreateElement\", type);\n    const element = document.createElement(type);\n    return element;\n}\nfunction createText(text) {\n    return document.createTextNode(text);\n}\nfunction setText(node, text) {\n    node.nodeValue = text;\n}\nfunction setElementText(el, text) {\n    console.log(\"SetElementText\", el, text);\n    el.textContent = text;\n}\nfunction patchProp(el, key, preValue, nextValue) {\n    console.log(`PatchProp 设置属性:${key} 值:${nextValue}`);\n    console.log(`key: ${key} 之前的值是:${preValue}`);\n    if (isOn(key)) {\n        const invokers = el._vei || (el._vei = {});\n        const existingInvoker = invokers[key];\n        if (nextValue && existingInvoker) {\n            existingInvoker.value = nextValue;\n        }\n        else {\n            const eventName = key.slice(2).toLowerCase();\n            if (nextValue) {\n                const invoker = (invokers[key] = nextValue);\n                el.addEventListener(eventName, invoker);\n            }\n            else {\n                el.removeEventListener(eventName, existingInvoker);\n                invokers[key] = undefined;\n            }\n        }\n    }\n    else {\n        if (nextValue === null || nextValue === \"\") {\n            el.removeAttribute(key);\n        }\n        else {\n            el.setAttribute(key, nextValue);\n        }\n    }\n}\nfunction insert(child, parent, anchor = null) {\n    console.log(\"Insert\");\n    parent.insertBefore(child, anchor);\n}\nfunction remove(child) {\n    const parent = child.parentNode;\n    if (parent) {\n        parent.removeChild(child);\n    }\n}\nlet renderer;\nfunction ensureRenderer() {\n    return (renderer ||\n        (renderer = createRenderer({\n            createElement,\n            createText,\n            setText,\n            setElementText,\n            patchProp,\n            insert,\n            remove,\n        })));\n}\nconst createApp = (...args) => {\n    return ensureRenderer().createApp(...args);\n};\n\nvar runtimeDom = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    createApp: createApp,\n    getCurrentInstance: getCurrentInstance,\n    registerRuntimeCompiler: registerRuntimeCompiler,\n    inject: inject,\n    provide: provide,\n    renderSlot: renderSlot,\n    createTextVNode: createTextVNode,\n    createElementVNode: createVNode,\n    createRenderer: createRenderer,\n    toDisplayString: toDisplayString,\n    watchEffect: watchEffect,\n    reactive: reactive,\n    ref: ref,\n    readonly: readonly,\n    unRef: unRef,\n    proxyRefs: proxyRefs,\n    isReadonly: isReadonly,\n    isReactive: isReactive,\n    isProxy: isProxy,\n    isRef: isRef,\n    shallowReadonly: shallowReadonly,\n    effect: effect,\n    stop: stop,\n    computed: computed,\n    h: h,\n    createAppAPI: createAppAPI\n});\n\nconst TO_DISPLAY_STRING = Symbol(`toDisplayString`);\nconst CREATE_ELEMENT_VNODE = Symbol(\"createElementVNode\");\nconst helperNameMap = {\n    [TO_DISPLAY_STRING]: \"toDisplayString\",\n    [CREATE_ELEMENT_VNODE]: \"createElementVNode\"\n};\n\nfunction generate(ast, options = {}) {\n    const context = createCodegenContext(ast, options);\n    const { push, mode } = context;\n    if (mode === \"module\") {\n        genModulePreamble(ast, context);\n    }\n    else {\n        genFunctionPreamble(ast, context);\n    }\n    const functionName = \"render\";\n    const args = [\"_ctx\"];\n    const signature = args.join(\", \");\n    push(`function ${functionName}(${signature}) {`);\n    push(\"return \");\n    genNode(ast.codegenNode, context);\n    push(\"}\");\n    return {\n        code: context.code,\n    };\n}\nfunction genFunctionPreamble(ast, context) {\n    const { runtimeGlobalName, push, newline } = context;\n    const VueBinging = runtimeGlobalName;\n    const aliasHelper = (s) => `${helperNameMap[s]} : _${helperNameMap[s]}`;\n    if (ast.helpers.length > 0) {\n        push(`\n        const { ${ast.helpers.map(aliasHelper).join(\", \")}} = ${VueBinging} \n\n      `);\n    }\n    newline();\n    push(`return `);\n}\nfunction genNode(node, context) {\n    switch (node.type) {\n        case 2:\n            genInterpolation(node, context);\n            break;\n        case 3:\n            genExpression(node, context);\n            break;\n        case 4:\n            genElement(node, context);\n            break;\n        case 5:\n            genCompoundExpression(node, context);\n            break;\n        case 0:\n            genText(node, context);\n            break;\n    }\n}\nfunction genCompoundExpression(node, context) {\n    const { push } = context;\n    for (let i = 0; i < node.children.length; i++) {\n        const child = node.children[i];\n        if (isString(child)) {\n            push(child);\n        }\n        else {\n            genNode(child, context);\n        }\n    }\n}\nfunction genText(node, context) {\n    const { push } = context;\n    push(`'${node.content}'`);\n}\nfunction genElement(node, context) {\n    const { push, helper } = context;\n    const { tag, props, children } = node;\n    push(`${helper(CREATE_ELEMENT_VNODE)}(`);\n    genNodeList(genNullableArgs([tag, props, children]), context);\n    push(`)`);\n}\nfunction genNodeList(nodes, context) {\n    const { push } = context;\n    for (let i = 0; i < nodes.length; i++) {\n        const node = nodes[i];\n        if (isString(node)) {\n            push(`${node}`);\n        }\n        else {\n            genNode(node, context);\n        }\n        if (i < nodes.length - 1) {\n            push(\", \");\n        }\n    }\n}\nfunction genNullableArgs(args) {\n    let i = args.length;\n    while (i--) {\n        if (args[i] != null)\n            break;\n    }\n    return args.slice(0, i + 1).map((arg) => arg || \"null\");\n}\nfunction genExpression(node, context) {\n    context.push(node.content, node);\n}\nfunction genInterpolation(node, context) {\n    const { push, helper } = context;\n    push(`${helper(TO_DISPLAY_STRING)}(`);\n    genNode(node.content, context);\n    push(\")\");\n}\nfunction genModulePreamble(ast, context) {\n    const { push, newline, runtimeModuleName } = context;\n    if (ast.helpers.length) {\n        const code = `import {${ast.helpers\n            .map((s) => `${helperNameMap[s]} as _${helperNameMap[s]}`)\n            .join(\", \")} } from ${JSON.stringify(runtimeModuleName)}`;\n        push(code);\n    }\n    newline();\n    push(`export `);\n}\nfunction createCodegenContext(ast, { runtimeModuleName = \"vue\", runtimeGlobalName = \"Vue\", mode = \"function\" }) {\n    const context = {\n        code: \"\",\n        mode,\n        runtimeModuleName,\n        runtimeGlobalName,\n        helper(key) {\n            return `_${helperNameMap[key]}`;\n        },\n        push(code) {\n            context.code += code;\n        },\n        newline() {\n            context.code += \"\\n\";\n        },\n    };\n    return context;\n}\n\nvar TagType;\n(function (TagType) {\n    TagType[TagType[\"Start\"] = 0] = \"Start\";\n    TagType[TagType[\"End\"] = 1] = \"End\";\n})(TagType || (TagType = {}));\nfunction baseParse(content) {\n    const context = createParserContext(content);\n    return createRoot(parseChildren(context, []));\n}\nfunction createParserContext(content) {\n    console.log(\"创建 paserContext\");\n    return {\n        source: content,\n    };\n}\nfunction parseChildren(context, ancestors) {\n    console.log(\"开始解析 children\");\n    const nodes = [];\n    while (!isEnd(context, ancestors)) {\n        let node;\n        const s = context.source;\n        if (startsWith(s, \"{{\")) {\n            node = parseInterpolation(context);\n        }\n        else if (s[0] === \"<\") {\n            if (s[1] === \"/\") {\n                if (/[a-z]/i.test(s[2])) {\n                    parseTag(context, 1);\n                    continue;\n                }\n            }\n            else if (/[a-z]/i.test(s[1])) {\n                node = parseElement(context, ancestors);\n            }\n        }\n        if (!node) {\n            node = parseText(context);\n        }\n        nodes.push(node);\n    }\n    return nodes;\n}\nfunction isEnd(context, ancestors) {\n    const s = context.source;\n    if (context.source.startsWith(\"</\")) {\n        for (let i = ancestors.length - 1; i >= 0; --i) {\n            if (startsWithEndTagOpen(s, ancestors[i].tag)) {\n                return true;\n            }\n        }\n    }\n    return !context.source;\n}\nfunction parseElement(context, ancestors) {\n    const element = parseTag(context, 0);\n    ancestors.push(element);\n    const children = parseChildren(context, ancestors);\n    ancestors.pop();\n    if (startsWithEndTagOpen(context.source, element.tag)) {\n        parseTag(context, 1);\n    }\n    else {\n        throw new Error(`缺失结束标签：${element.tag}`);\n    }\n    element.children = children;\n    return element;\n}\nfunction startsWithEndTagOpen(source, tag) {\n    return (startsWith(source, \"</\") &&\n        source.slice(2, 2 + tag.length).toLowerCase() === tag.toLowerCase());\n}\nfunction parseTag(context, type) {\n    const match = /^<\\/?([a-z][^\\r\\n\\t\\f />]*)/i.exec(context.source);\n    const tag = match[1];\n    advanceBy(context, match[0].length);\n    advanceBy(context, 1);\n    if (type === 1)\n        return;\n    let tagType = 0;\n    return {\n        type: 4,\n        tag,\n        tagType,\n    };\n}\nfunction parseInterpolation(context) {\n    const openDelimiter = \"{{\";\n    const closeDelimiter = \"}}\";\n    const closeIndex = context.source.indexOf(closeDelimiter, openDelimiter.length);\n    advanceBy(context, 2);\n    const rawContentLength = closeIndex - openDelimiter.length;\n    const rawContent = context.source.slice(0, rawContentLength);\n    const preTrimContent = parseTextData(context, rawContent.length);\n    const content = preTrimContent.trim();\n    advanceBy(context, closeDelimiter.length);\n    return {\n        type: 2,\n        content: {\n            type: 3,\n            content,\n        },\n    };\n}\nfunction parseText(context) {\n    console.log(\"解析 text\", context);\n    const endTokens = [\"<\", \"{{\"];\n    let endIndex = context.source.length;\n    for (let i = 0; i < endTokens.length; i++) {\n        const index = context.source.indexOf(endTokens[i]);\n        if (index !== -1 && endIndex > index) {\n            endIndex = index;\n        }\n    }\n    const content = parseTextData(context, endIndex);\n    return {\n        type: 0,\n        content,\n    };\n}\nfunction parseTextData(context, length) {\n    console.log(\"解析 textData\");\n    const rawText = context.source.slice(0, length);\n    advanceBy(context, length);\n    return rawText;\n}\nfunction advanceBy(context, numberOfCharacters) {\n    console.log(\"推进代码\", context, numberOfCharacters);\n    context.source = context.source.slice(numberOfCharacters);\n}\nfunction createRoot(children) {\n    return {\n        type: 1,\n        children,\n        helpers: [],\n    };\n}\nfunction startsWith(source, searchString) {\n    return source.startsWith(searchString);\n}\n\nfunction transform(root, options = {}) {\n    const context = createTransformContext(root, options);\n    traverseNode(root, context);\n    createRootCodegen(root);\n    root.helpers.push(...context.helpers.keys());\n}\nfunction traverseNode(node, context) {\n    const type = node.type;\n    const nodeTransforms = context.nodeTransforms;\n    const exitFns = [];\n    for (let i = 0; i < nodeTransforms.length; i++) {\n        const transform = nodeTransforms[i];\n        const onExit = transform(node, context);\n        if (onExit) {\n            exitFns.push(onExit);\n        }\n    }\n    switch (type) {\n        case 2:\n            context.helper(TO_DISPLAY_STRING);\n            break;\n        case 1:\n        case 4:\n            traverseChildren(node, context);\n            break;\n    }\n    let i = exitFns.length;\n    while (i--) {\n        exitFns[i]();\n    }\n}\nfunction traverseChildren(parent, context) {\n    parent.children.forEach((node) => {\n        traverseNode(node, context);\n    });\n}\nfunction createTransformContext(root, options) {\n    const context = {\n        root,\n        nodeTransforms: options.nodeTransforms || [],\n        helpers: new Map(),\n        helper(name) {\n            const count = context.helpers.get(name) || 0;\n            context.helpers.set(name, count + 1);\n        },\n    };\n    return context;\n}\nfunction createRootCodegen(root, context) {\n    const { children } = root;\n    const child = children[0];\n    if (child.type === 4 && child.codegenNode) {\n        const codegenNode = child.codegenNode;\n        root.codegenNode = codegenNode;\n    }\n    else {\n        root.codegenNode = child;\n    }\n}\n\nfunction transformExpression(node) {\n    if (node.type === 2) {\n        node.content = processExpression(node.content);\n    }\n}\nfunction processExpression(node) {\n    node.content = `_ctx.${node.content}`;\n    return node;\n}\n\nvar NodeTypes;\n(function (NodeTypes) {\n    NodeTypes[NodeTypes[\"TEXT\"] = 0] = \"TEXT\";\n    NodeTypes[NodeTypes[\"ROOT\"] = 1] = \"ROOT\";\n    NodeTypes[NodeTypes[\"INTERPOLATION\"] = 2] = \"INTERPOLATION\";\n    NodeTypes[NodeTypes[\"SIMPLE_EXPRESSION\"] = 3] = \"SIMPLE_EXPRESSION\";\n    NodeTypes[NodeTypes[\"ELEMENT\"] = 4] = \"ELEMENT\";\n    NodeTypes[NodeTypes[\"COMPOUND_EXPRESSION\"] = 5] = \"COMPOUND_EXPRESSION\";\n})(NodeTypes || (NodeTypes = {}));\nvar ElementTypes;\n(function (ElementTypes) {\n    ElementTypes[ElementTypes[\"ELEMENT\"] = 0] = \"ELEMENT\";\n})(ElementTypes || (ElementTypes = {}));\nfunction createVNodeCall(context, tag, props, children) {\n    if (context) {\n        context.helper(CREATE_ELEMENT_VNODE);\n    }\n    return {\n        type: 4,\n        tag,\n        props,\n        children,\n    };\n}\n\nfunction transformElement(node, context) {\n    if (node.type === 4) {\n        return () => {\n            const vnodeTag = `'${node.tag}'`;\n            const vnodeProps = null;\n            let vnodeChildren = null;\n            if (node.children.length > 0) {\n                if (node.children.length === 1) {\n                    const child = node.children[0];\n                    vnodeChildren = child;\n                }\n            }\n            node.codegenNode = createVNodeCall(context, vnodeTag, vnodeProps, vnodeChildren);\n        };\n    }\n}\n\nfunction isText(node) {\n    return node.type === 2 || node.type === 0;\n}\n\nfunction transformText(node, context) {\n    if (node.type === 4) {\n        return () => {\n            const children = node.children;\n            let currentContainer;\n            for (let i = 0; i < children.length; i++) {\n                const child = children[i];\n                if (isText(child)) {\n                    for (let j = i + 1; j < children.length; j++) {\n                        const next = children[j];\n                        if (isText(next)) {\n                            if (!currentContainer) {\n                                currentContainer = children[i] = {\n                                    type: 5,\n                                    loc: child.loc,\n                                    children: [child],\n                                };\n                            }\n                            currentContainer.children.push(` + `, next);\n                            children.splice(j, 1);\n                            j--;\n                        }\n                        else {\n                            currentContainer = undefined;\n                            break;\n                        }\n                    }\n                }\n            }\n        };\n    }\n}\n\nfunction baseCompile(template, options) {\n    const ast = baseParse(template);\n    transform(ast, Object.assign(options, {\n        nodeTransforms: [transformElement, transformText, transformExpression],\n    }));\n    return generate(ast);\n}\n\nfunction compileToFunction(template, options = {}) {\n    const { code } = baseCompile(template, options);\n    const render = new Function(\"Vue\", code)(runtimeDom);\n    return render;\n}\nregisterRuntimeCompiler(compileToFunction);\n\nexport { computed, createApp, createAppAPI, createVNode as createElementVNode, createRenderer, createTextVNode, effect, getCurrentInstance, h, inject, isProxy, isReactive, isReadonly, isRef, provide, proxyRefs, reactive, readonly, ref, registerRuntimeCompiler, renderSlot, shallowReadonly, stop, toDisplayString, unRef, watchEffect };\n//# sourceMappingURL=mini-vue.esm-bundler.js.map\n"
  },
  {
    "path": "packages/vue/example/apiInject/App.js",
    "content": "// 组件 provide 和 inject 功能\nimport {\n  h,\n  provide,\n  inject,\n} from \"../../dist/mini-vue.esm-bundler.js\";\n\nconst ProviderOne = {\n  setup() {\n    provide(\"foo\", \"foo\");\n    provide(\"bar\", \"bar\");\n    return () => h(ProviderTwo);\n  },\n};\n\nconst ProviderTwo = {\n  setup() {\n    // override parent value\n    provide(\"foo\", \"fooOverride\");\n    provide(\"baz\", \"baz\");\n    const foo = inject(\"foo\");\n    // 这里获取的 foo 的值应该是 \"foo\"\n    // 这个组件的子组件获取的 foo ，才应该是 fooOverride\n    if (foo !== \"foo\") {\n      throw new Error(\"Foo should equal to foo\");\n    }\n    return () => h(Consumer);\n  },\n};\n\nconst Consumer = {\n  setup() {\n    const foo = inject(\"foo\");\n    const bar = inject(\"bar\");\n    const baz = inject(\"baz\");\n    return () => {\n      return h(\"div\", {}, `${foo}-${bar}-${baz}`);\n    };\n  },\n};\n\nexport default {\n  name: \"App\",\n  setup() {\n    return () => h(\"div\", {}, [h(\"p\", {}, \"apiInject\"), h(ProviderOne)]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/apiInject/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/compiler-base/App.js",
    "content": "// 最简单的情况\n// template 只有一个 interpolation\n// export default {\n//   template: `{{msg}}`,\n//   setup() {\n//     return {\n//       msg: \"vue3 - compiler\",\n//     };\n//   },\n// };\n\n\n// 复杂一点\n// template 包含 element 和 interpolation \nexport default {\n  template: `<p>{{msg}}</p>`,\n  setup() {\n    return {\n      msg: \"vue3 - compiler\",\n    };\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/compiler-base/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n\n\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/componentEmit/App.js",
    "content": "// 组件 emit 逻辑 demo\n// click emit 发出 change， 可以触发 App 组件内定义好的侦听函数\n// 允许接收多个参数\nimport { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nimport Child from \"./Child.js\";\n\nexport default {\n  name: \"App\",\n  setup() {},\n\n  render() {\n    return h(\"div\", {}, [\n      h(\"div\", {}, \"你好\"),\n      h(Child, {\n        msg: \"your name is child\",\n        onChange(a, b) {\n          console.log(\"---------------change------------------\");\n          console.log(a, b);\n        },\n        onChangePageName(a, b) {\n          console.log(\"---------------change-page-name------------------\");\n          console.log(a, b);\n        },\n      }),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/componentEmit/Child.js",
    "content": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  setup(props, { emit }) {\n    emit(\"change\", \"aaaaa\", \"bbbbbb\");\n    // 支持多个 -\n    emit(\"change-page-name\", \"start\", \"game\");\n  },\n  render() {\n    return h(\"div\", {}, \"child\");\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/componentEmit/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/componentProxy/App.js",
    "content": "// 在 render 中使用 proxy 调用 emit 函数\n// 也可以直接使用 this\n// 验证 proxy 的实现逻辑\nimport { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nimport Child from \"./Child.js\";\n\nexport default {\n  name: \"App\",\n  setup() {},\n\n  render() {\n    return h(\"div\", {}, [\n      h(\"div\", {}, \"你好\"),\n      h(Child, {\n        msg: \"your name is child\",\n        onChange(a, b) {\n          console.log(\"---------------change------------------\");\n          console.log(a, b);\n        },\n      }),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/componentProxy/Child.js",
    "content": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  setup(props, { emit }) {},\n  render(proxy) {\n    const self = this\n    return h(\"div\", {}, [\n      h(\n        \"button\",\n        {\n          onClick() {\n            console.log(proxy);\n            console.log(\"click\");\n            proxy.$emit(\"change\", \"aaa\", \"bbbb\");\n            // 使用 this\n            console.log(this)\n            self.$emit(\"change\", \"ccc\", \"ddd\");\n          },\n        },\n        \"emit\"\n      ),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/componentProxy/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/componentSlots/App.js",
    "content": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nimport Child from \"./Child.js\";\n\nexport default {\n  name: \"App\",\n  setup() {},\n\n  render() {\n    return h(\"div\", {}, [\n      h(\"div\", {}, \"你好\"),\n      h(\n        Child,\n        {\n          msg: \"your name is child\",\n        },\n        {\n          default: ({ age }) => [\n            h(\"p\", {}, \"我是通过 slot 渲染出来的第一个元素 \"),\n            h(\"p\", {}, \"我是通过 slot 渲染出来的第二个元素\"),\n            h(\"p\", {}, `我可以接收到 age: ${age}`),\n          ],\n        }\n      ),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/componentSlots/Child.js",
    "content": "import { h, ref, reactive, renderSlot } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  setup(props, context) {},\n  render() {\n    return h(\"div\", {\"data-test\":\"child\"}, [\n      h(\"div\", {}, \"child\"),\n      // renderSlot 会返回一个 vnode\n      // 其本质和 h 是一样的\n      // 第三个参数给出数据\n      renderSlot(this.$slots, \"default\", {\n        age: 16,\n      }),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/componentSlots/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/componentUpdate/App.js",
    "content": "// 在 render 中使用 proxy 调用 emit 函数\n// 也可以直接使用 this\n// 验证 proxy 的实现逻辑\nimport { h, ref } from \"../../dist/mini-vue.esm-bundler.js\";\nimport Child from \"./Child.js\";\n\nexport default {\n  name: \"App\",\n  setup() {\n    const msg = ref(\"123\");\n    window.msg = msg\n\n    const changeChildProps = () => {\n      msg.value = \"456\";\n    };\n\n    return { msg, changeChildProps };\n  },\n\n  render() {\n    return h(\"div\", {}, [\n      h(\"div\", {}, \"你好\"),\n      h(\n        \"button\",\n        {\n          onClick: this.changeChildProps,\n        },\n        \"change child props\"\n      ),\n      h(Child, {\n        msg: this.msg,\n      }),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/componentUpdate/Child.js",
    "content": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  setup(props, { emit }) {},\n  render(proxy) {\n    return h(\"div\", {}, [h(\"div\", {}, \"child\" + this.$props.msg)]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/componentUpdate/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/createTextVnode/App.js",
    "content": "import { h, ref, reactive, createTextVNode } from \"../../dist/mini-vue.esm-bundler.js\";\n\nexport default {\n  name: \"App\",\n  setup() {},\n\n  render() {\n    return h(\"div\", {}, [\n      h(\"div\", {}, \"你好\"),\n      createTextVNode(\"这是通过 createTextVNode 创建的节点\"),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/createTextVnode/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/customRenderer/App.js",
    "content": "import { h, ref } from \"../../dist/mini-vue.esm-bundler.js\";\nimport { game } from \"./game.js\";\n\nexport default {\n  name: \"App\",\n  setup() {\n    // 通过 ticker 来去更新 x 的值\n\n    const x = ref(0);\n    const y = ref(0);\n    let dir = 1;\n    const speed = 2;\n\n    game.ticker.add(() => {\n      if (x.value > 400) {\n        dir = -1;\n      } else if (x.value < 0) {\n        dir = 1;\n      }\n\n      x.value += speed * dir;\n    });\n\n    return {\n      x,\n      y,\n    };\n  },\n\n  render() {\n    return h(\"rect\", { x: this.x, y: this.y });\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/customRenderer/game.js",
    "content": "export const game = new PIXI.Application({\n  width: 500,\n  height: 500,\n});\n\ndocument.body.append(game.view);\n\nexport function createRootContainer() {\n  return game.stage;\n}\n"
  },
  {
    "path": "packages/vue/example/customRenderer/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n    <!-- <script src=\"../../node_modules/pixi.js/dist/browser/pixi.js\"></script> -->\n    <script src=\"https://pixijs.download/release/pixi.js\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n\n    <script src=\"main.js\" type=\"module\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/customRenderer/main.js",
    "content": "import App from \"./App.js\";\nimport { createApp } from \"./renderer.js\";\nimport { createRootContainer } from \"./game.js\";\n\ncreateApp(App).mount(createRootContainer());\n"
  },
  {
    "path": "packages/vue/example/customRenderer/renderer.js",
    "content": "import { createRenderer } from \"../../dist/mini-vue.esm-bundler.js\";\n\n// 给基于 pixi.js 的渲染函数\nconst renderer = createRenderer({\n  createElement(type) {\n    const rect = new PIXI.Graphics();\n    rect.beginFill(0xff0000);\n    rect.drawRect(0, 0, 100, 100);\n    rect.endFill();\n\n    return rect;\n  },\n\n  patchProp(el, key, prevValue, nextValue) {\n    el[key] = nextValue;\n  },\n\n  insert(el, parent) {\n    parent.addChild(el);\n  },\n});\n\nexport function createApp(rootComponent) {\n  return renderer.createApp(rootComponent);\n}\n"
  },
  {
    "path": "packages/vue/example/getCurrentInstance/App.js",
    "content": "// 可以在 setup 中使用 getCurrentInstance 获取组件实例对象\nimport { h, getCurrentInstance } from \"../../dist/mini-vue.esm-bundler.js\";\n\nexport default {\n  name: \"App\",\n  setup() {\n\n    console.log(getCurrentInstance())\n\n\n\n    return () => h(\"div\", {}, [h(\"p\", {}, \"getCurrentInstance\")]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/getCurrentInstance/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/helloWorld/App.js",
    "content": "import { h, ref } from \"../../dist/mini-vue.esm-bundler.js\";\n\nconst count = ref(0);\n\nconst HelloWorld = {\n  name: \"HelloWorld\",\n  setup() {},\n  // TODO 第一个小目标\n  // 可以在使用 template 只需要有一个插值表达式即\n  // 可以解析 tag 标签\n  // template: `\n  //   <div>hi {{msg}}</div>\n  //   需要编译成 render 函数\n  // `,\n  render() {\n    return h(\n      \"div\",\n      { tId: \"helloWorld\" },\n      `hello world: count: ${count.value}`\n    );\n  },\n};\n\nexport default {\n  name: \"App\",\n  setup() {},\n\n  render() {\n    return h(\"div\", { tId: 1 }, [h(\"p\", {}, \"主页\"), h(HelloWorld)]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/helloWorld/index.html",
    "content": "\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Document</title>\n</head>\n<body>\n    \n    <div id=\"root\"></div>\n    \n    <script src=\"main.js\" type=\"module\"></script>\n</body>\n</html>"
  },
  {
    "path": "packages/vue/example/helloWorld/main.js",
    "content": "import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\nimport App from \"./App.js\";\n\nconst rootContainer = document.querySelector(\"#root\");\ncreateApp(App).mount(rootContainer);\n"
  },
  {
    "path": "packages/vue/example/nextTicker/App.js",
    "content": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nimport NextTicker from \"./NextTicker.js\";\n\nexport default {\n  name: \"App\",\n  setup() {},\n\n  render() {\n    return h(\"div\", { tId: 1 }, [h(\"p\", {}, \"主页\"), h(NextTicker)]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/nextTicker/NextTicker.js",
    "content": "// 测试 nextTick 逻辑\nimport { h, ref } from \"../../dist/mini-vue.esm-bundler.js\";\n\n// 如果 for 循环改变 count 的值 100 次的话\n// 会同时触发 100 次的 update 页面逻辑\n// 这里可以把 update 页面的逻辑放到微任务中执行\n// 避免更改了响应式对象就会执行 update 的逻辑\n// 因为只有最后一次调用 update 才是有价值的\nwindow.count = ref(1);\n\n// 如果一个响应式变量同时触发了两个组件的 update\n// 会发生什么有趣的事呢？\nconst Child1 = {\n  name: \"NextTickerChild1\",\n  setup() {},\n  render() {\n    return h(\"div\", {}, `child1 count:${window.count.value}`);\n  },\n};\n\nconst Child2 = {\n  name: \"NextTickerChild2\",\n  setup() {},\n  render() {\n    return h(\"div\", {}, `child2 count:${window.count.value}`);\n  },\n};\n\nexport default {\n  name: \"NextTicker\",\n  setup() {},\n  render() {\n    return h(\n      \"div\",\n      { tId: \"nextTicker\" },\n      [h(Child1), h(Child2)]\n      //   `for nextTick: count: ${window.count.value}`\n    );\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/nextTicker/index.html",
    "content": "\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Document</title>\n</head>\n<body>\n    \n    <div id=\"root\"></div>\n    \n    <script src=\"main.js\" type=\"module\"></script>\n</body>\n</html>"
  },
  {
    "path": "packages/vue/example/nextTicker/main.js",
    "content": "import {createApp} from \"../../dist/mini-vue.esm-bundler.js\";\nimport App from \"./App.js\";\n\nconst rootContainer = document.querySelector(\"#root\");\ncreateApp(App).mount(rootContainer);\n"
  },
  {
    "path": "packages/vue/example/patchChildren/App.js",
    "content": "import {h} from '../../dist/mini-vue.esm-bundler.js'\nimport ArrayToText from \"./ArrayToText.js\";\nimport TextToText from \"./TextToText.js\";\nimport TextToArray from \"./TextToArray.js\";\nimport ArrayToArray from \"./ArrayToArray.js\";\n\nexport default {\n  name: \"App\",\n  setup() {},\n\n  render() {\n    return h(\"div\", { tId: 1 }, [\n      h(\"p\", {}, \"主页\"),\n      // 老的是 array 新的是 text\n      // h(ArrayToText),\n      // 老的是 text 新的是 text\n      // h(TextToText),\n      // 老的是 text 新的是 array\n      h(TextToArray)\n      // 老的是 array 新的是 array\n      // h(ArrayToArray),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/patchChildren/ArrayToArray.js",
    "content": "// 老的是 array\n// 新的是 array\n\nimport { ref, h } from \"../../dist/mini-vue.esm-bundler.js\";\n\n// 1. 左侧的对比\n// (a b) c\n// (a b) d e\n// const prevChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n// ];\n// const nextChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"D\" }, \"D\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n// ];\n\n// 2. 右侧的对比\n// a (b c)\n// d e (b c)\n// const prevChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n// ];\n// const nextChildren = [\n//   h(\"p\", { key: \"D\" }, \"D\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n// ];\n\n// 3. 新的比老的长\n//     创建新的\n// 左侧\n// (a b)\n// (a b) c\n// i = 2, e1 = 1, e2 = 2\n// const prevChildren = [h(\"p\", { key: \"A\" }, \"A\"), h(\"p\", { key: \"B\" }, \"B\")];\n// const nextChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n//   h(\"p\", { key: \"D\" }, \"D\"),\n// ];\n\n// 右侧\n// (a b)\n// c (a b)\n// i = 0, e1 = -1, e2 = 0\n// const prevChildren = [h(\"p\", { key: \"A\" }, \"A\"), h(\"p\", { key: \"B\" }, \"B\")];\n// const nextChildren = [\n//   h(\"p\", { key: \"C\" }, \"C\"),\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n// ];\n\n// 4. 老的比新的长\n//     删除老的\n// 左侧\n// (a b) c\n// (a b)\n// i = 2, e1 = 2, e2 = 1\n// const prevChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n// ];\n// const nextChildren = [h(\"p\", { key: \"A\" }, \"A\"), h(\"p\", { key: \"B\" }, \"B\")];\n\n// 右侧\n// a (b c)\n// (b c)\n// i = 0, e1 = 0, e2 = -1\n\n// const prevChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n// ];\n// const nextChildren = [h(\"p\", { key: \"B\" }, \"B\"), h(\"p\", { key: \"C\" }, \"C\")];\n\n// 5. 对比中间的部分\n// 删除老的  (在老的里面存在，新的里面不存在)\n// 5.1\n// a,b,(c,d),f,g\n// a,b,(e,c),f,g\n// D 节点在新的里面是没有的 - 需要删除掉\n// C 节点 props 也发生了变化\n\n// const prevChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\", id: \"c-prev\" }, \"C\"),\n//   h(\"p\", { key: \"D\" }, \"D\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// const nextChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"C\", id:\"c-next\" }, \"C\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// 5.1.1\n// a,b,(c,e,d),f,g\n// a,b,(e,c),f,g\n// 中间部分，老的比新的多， 那么多出来的直接就可以被干掉(优化删除逻辑)\n// const prevChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\", id: \"c-prev\" }, \"C\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"D\" }, \"D\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// const nextChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"C\", id:\"c-next\" }, \"C\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// 2 移动 (节点存在于新的和老的里面，但是位置变了)\n\n// 2.1\n// a,b,(c,d,e),f,g\n// a,b,(e,c,d),f,g\n// 最长子序列： [1,2]\n\n// const prevChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n//   h(\"p\", { key: \"D\" }, \"D\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// const nextChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n//   h(\"p\", { key: \"D\" }, \"D\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// 3. 创建新的节点\n// a,b,(c,e),f,g\n// a,b,(e,c,d),f,g\n// d 节点在老的节点中不存在，新的里面存在，所以需要创建\n// const prevChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// const nextChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n//   h(\"p\", { key: \"D\" }, \"D\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// 综合例子\n// a,b,(c,d,e,z),f,g\n// a,b,(d,c,y,e),f,g\n\n// const prevChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n//   h(\"p\", { key: \"D\" }, \"D\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"Z\" }, \"Z\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// const nextChildren = [\n//   h(\"p\", { key: \"A\" }, \"A\"),\n//   h(\"p\", { key: \"B\" }, \"B\"),\n//   h(\"p\", { key: \"D\" }, \"D\"),\n//   h(\"p\", { key: \"C\" }, \"C\"),\n//   h(\"p\", { key: \"Y\" }, \"Y\"),\n//   h(\"p\", { key: \"E\" }, \"E\"),\n//   h(\"p\", { key: \"F\" }, \"F\"),\n//   h(\"p\", { key: \"G\" }, \"G\"),\n// ];\n\n// fix c 节点应该是 move 而不是删除之后重新创建的\nconst prevChildren = [\n  h(\"p\", { key: \"A\" }, \"A\"),\n  h(\"p\", {}, \"C\"),\n  h(\"p\", { key: \"B\" }, \"B\"),\n  h(\"p\", { key: \"D\" }, \"D\"),\n];\n\nconst nextChildren = [\n  h(\"p\", { key: \"A\" }, \"A\"),\n  h(\"p\", { key: \"B\" }, \"B\"),\n  h(\"p\", {}, \"C\"),\n  h(\"p\", { key: \"D\" }, \"D\"),\n];\n\nexport default {\n  name: \"ArrayToArray\",\n  setup() {\n    const isChange = ref(false);\n    window.isChange = isChange;\n\n    return {\n      isChange,\n    };\n  },\n  render() {\n    const self = this;\n\n    return self.isChange === true\n      ? h(\"div\", {}, nextChildren)\n      : h(\"div\", {}, prevChildren);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/patchChildren/ArrayToText.js",
    "content": "// 老的是 array\n// 新的是 text\n\nimport { ref, h } from \"../../dist/mini-vue.esm-bundler.js\";\nconst nextChildren = \"newChildren\";\nconst prevChildren = [h(\"div\", {}, \"A\"), h(\"div\", {}, \"B\")];\n\nexport default {\n  name: \"ArrayToText\",\n  setup() {\n    const isChange = ref(false);\n    window.isChange = isChange;\n\n    return {\n      isChange,\n    };\n  },\n  render() {\n    const self = this;\n\n    return self.isChange === true\n      ? h(\"div\", {}, nextChildren)\n      : h(\"div\", {}, prevChildren);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/patchChildren/TextToArray.js",
    "content": "// 新的是 array\n// 老的是 text\nimport { ref, h } from \"../../dist/mini-vue.esm-bundler.js\";\n\nconst prevChildren = \"oldChild\";\nconst nextChildren = [h(\"div\", {}, \"A\"), h(\"div\", {}, \"B\")];\n\nexport default {\n  name: \"TextToArray\",\n  setup() {\n    const isChange = ref(false);\n    window.isChange = isChange;\n\n    return {\n      isChange,\n    };\n  },\n  render() {\n    const self = this;\n    console.log(\"?????????\")\n\n    return self.isChange === true\n      ? h(\"div\", {}, nextChildren)\n      : h(\"div\", {}, prevChildren);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/patchChildren/TextToText.js",
    "content": "// 新的是 text\n// 老的是 text\nimport { ref, h } from \"../../dist/mini-vue.esm-bundler.js\";\n\nconst prevChildren = \"oldChild\";\nconst nextChildren = \"newChild\";\n\nexport default {\n  name: \"TextToText\",\n  setup() {\n    const isChange = ref(false);\n    window.isChange = isChange;\n\n    return {\n      isChange,\n    };\n  },\n  render() {\n    const self = this;\n\n    return self.isChange === true\n      ? h(\"div\", {}, nextChildren)\n      : h(\"div\", {}, prevChildren);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/patchChildren/index.html",
    "content": "\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Document</title>\n</head>\n<body>\n    \n    <div id=\"root\"></div>\n    <script src=\"main.js\" type=\"module\"></script>\n</body>\n</html>"
  },
  {
    "path": "packages/vue/example/patchChildren/main.js",
    "content": "import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\nimport App from \"./App.js\";\n\nconst rootContainer = document.querySelector(\"#root\");\ncreateApp(App).mount(rootContainer);\n"
  },
  {
    "path": "packages/vue/example/renderComponent/App.js",
    "content": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nimport Child from \"./Child.js\";\n\nexport default {\n  name: \"App\",\n  setup() {},\n\n  render() {\n    return h(\"div\", {}, [\n      h(\"div\", {}, \"你好\"),\n      h(Child, {\n        msg: \"your name is child\",\n      }),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/renderComponent/Child.js",
    "content": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  setup(props, context) {\n    console.log(\"props------------------>\", props);\n    console.log(\"context---------------->\", context);\n  },\n  render() {\n    return h(\"div\", {}, \"child\");\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/renderComponent/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/example/setupStateRenderComponent/App.js",
    "content": "// 在 render 中可以通过 this.xxx 访问到 setup 返回的对象\nimport { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\n\nexport default {\n  name: \"App\",\n  setup() {\n    const count = ref(0);\n    const handleClick = () => {\n      console.log(\"click\");\n      count.value++;\n    };\n\n    return {\n      count,\n      handleClick,\n    };\n  },\n\n  render() {\n    console.log(this.count);\n    return h(\"div\", {}, [\n      h(\"div\", {}, String(this.count)),\n      h(\"button\", { onClick: this.handleClick }, \"click\"),\n    ]);\n  },\n};\n"
  },
  {
    "path": "packages/vue/example/setupStateRenderComponent/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\">\n      import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\n      import App from \"./App.js\";\n\n      const rootContainer = document.querySelector(\"#app\");\n      createApp(App).mount(rootContainer);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/vue/index.js",
    "content": "'use strict'\n\nif (process.env.NODE_ENV === 'production') {\n  // module.exports = require('./dist/mini-vue.cjs.prod.js')\n} else {\n  module.exports = require('./dist/mini-vue.cjs.js')\n}\n"
  },
  {
    "path": "packages/vue/package.json",
    "content": "{\n  \"name\": \"mini-vue\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"cypress open\",\n    \"serve\": \"serve .\"\n  },\n  \"keywords\": [],\n  \"author\": \"cuixiaorui\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@mini-vue/compiler-core\": \"workspace:^1.0.0\",\n    \"@mini-vue/runtime-dom\": \"workspace:^1.0.0\",\n    \"@mini-vue/shared\": \"workspace:^1.0.0\"\n  },\n  \"devDependencies\": {\n    \"cypress\": \"^11.0.1\",\n    \"serve\": \"^14.1.1\"\n  }\n}\n"
  },
  {
    "path": "packages/vue/src/index.ts",
    "content": "// 这个文件充当 vue 模块\nimport * as runtimeDom from \"@mini-vue/runtime-dom\";\nimport { registerRuntimeCompiler } from \"@mini-vue/runtime-dom\";\n\nimport { baseCompile } from \"@mini-vue/compiler-core\";\n\nexport * from \"@mini-vue/runtime-dom\";\n\n\nfunction compileToFunction(template, options = {}) {\n  const { code } = baseCompile(template, options);\n\n  // 调用 compile 得到的代码在给封装到函数内，\n  // 这里会依赖 runtimeDom 的一些函数，所以在这里通过参数的形式注入进去\n  const render = new Function(\"Vue\", code)(runtimeDom);\n\n  return render;\n}\n\nregisterRuntimeCompiler(compileToFunction);"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - 'packages/*'\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import typescript from \"@rollup/plugin-typescript\";\nimport sourceMaps from \"rollup-plugin-sourcemaps\";\nimport resolve from \"@rollup/plugin-node-resolve\";\nimport commonjs from \"@rollup/plugin-commonjs\";\nimport replace from \"@rollup/plugin-replace\";\n\nexport default {\n  input:\"./packages/vue/src/index.ts\",\n  plugins: [\n    replace({\n      \"process.env.NODE_ENV\": JSON.stringify(\"development\"),\n      \"process.env.VUE_ENV\": JSON.stringify(\"browser\"),\n      \"process.env.LANGUAGE\": JSON.stringify(process.env.LANGUAGE),\n    }),\n    resolve(),\n    commonjs(),\n    typescript(),\n    sourceMaps(),\n  ],\n  output: [\n    {\n      format: \"cjs\",\n      file: \"./packages/vue/dist/mini-vue.cjs.js\",\n      sourcemap: true,\n    },\n    {\n      name: \"vue\",\n      format: \"es\",\n      file: \"./packages/vue/dist/mini-vue.esm-bundler.js\",\n      sourcemap: true,\n    },\n  ],\n  onwarn: (msg, warn) => {\n    // 忽略 Circular 的错误\n    if (!/Circular/.test(msg)) {\n      warn(msg);\n    }\n  },\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"strict\": true,\n    \"rootDir\": \".\",\n    \"moduleResolution\": \"node\",\n    \"esModuleInterop\": true,\n    \"target\": \"es2016\",\n    \"module\": \"esnext\",\n    \"noImplicitAny\": false,\n    \"removeComments\": true,\n    \"preserveConstEnums\": true,\n    \"sourceMap\": true,\n    \"downlevelIteration\": true,\n    \"lib\": [\"esnext\", \"DOM\"],\n    \"types\": [\"vitest/globals\"],\n    \"paths\": {\n      \"@mini-vue/*\": [\"packages/*/src\"]\n    }\n  },\n  \"include\": [\"packages/*/src\", \"packages/*/__tests__\"]\n}\n"
  },
  {
    "path": "vitest.config.ts",
    "content": "import { defineConfig } from \"vitest/config\";\nimport path from \"path\";\n\nexport default defineConfig({\n  test: {\n    globals: true,\n  },\n  resolve: {\n    alias: [\n      {\n        find: /@mini-vue\\/([\\w-]*)/,\n        replacement: path.resolve(__dirname, \"packages\") + \"/$1/src\",\n      },\n    ],\n  },\n});\n"
  }
]