Full Code of cuixiaorui/mini-vue for AI

master 07e4e10a5ccd cached
135 files
221.7 KB
66.6k tokens
470 symbols
1 requests
Download .txt
Showing preview only (252K chars total). Download the full file or copy to clipboard to get everything.
Repository: cuixiaorui/mini-vue
Branch: master
Commit: 07e4e10a5ccd
Files: 135
Total size: 221.7 KB

Directory structure:
gitextract_4amh303j/

├── .gitignore
├── .vscode/
│   ├── launch.json
│   └── settings.json
├── LICENSE
├── README.md
├── README_EN.md
├── package.json
├── packages/
│   ├── compiler-core/
│   │   ├── __tests__/
│   │   │   ├── __snapshots__/
│   │   │   │   └── codegen.spec.ts.snap
│   │   │   ├── codegen.spec.ts
│   │   │   ├── parse.spec.ts
│   │   │   └── transform.spec.ts
│   │   ├── package.json
│   │   └── src/
│   │       ├── ast.ts
│   │       ├── codegen.ts
│   │       ├── compile.ts
│   │       ├── index.ts
│   │       ├── parse.ts
│   │       ├── runtimeHelpers.ts
│   │       ├── transform.ts
│   │       ├── transforms/
│   │       │   ├── transformElement.ts
│   │       │   ├── transformExpression.ts
│   │       │   └── transformText.ts
│   │       └── utils.ts
│   ├── reactivity/
│   │   ├── __tests__/
│   │   │   ├── computed.spec.ts
│   │   │   ├── dep.spec.ts
│   │   │   ├── effect.spec.ts
│   │   │   ├── reactive.spec.ts
│   │   │   ├── readonly.spec.ts
│   │   │   ├── ref.spec.ts
│   │   │   └── shallowReadonly.spec.ts
│   │   ├── package.json
│   │   └── src/
│   │       ├── baseHandlers.ts
│   │       ├── computed.ts
│   │       ├── dep.ts
│   │       ├── effect.ts
│   │       ├── index.ts
│   │       ├── reactive.ts
│   │       └── ref.ts
│   ├── runtime-core/
│   │   ├── __tests__/
│   │   │   ├── apiWatch.spec.ts
│   │   │   ├── componentEmits.spec.ts
│   │   │   ├── rendererComponent.spec.ts
│   │   │   └── rendererElement.spec.ts
│   │   ├── package.json
│   │   └── src/
│   │       ├── .pnpm-debug.log
│   │       ├── apiInject.ts
│   │       ├── apiWatch.ts
│   │       ├── component.ts
│   │       ├── componentEmits.ts
│   │       ├── componentProps.ts
│   │       ├── componentPublicInstance.ts
│   │       ├── componentRenderUtils.ts
│   │       ├── componentSlots.ts
│   │       ├── createApp.ts
│   │       ├── h.ts
│   │       ├── helpers/
│   │       │   └── renderSlot.ts
│   │       ├── index.ts
│   │       ├── renderer.ts
│   │       ├── scheduler.ts
│   │       └── vnode.ts
│   ├── runtime-dom/
│   │   ├── package.json
│   │   └── src/
│   │       └── index.ts
│   ├── runtime-test/
│   │   └── src/
│   │       ├── index.ts
│   │       ├── nodeOps.ts
│   │       ├── patchProp.ts
│   │       └── serialize.ts
│   ├── shared/
│   │   ├── package.json
│   │   └── src/
│   │       ├── index.ts
│   │       ├── shapeFlags.ts
│   │       └── toDisplayString.ts
│   └── vue/
│       ├── cypress/
│       │   ├── e2e/
│       │   │   ├── apiInject.cy.js
│       │   │   ├── componentEmit.cy.js
│       │   │   ├── componentSlots.cy.js
│       │   │   ├── componentUpdate.cy.js
│       │   │   ├── customRenderer.cy.js
│       │   │   ├── getCurrentInstance.cy.js
│       │   │   ├── helloworld.cy.js
│       │   │   ├── nextTicker.cy.js
│       │   │   └── patchChildren.cy.js
│       │   ├── fixtures/
│       │   │   └── example.json
│       │   └── support/
│       │       ├── commands.js
│       │       └── e2e.js
│       ├── cypress.config.js
│       ├── dist/
│       │   ├── mini-vue.cjs.js
│       │   └── mini-vue.esm-bundler.js
│       ├── example/
│       │   ├── apiInject/
│       │   │   ├── App.js
│       │   │   └── index.html
│       │   ├── compiler-base/
│       │   │   ├── App.js
│       │   │   └── index.html
│       │   ├── componentEmit/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   ├── componentProxy/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   ├── componentSlots/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   ├── componentUpdate/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   ├── createTextVnode/
│       │   │   ├── App.js
│       │   │   └── index.html
│       │   ├── customRenderer/
│       │   │   ├── App.js
│       │   │   ├── game.js
│       │   │   ├── index.html
│       │   │   ├── main.js
│       │   │   └── renderer.js
│       │   ├── getCurrentInstance/
│       │   │   ├── App.js
│       │   │   └── index.html
│       │   ├── helloWorld/
│       │   │   ├── App.js
│       │   │   ├── index.html
│       │   │   └── main.js
│       │   ├── nextTicker/
│       │   │   ├── App.js
│       │   │   ├── NextTicker.js
│       │   │   ├── index.html
│       │   │   └── main.js
│       │   ├── patchChildren/
│       │   │   ├── App.js
│       │   │   ├── ArrayToArray.js
│       │   │   ├── ArrayToText.js
│       │   │   ├── TextToArray.js
│       │   │   ├── TextToText.js
│       │   │   ├── index.html
│       │   │   └── main.js
│       │   ├── renderComponent/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   └── setupStateRenderComponent/
│       │       ├── App.js
│       │       └── index.html
│       ├── index.js
│       ├── package.json
│       └── src/
│           └── index.ts
├── pnpm-workspace.yaml
├── rollup.config.js
├── tsconfig.json
└── vitest.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules

================================================
FILE: .vscode/launch.json
================================================
{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "name": "vscode-jest-tests",
      "request": "launch",
      "program": "${workspaceFolder}/node_modules/jest/bin/jest",
      "args": ["--runInBand", "--watchAll=false"],
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen",
      "disableOptimisticBPs": true,
      "windows": {
        "program": "${workspaceFolder}/node_modules/jest/bin/jest"
      }
    }
  ]
}


================================================
FILE: .vscode/settings.json
================================================
{
    "liveServer.settings.port": 5501
}

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 zenoslin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, libribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
[CN](README.md) / [EN](README_EN.md)

## 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)

实现最简 vue3 模型,用于深入学习 vue3, 让你更轻松的理解 vue3 的核心逻辑

## Usage

[B 站](https://www.bilibili.com/video/BV1Zy4y1J73E) 提供了视频讲解使用方式

## Why

当我们需要深入学习 vue3 时,我们就需要看源码来学习,但是像这种工业级别的库,源码中有很多逻辑是用于处理边缘情况或者是兼容处理逻辑,是不利于我们学习的。

我们应该关注于核心逻辑,而这个库的目的就是把 vue3 源码中最核心的逻辑剥离出来,只留下核心逻辑,以供大家学习。

## How

基于 vue3 的功能点,一点一点的拆分出来。

代码命名会保持和源码中的一致,方便大家通过命名去源码中查找逻辑。

### Tasking

#### runtime-core

- [x] 支持组件类型
- [x] 支持 element 类型
- [x] 初始化 props
- [x] setup 可获取 props 和 context
- [x] 支持 component emit
- [x] 支持 proxy
- [x] 可以在 render 函数中获取 setup 返回的对象
- [x] nextTick 的实现
- [x] 支持 getCurrentInstance
- [x] 支持 provide/inject
- [x] 支持最基础的 slots
- [x] 支持 Text 类型节点
- [x] 支持 $el api
- [x] 支持 watchEffect


#### reactivity

目标是用自己的 reactivity 支持现有的 demo 运行

- [x] reactive 的实现
- [x] ref 的实现
- [x] readonly 的实现
- [x] computed 的实现
- [x] track 依赖收集
- [x] trigger 触发依赖
- [x] 支持 isReactive
- [x] 支持嵌套 reactive
- [x] 支持 toRaw
- [x] 支持 effect.scheduler
- [x] 支持 effect.stop
- [x] 支持 isReadonly
- [x] 支持 isProxy
- [x] 支持 shallowReadonly
- [x] 支持 proxyRefs

### compiler-core
- [x] 解析插值
- [x] 解析 element
- [x] 解析 text

### runtime-dom
- [x] 支持 custom renderer 

### runtime-test
- [x] 支持测试 runtime-core 的逻辑

### infrastructure
- [x] support monorepo with pnpm
### build

```shell
pnpm build
```

### example

通过 server 的方式打开 packages/vue/example/\* 下的 index.html 即可

>  推荐使用 [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)

### 初始化

#### 流程图
![初始化流程图](https://user-images.githubusercontent.com/12064746/138114565-3e0eecbb-7fd0-4203-bf36-5e5fd8003ce0.png)

> 可加 vx:cuixr1314  获取所有脑图(备注:github mini-vue 领取脑图)
#### 关键函数调用图


![关键函数调用图2](https://images-1252602850.cos.ap-beijing.myqcloud.com/20220927170658.png)

> 可以基于函数名快速搜索到源码内容

### update

#### 流程图

![image](https://user-images.githubusercontent.com/12064746/138115157-1f4fb8a2-7e60-412d-96de-12e68eb0288c.png)

#### 关键函数调用图

![image](https://user-images.githubusercontent.com/12064746/138114969-9139e4af-b2df-41b2-a5d9-069d8b41903c.png)


> 可以基于函数名快速搜索到源码内容



### 从零到一实现一遍

自从有了 mini-vue 之后 很多同学都问我 能不能带着他从零到一敲一遍

因为对于源码的学习来讲 看在多遍也不如自己写一遍

为此我把 mini-vue 做成了一套视频课  从零到一带着大家实现一遍 不跳过任何一行代码

当然除了功能上的实现还有编程思想融入到了课程内 

比如 TDD、小步走、重构手法、TPP 

> TDD 测试驱动开发 影响了我整个技术生涯 可以说在我认识到 TDD 之后 技术才有了质的飞跃 

课程目录如下:

1. vue3 源码结构的介绍
2. reactivity 的核心流程
3. runtime-core 初始化的核心流程
4. runtime-core 更新的核心流程
5. setup 环境 -> 集成 jest 做单元测试 & 集成 typescript
6. 实现 effect 返回 runner
7. 实现 effect 的 scheduler 功能
8. 实现 effect 的 stop 功能
9. 实现 readonly 功能
10. 实现 isReactive 和 isReadonly 
11. 优化 stop 功能
12. 实现 reactive 和 readonly 嵌套对象转换功能
13. 实现 shallowReadonly 功能
14. 实现 isProxy 功能
15. 实现 isProxy 功能
16. 实现 ref 功能
17. 实现 isRef 和 unRef 功能
18. 实现 proxyR 功能
19. 实现 computed 计算属性功能
20. 实现初始化 component 主流程
21. 实现 rollup 打包
22. 实现初始化 element 主流程
23. 实现组件代理对象
24. 实现 shapeFlags
25. 实现注册事件功能
26. 实现组件 props 功能
27. 实现组件 emit 功能
28. 实现组件 slots 功能
29. 实现 Fragment 和 Text 类型节点
30. 实现 getCurrentInstance 
31. 实现依赖注入功能 provide/inject
32. 实现自定义渲染器 custom renderer
33. 更新 element 流程搭建
34. 更新 element 的props
35. 更新 element 的 children
36. 双端对比 diff 算法1
37. 双端对比 diff 算法2 - key 的作用
38. 双端对比 diff 算法3 - 最长子序列的作用
39. 学习尤大解决 bug 的处理方式
40. 实现组件更新功能
41. 实现 nextTick 功能
42. 编译模块概述
43. 实现解析插值功能
44. 实现解析 element 标签
45. 实现解析 text 功能
46. 实现解析三种联合类型 template
47. parse 的实现原理&有限状态机
48. 实现 transform 功能
49. 实现代码生成 string 类型
50. 实现代码生成插值类型
51. 实现代码生成三种联合类型
52. 实现编译 template 成 render 函数
53. 实现 monorepo & 使用 vitest 替换 jest 

课程内部包含了 vue3 的三大核心模块:reactivity、runtime 以及 compiler 模块

等你自己手写一遍之后 在去看 vue3 源码或者再去看分析解析 vue3 源码的书籍时你会有不同的体验

除此之外 还录制了课程介绍以及课程试听课
- [课程介绍](https://www.bilibili.com/video/BV16Z4y1r7Wp)
- [试听课](https://www.bilibili.com/video/BV1R341177P7)
- [购买链接](https://cua.h5.xeknow.com/s/xDWLc)

> 可以直接购买 也可以加我 wx: cuixr1314 来咨询这门课是否合适你

除了课程内容以外 还有专门的社群来答疑大家在学习上的问题 😊   


================================================
FILE: README_EN.md
================================================
[EN](README.md) / [CN](README.md)
## mini-vue

Implement the simplest vue3 model for in-depth study of vue3 source code

## Usage

[Bilibili](https://www.bilibili.com/video/BV1Zy4y1J73E) Provides a video explaining how to use it

> Can follow my [Bilibili](https://space.bilibili.com/175301983),Interpretation of live source code from time to time

## Discuss

You can join the group to discuss the vue3 source code


![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cbe1b6e9c67944828c3e653fd7919dc0~tplv-k3u1fbpfcp-watermark.image)

> with WeChat

## Service

Provide one-to-one video teaching services, and take you to see the mini-vue source code hand in hand

> Can add group communication

## Why

When 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.

We 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.

## How

Based on the function points of vue3, split it out bit by bit

The code naming will remain consistent with the source code, so that you can find logic in the source code through naming.

### Tasking

- [x] support component type
- [x] support element type
- [x] init props of component
- [x] context can get props and context in setup
- [x] support component emit
- [x] support proxy
- [x] can get the object returned by setup in the render function
- [x] Implementation of nextTick
- [x] support getCurrentInstance
- [x] support provide/inject
- [x] support basic slots
- [x] support text type 

### roadmap

- [ ] support english
- [ ] normalize console.log

### build

```shell
yarn build
```

### example

Open index.html under example/\* use server

>  Recommended Use [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)

### Initialization

#### flow chart

![初始化流程图](https://user-images.githubusercontent.com/12064746/138114565-3e0eecbb-7fd0-4203-bf36-5e5fd8003ce0.png)

#### Key function call graph

![关键函数调用图1](https://user-gold-cdn.xitu.io/2020/6/22/172dc07fc42b7d2c?w=1342&h=144&f=png&s=54200)

![关键函数调用图2](https://user-gold-cdn.xitu.io/2020/6/22/172dc08840e25b42?w=1816&h=934&f=png&s=550722)

> The source code content can be quickly searched based on the function name

### update

#### flow chart

![update流程图](https://user-images.githubusercontent.com/12064746/138115157-1f4fb8a2-7e60-412d-96de-12e68eb0288c.png)

#### Key function call graph

![update关键函数调用图](https://user-images.githubusercontent.com/12064746/138114969-9139e4af-b2df-41b2-a5d9-069d8b41903c.png)

> The source code content can be quickly searched based on the function name


================================================
FILE: package.json
================================================
{
  "private": true,
  "version": "0.0.1",
  "description": "Help you learn more efficiently vue3 source code",
  "main": "lib/mini-vue.cjs.js",
  "module": "lib/mini-vue.esm.js",
  "scripts": {
    "dev": "rollup -c -w",
    "build": "rollup -c",
    "test": "vitest"
  },
  "author": "cuixiaorui",
  "homepage": "https://github.com/cuixiaorui",
  "license": "ISC",
  "devDependencies": {
    "@rollup/plugin-commonjs": "^13.0.0",
    "@rollup/plugin-node-resolve": "^8.1.0",
    "@rollup/plugin-replace": "^2.3.3",
    "@rollup/plugin-typescript": "^8.2.5",
    "rollup": "^2.17.1",
    "rollup-plugin-sourcemaps": "^0.6.2",
    "tslib": "^2.3.1",
    "typescript": "^4.4.3",
    "vitest": "^0.22.1"
  },
  "dependencies": {
    "@vue/reactivity": "^3.0.5",
    "pixi.js": "^6.2.0"
  }
}


================================================
FILE: packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
================================================
// Vitest Snapshot v1

exports[`element and interpolation 1`] = `
"
        const { toDisplayString : _toDisplayString, createElementVNode : _createElementVNode} = Vue 

      
return function render(_ctx) {return _createElementVNode('div', null, 'hi,' + _toDisplayString(_ctx.msg))}"
`;

exports[`interpolation module 1`] = `
"
        const { toDisplayString : _toDisplayString} = Vue 

      
return function render(_ctx) {return _toDisplayString(_ctx.hello)}"
`;


================================================
FILE: packages/compiler-core/__tests__/codegen.spec.ts
================================================
import { generate } from "../src/codegen";
import { baseParse } from "../src/parse";
import { transform } from "../src/transform";
import { transformElement } from "../src/transforms/transformElement";
import { transformExpression } from "../src/transforms/transformExpression";
import { transformText } from "../src/transforms/transformText";

test("interpolation module", () => {
  const ast = baseParse("{{hello}}");
  transform(ast, {
    nodeTransforms: [transformExpression],
  });

  const { code } = generate(ast);
  expect(code).toMatchSnapshot();
});

test("element and interpolation", () => {
  const ast = baseParse("<div>hi,{{msg}}</div>");
  transform(ast, {
    nodeTransforms: [transformElement, transformText, transformExpression],
  });

  const { code } = generate(ast);
  expect(code).toMatchSnapshot();
});


================================================
FILE: packages/compiler-core/__tests__/parse.spec.ts
================================================
import { ElementTypes, NodeTypes } from "../src/ast";
import { baseParse } from "../src/parse";

describe("parser", () => {
  describe("text", () => {
    test("simple text", () => {
      const ast = baseParse("some text");
      const text = ast.children[0];

      expect(text).toStrictEqual({
        type: NodeTypes.TEXT,
        content: "some text",
      });
    });

    test("simple text with invalid end tag", () => {
      const ast = baseParse("some text</div>");
      const text = ast.children[0];

      expect(text).toStrictEqual({
        type: NodeTypes.TEXT,
        content: "some text",
      });
    });

    test("text with interpolation", () => {
      const ast = baseParse("some {{ foo + bar }} text");
      const text1 = ast.children[0];
      const text2 = ast.children[2];

      // ast.children[1] 应该是 interpolation
      expect(text1).toStrictEqual({
        type: NodeTypes.TEXT,
        content: "some ",
      });
      expect(text2).toStrictEqual({
        type: NodeTypes.TEXT,
        content: " text",
      });
    });
  });

  describe("Interpolation", () => {
    test("simple interpolation", () => {
      // 1. 看看是不是一个 {{ 开头的
      // 2. 是的话,那么就作为 插值来处理
      // 3. 获取内部 message 的内容即可
      const ast = baseParse("{{message}}");
      const interpolation = ast.children[0];

      expect(interpolation).toStrictEqual({
        type: NodeTypes.INTERPOLATION,
        content: {
          type: NodeTypes.SIMPLE_EXPRESSION,
          content: `message`,
        },
      });
    });
  });

  describe("Element", () => {
    test("simple div", () => {
      const ast = baseParse("<div>hello</div>");
      const element = ast.children[0];

      expect(element).toStrictEqual({
        type: NodeTypes.ELEMENT,
        tag: "div",
        tagType: ElementTypes.ELEMENT,
        children: [
          {
            type: NodeTypes.TEXT,
            content: "hello",
          },
        ],
      });
    });

    test("element with interpolation", () => {
      const ast = baseParse("<div>{{ msg }}</div>");
      const element = ast.children[0];

      expect(element).toStrictEqual({
        type: NodeTypes.ELEMENT,
        tag: "div",
        tagType: ElementTypes.ELEMENT,
        children: [
          {
            type: NodeTypes.INTERPOLATION,
            content: {
              type: NodeTypes.SIMPLE_EXPRESSION,
              content: `msg`,
            },
          },
        ],
      });
    });

    test("element with interpolation and text", () => {
      const ast = baseParse("<div>hi,{{ msg }}</div>");
      const element = ast.children[0];

      expect(element).toStrictEqual({
        type: NodeTypes.ELEMENT,
        tag: "div",
        tagType: ElementTypes.ELEMENT,
        children: [
          {
            type: NodeTypes.TEXT,
            content: "hi,",
          },
          {
            type: NodeTypes.INTERPOLATION,
            content: {
              type: NodeTypes.SIMPLE_EXPRESSION,
              content: "msg",
            },
          },
        ],
      });
    });

    test("should throw error when lack end tag  ", () => {
      expect(() => {
        baseParse("<div><span></div>");
      }).toThrow("缺失结束标签:span");
    });
  });
});


================================================
FILE: packages/compiler-core/__tests__/transform.spec.ts
================================================
import { baseParse } from "../src/parse";
import { TO_DISPLAY_STRING } from "../src/runtimeHelpers";
import { transform } from "../src/transform";
describe("Compiler: transform", () => {
  test("context state", () => {
    const ast = baseParse(`<div>hello {{ world }}</div>`);
    console.log(ast);

    // manually store call arguments because context is mutable and shared
    // across calls
    const calls: any[] = [];
    const plugin = (node, context) => {
      calls.push([node, { ...context }]);
    };

    transform(ast, {
      nodeTransforms: [plugin],
    });

    const div = ast.children[0];
    expect(calls.length).toBe(4);
    expect(calls[0]).toMatchObject([
      ast,
      {},
      // TODO
      //       {
      //         parent: null,
      //         currentNode: ast,
      //       },
    ]);
    expect(calls[1]).toMatchObject([
      div,
      {},
      // TODO
      //   {
      //     parent: ast,
      //     currentNode: div,
      //   },
    ]);
    expect(calls[2]).toMatchObject([
      div.children[0],
      {},
      //       {
      //         parent: div,
      //         currentNode: div.children[0],
      //       },
    ]);
    expect(calls[3]).toMatchObject([
      div.children[1],
      {},
      //   {
      //     parent: div,
      //     currentNode: div.children[1],
      //   },
    ]);
  });

  test("should inject toString helper for interpolations", () => {
    const ast = baseParse(`{{ foo }}`);
    transform(ast, {});
    expect(ast.helpers).toContain(TO_DISPLAY_STRING);
  });
});


================================================
FILE: packages/compiler-core/package.json
================================================
{
  "name": "@mini-vue/compiler-core",
  "version": "1.0.0",
  "description": "@mini-vue/compiler-core",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@mini-vue/shared": "workspace:^1.0.0"
  }
}


================================================
FILE: packages/compiler-core/src/ast.ts
================================================
import { CREATE_ELEMENT_VNODE } from "./runtimeHelpers";

export const enum NodeTypes {
  TEXT,
  ROOT,
  INTERPOLATION,
  SIMPLE_EXPRESSION,
  ELEMENT,
  COMPOUND_EXPRESSION
}

export const enum ElementTypes {
  ELEMENT,
}

export function createSimpleExpression(content) {
  return {
    type: NodeTypes.SIMPLE_EXPRESSION,
    content,
  };
}

export function createInterpolation(content) {
  return {
    type: NodeTypes.INTERPOLATION,
    content: content,
  };
}

export function createVNodeCall(context, tag, props?, children?) {
  if (context) {
    context.helper(CREATE_ELEMENT_VNODE);
  }

  return {
    // TODO vue3 里面这里的 type 是 VNODE_CALL
    // 是为了 block 而 mini-vue 里面没有实现 block 
    // 所以创建的是 Element 类型就够用了
    type: NodeTypes.ELEMENT,
    tag,
    props,
    children,
  };
}


================================================
FILE: packages/compiler-core/src/codegen.ts
================================================
import { isString } from "@mini-vue/shared";
import { NodeTypes } from "./ast";
import {
  CREATE_ELEMENT_VNODE,
  helperNameMap,
  TO_DISPLAY_STRING,
} from "./runtimeHelpers";

export function generate(ast, options = {}) {
  // 先生成 context
  const context = createCodegenContext(ast, options);
  const { push, mode } = context;

  // 1. 先生成 preambleContext

  if (mode === "module") {
    genModulePreamble(ast, context);
  } else {
    genFunctionPreamble(ast, context);
  }

  const functionName = "render";

  const args = ["_ctx"];

  // _ctx,aaa,bbb,ccc
  // 需要把 args 处理成 上面的 string
  const signature = args.join(", ");
  push(`function ${functionName}(${signature}) {`);
  // 这里需要生成具体的代码内容
  // 开始生成 vnode tree 的表达式
  push("return ");
  genNode(ast.codegenNode, context);

  push("}");

  return {
    code: context.code,
  };
}

function genFunctionPreamble(ast: any, context: any) {
  const { runtimeGlobalName, push, newline } = context;
  const VueBinging = runtimeGlobalName;

  const aliasHelper = (s) => `${helperNameMap[s]} : _${helperNameMap[s]}`;

  if (ast.helpers.length > 0) {
    push(
      `
        const { ${ast.helpers.map(aliasHelper).join(", ")}} = ${VueBinging} 

      `
    );
  }

  newline();
  push(`return `);
}

function genNode(node: any, context: any) {
  // 生成代码的规则就是读取 node ,然后基于不同的 node 来生成对应的代码块
  // 然后就是把代码快给拼接到一起就可以了

  switch (node.type) {
    case NodeTypes.INTERPOLATION:
      genInterpolation(node, context);
      break;
    case NodeTypes.SIMPLE_EXPRESSION:
      genExpression(node, context);
      break;

    case NodeTypes.ELEMENT:
      genElement(node, context);
      break;

    case NodeTypes.COMPOUND_EXPRESSION:
      genCompoundExpression(node, context);
      break;

    case NodeTypes.TEXT:
      genText(node, context);
      break;

    default:
      break;
  }
}

function genCompoundExpression(node: any, context: any) {
  const { push } = context;
  for (let i = 0; i < node.children.length; i++) {
    const child = node.children[i];
    if (isString(child)) {
      push(child);
    } else {
      genNode(child, context);
    }
  }
}

function genText(node: any, context: any) {
  // Implement
  const { push } = context;

  push(`'${node.content}'`);
}

function genElement(node, context) {
  const { push, helper } = context;
  const { tag, props, children } = node;

  push(`${helper(CREATE_ELEMENT_VNODE)}(`);

  genNodeList(genNullableArgs([tag, props, children]), context);

  push(`)`);
}

function genNodeList(nodes: any, context: any) {
  const { push } = context;
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];

    if (isString(node)) {
      push(`${node}`);
    } else {
      genNode(node, context);
    }
    // node 和 node 之间需要加上 逗号(,)
    // 但是最后一个不需要 "div", [props], [children]
    if (i < nodes.length - 1) {
      push(", ");
    }
  }
}

function genNullableArgs(args) {
  // 把末尾为null 的都删除掉
  // vue3源码中,后面可能会包含 patchFlag、dynamicProps 等编译优化的信息
  // 而这些信息有可能是不存在的,所以在这边的时候需要删除掉
  let i = args.length;
  // 这里 i-- 用的还是特别的巧妙的
  // 当为0 的时候自然就退出循环了
  while (i--) {
    if (args[i] != null) break;
  }

  // 把为 falsy 的值都替换成 "null"
  return args.slice(0, i + 1).map((arg) => arg || "null");
}

function genExpression(node: any, context: any) {
  context.push(node.content, node);
}

function genInterpolation(node: any, context: any) {
  const { push, helper } = context;
  push(`${helper(TO_DISPLAY_STRING)}(`);
  genNode(node.content, context);
  push(")");
}

function genModulePreamble(ast, context) {
  // preamble 就是 import 语句
  const { push, newline, runtimeModuleName } = context;

  if (ast.helpers.length) {
    // 比如 ast.helpers 里面有个 [toDisplayString]
    // 那么生成之后就是 import { toDisplayString as _toDisplayString } from "vue"
    const code = `import {${ast.helpers
      .map((s) => `${helperNameMap[s]} as _${helperNameMap[s]}`)
      .join(", ")} } from ${JSON.stringify(runtimeModuleName)}`;

    push(code);
  }

  newline();
  push(`export `);
}

function createCodegenContext(
  ast: any,
  { runtimeModuleName = "vue", runtimeGlobalName = "Vue", mode = "function" }
): any {
  const context = {
    code: "",
    mode,
    runtimeModuleName,
    runtimeGlobalName,
    helper(key) {
      return `_${helperNameMap[key]}`;
    },
    push(code) {
      context.code += code;
    },
    newline() {
      // 换新行
      // TODO 需要额外处理缩进
      context.code += "\n";
    },
  };

  return context;
}


================================================
FILE: packages/compiler-core/src/compile.ts
================================================
import { generate } from "./codegen";
import { baseParse } from "./parse";
import { transform } from "./transform";
import { transformExpression } from "./transforms/transformExpression";
import { transformElement } from "./transforms/transformElement";
import { transformText } from "./transforms/transformText";

export function baseCompile(template, options) {
  // 1. 先把 template 也就是字符串 parse 成 ast
  const ast = baseParse(template);
  // 2. 给 ast 加点料(- -#)
  transform(
    ast,
    Object.assign(options, {
      nodeTransforms: [transformElement, transformText, transformExpression],
    })
  );

  // 3. 生成 render 函数代码
  return generate(ast);
}


================================================
FILE: packages/compiler-core/src/index.ts
================================================
export { baseCompile } from "./compile";


================================================
FILE: packages/compiler-core/src/parse.ts
================================================
import { ElementTypes, NodeTypes } from "./ast";

const enum TagType {
  Start,
  End,
}

export function baseParse(content: string) {
  const context = createParserContext(content);
  return createRoot(parseChildren(context, []));
}

function createParserContext(content) {
  console.log("创建 paserContext");
  return {
    source: content,
  };
}

function parseChildren(context, ancestors) {
  console.log("开始解析 children");
  const nodes: any = [];

  while (!isEnd(context, ancestors)) {
    let node;
    const s = context.source;

    if (startsWith(s, "{{")) {
      // 看看如果是 {{ 开头的话,那么就是一个插值, 那么去解析他
      node = parseInterpolation(context);
    } else if (s[0] === "<") {
      if (s[1] === "/") {
        // 这里属于 edge case 可以不用关心
        // 处理结束标签
        if (/[a-z]/i.test(s[2])) {
          // 匹配 </div>
          // 需要改变 context.source 的值 -> 也就是需要移动光标
          parseTag(context, TagType.End);
          // 结束标签就以为这都已经处理完了,所以就可以跳出本次循环了
          continue;
        }
      } else if (/[a-z]/i.test(s[1])) {
        node = parseElement(context, ancestors);
      }
    }

    if (!node) {
      node = parseText(context);
    }

    nodes.push(node);
  }

  return nodes;
}

function isEnd(context: any, ancestors) {
  // 检测标签的节点
  // 如果是结束标签的话,需要看看之前有没有开始标签,如果有的话,那么也应该结束
  // 这里的一个 edge case 是 <div><span></div>
  // 像这种情况下,其实就应该报错
  const s = context.source;
  if (context.source.startsWith("</")) {
    // 从后面往前面查
    // 因为便签如果存在的话 应该是 ancestors 最后一个元素
    for (let i = ancestors.length - 1; i >= 0; --i) {
      if (startsWithEndTagOpen(s, ancestors[i].tag)) {
        return true;
      }
    }
  }

  // 看看 context.source 还有没有值
  return !context.source;
}

function parseElement(context, ancestors) {
  // 应该如何解析 tag 呢
  // <div></div>
  // 先解析开始 tag
  const element = parseTag(context, TagType.Start);

  ancestors.push(element);
  const children = parseChildren(context, ancestors);
  ancestors.pop();

  // 解析 end tag 是为了检测语法是不是正确的
  // 检测是不是和 start tag 一致
  if (startsWithEndTagOpen(context.source, element.tag)) {
    parseTag(context, TagType.End);
  } else {
    throw new Error(`缺失结束标签:${element.tag}`);
  }

  element.children = children;

  return element;
}

function startsWithEndTagOpen(source: string, tag: string) {
  // 1. 头部 是不是以  </ 开头的
  // 2. 看看是不是和 tag 一样
  return (
    startsWith(source, "</") &&
    source.slice(2, 2 + tag.length).toLowerCase() === tag.toLowerCase()
  );
}

function parseTag(context: any, type: TagType): any {
  // 发现如果不是 > 的话,那么就把字符都收集起来 ->div
  // 正则
  const match: any = /^<\/?([a-z][^\r\n\t\f />]*)/i.exec(context.source);
  const tag = match[1];

  // 移动光标
  // <div
  advanceBy(context, match[0].length);

  // 暂时不处理 selfClose 标签的情况 ,所以可以直接 advanceBy 1个坐标 <  的下一个就是 >
  advanceBy(context, 1);

  if (type === TagType.End) return;

  let tagType = ElementTypes.ELEMENT;

  return {
    type: NodeTypes.ELEMENT,
    tag,
    tagType,
  };
}

function parseInterpolation(context: any) {
  // 1. 先获取到结束的index
  // 2. 通过 closeIndex - startIndex 获取到内容的长度 contextLength
  // 3. 通过 slice 截取内容

  // }} 是插值的关闭
  // 优化点是从 {{ 后面搜索即可
  const openDelimiter = "{{";
  const closeDelimiter = "}}";

  const closeIndex = context.source.indexOf(
    closeDelimiter,
    openDelimiter.length
  );

  // TODO closeIndex -1 需要报错的

  // 让代码前进2个长度,可以把 {{ 干掉
  advanceBy(context, 2);

  const rawContentLength = closeIndex - openDelimiter.length;
  const rawContent = context.source.slice(0, rawContentLength);

  const preTrimContent = parseTextData(context, rawContent.length);
  const content = preTrimContent.trim();

  // 最后在让代码前进2个长度,可以把 }} 干掉
  advanceBy(context, closeDelimiter.length);

  return {
    type: NodeTypes.INTERPOLATION,
    content: {
      type: NodeTypes.SIMPLE_EXPRESSION,
      content,
    },
  };
}

function parseText(context): any {
  console.log("解析 text", context);

  // endIndex 应该看看有没有对应的 <
  // 比如 hello</div>
  // 像这种情况下 endIndex 就应该是在 o 这里
  // {
  const endTokens = ["<", "{{"];
  let endIndex = context.source.length;

  for (let i = 0; i < endTokens.length; i++) {
    const index = context.source.indexOf(endTokens[i]);
    // endIndex > index 是需要要 endIndex 尽可能的小
    // 比如说:
    // hi, {{123}} <div></div>
    // 那么这里就应该停到 {{ 这里,而不是停到 <div 这里
    if (index !== -1 && endIndex > index) {
      endIndex = index;
    }
  }

  const content = parseTextData(context, endIndex);

  return {
    type: NodeTypes.TEXT,
    content,
  };
}

function parseTextData(context: any, length: number): any {
  console.log("解析 textData");
  // 1. 直接返回 context.source
  // 从 length 切的话,是为了可以获取到 text 的值(需要用一个范围来确定)
  const rawText = context.source.slice(0, length);
  // 2. 移动光标
  advanceBy(context, length);

  return rawText;
}

function advanceBy(context, numberOfCharacters) {
  console.log("推进代码", context, numberOfCharacters);
  context.source = context.source.slice(numberOfCharacters);
}

function createRoot(children) {
  return {
    type: NodeTypes.ROOT,
    children,
    helpers: [],
  };
}

function startsWith(source: string, searchString: string): boolean {
  return source.startsWith(searchString);
}


================================================
FILE: packages/compiler-core/src/runtimeHelpers.ts
================================================
export const TO_DISPLAY_STRING = Symbol(`toDisplayString`);
export const CREATE_ELEMENT_VNODE = Symbol("createElementVNode");

export const helperNameMap = {
  [TO_DISPLAY_STRING]: "toDisplayString",
  [CREATE_ELEMENT_VNODE]: "createElementVNode"
};


================================================
FILE: packages/compiler-core/src/transform.ts
================================================
import { NodeTypes } from "./ast";
import { TO_DISPLAY_STRING } from "./runtimeHelpers";

export function transform(root, options = {}) {
  // 1. 创建 context

  const context = createTransformContext(root, options);

  // 2. 遍历 node
  traverseNode(root, context);

  createRootCodegen(root, context);

  root.helpers.push(...context.helpers.keys());
}

function traverseNode(node: any, context) {
  const type: NodeTypes = node.type;

  // 遍历调用所有的 nodeTransforms
  // 把 node 给到 transform
  // 用户可以对 node 做处理
  const nodeTransforms = context.nodeTransforms;
  const exitFns: any = [];
  for (let i = 0; i < nodeTransforms.length; i++) {
    const transform = nodeTransforms[i];

    const onExit = transform(node, context);
    if (onExit) {
      exitFns.push(onExit);
    }
  }

  switch (type) {
    case NodeTypes.INTERPOLATION:
      // 插值的点,在于后续生成 render 代码的时候是获取变量的值
      context.helper(TO_DISPLAY_STRING);
      break;

    case NodeTypes.ROOT:
    case NodeTypes.ELEMENT:

      traverseChildren(node, context);
      break;

    default:
      break;
  }



  let i = exitFns.length;
  // i-- 这个很巧妙
  // 使用 while 是要比 for 快 (可以使用 https://jsbench.me/ 来测试一下)
  while (i--) {
    exitFns[i]();
  }
}

function traverseChildren(parent: any, context: any) {
  // node.children
  parent.children.forEach((node) => {
    // TODO 需要设置 context 的值
    traverseNode(node, context);
  });
}

function createTransformContext(root, options): any {
  const context = {
    root,
    nodeTransforms: options.nodeTransforms || [],
    helpers: new Map(),
    helper(name) {
      // 这里会收集调用的次数
      // 收集次数是为了给删除做处理的, (当只有 count 为0 的时候才需要真的删除掉)
      // helpers 数据会在后续生成代码的时候用到
      const count = context.helpers.get(name) || 0;
      context.helpers.set(name, count + 1);
    },
  };

  return context;
}

function createRootCodegen(root: any, context: any) {
  const { children } = root;

  // 只支持有一个根节点
  // 并且还是一个 single text node
  const child = children[0];

  // 如果是 element 类型的话 , 那么我们需要把它的 codegenNode 赋值给 root
  // root 其实是个空的什么数据都没有的节点
  // 所以这里需要额外的处理 codegenNode
  // codegenNode 的目的是专门为了 codegen 准备的  为的就是和 ast 的 node 分离开
  if (child.type === NodeTypes.ELEMENT && child.codegenNode) {
    const codegenNode = child.codegenNode;
    root.codegenNode = codegenNode;
  } else {
    root.codegenNode = child;
  }
}


================================================
FILE: packages/compiler-core/src/transforms/transformElement.ts
================================================
import { createVNodeCall, NodeTypes } from "../ast";

export function transformElement(node, context) {
  if (node.type === NodeTypes.ELEMENT) {
    return () => {
      // 没有实现 block  所以这里直接创建 element

      // TODO
      // 需要把之前的 props 和 children 等一系列的数据都处理
      const vnodeTag = `'${node.tag}'`;
      // TODO props 暂时不支持
      const vnodeProps = null;
      let vnodeChildren = null;
      if (node.children.length > 0) {
        if (node.children.length === 1) {
          // 只有一个孩子节点 ,那么当生成 render 函数的时候就不用 [] 包裹
          const child = node.children[0];
          vnodeChildren = child;
        }
      }

      // 创建一个新的 node 用于 codegen 的时候使用
      node.codegenNode = createVNodeCall(
        context,
        vnodeTag,
        vnodeProps,
        vnodeChildren
      );
    };
  }
}


================================================
FILE: packages/compiler-core/src/transforms/transformExpression.ts
================================================
import { NodeTypes } from "../ast";

export function transformExpression(node) {
  if (node.type === NodeTypes.INTERPOLATION) {
    node.content = processExpression(node.content);
  }
}

function processExpression(node) {
  node.content = `_ctx.${node.content}`;

  return node
}


================================================
FILE: packages/compiler-core/src/transforms/transformText.ts
================================================
import { NodeTypes } from "../ast";
import { isText } from "../utils";

export function transformText(node, context) {
  if (node.type === NodeTypes.ELEMENT) {
    // 在 exit 的时期执行
    // 下面的逻辑会改变 ast 树
    // 有些逻辑是需要在改变之前做处理的
    return () => {
      // hi,{{msg}}
      // 上面的模块会生成2个节点,一个是 text 一个是 interpolation 的话
      // 生成的 render 函数应该为 "hi," + _toDisplayString(_ctx.msg)
      // 这里面就会涉及到添加一个 “+” 操作符
      // 那这里的逻辑就是处理它

      // 检测下一个节点是不是 text 类型,如果是的话, 那么会创建一个 COMPOUND 类型
      // COMPOUND 类型把 2个 text || interpolation 包裹(相当于是父级容器)

      const children = node.children;
      let currentContainer;

      for (let i = 0; i < children.length; i++) {
        const child = children[i];

        if (isText(child)) {
          // 看看下一个节点是不是 text 类
          for (let j = i + 1; j < children.length; j++) {
            const next = children[j];
            if (isText(next)) {
              // currentContainer 的目的是把相邻的节点都放到一个 容器内
              if (!currentContainer) {
                currentContainer = children[i] = {
                  type: NodeTypes.COMPOUND_EXPRESSION,
                  loc: child.loc,
                  children: [child],
                };
              }

              currentContainer.children.push(` + `, next);
              // 把当前的节点放到容器内, 然后删除掉j
              children.splice(j, 1);
              // 因为把 j 删除了,所以这里就少了一个元素,那么 j 需要 --
              j--;
            } else {
              currentContainer = undefined;
              break;
            }
          }
        }
      }
    };
  }
}


================================================
FILE: packages/compiler-core/src/utils.ts
================================================
import { NodeTypes } from "./ast";

export function isText(node) {
  return node.type === NodeTypes.INTERPOLATION || node.type === NodeTypes.TEXT;
}


================================================
FILE: packages/reactivity/__tests__/computed.spec.ts
================================================
import { computed } from "../src/computed";
import { reactive } from "../src/reactive";
import {vi} from 'vitest'

describe("computed", () => {
  it("happy path", () => {
    const value = reactive({
      foo: 1,
    });

    const getter = computed(() => {
      return value.foo;
    });

    value.foo = 2;
    expect(getter.value).toBe(2);
  });

  it("should compute lazily", () => {
    const value = reactive({
      foo: 1,
    });
    const getter = vi.fn(() => {
      return value.foo;
    });
    const cValue = computed(getter);

    // lazy
    expect(getter).not.toHaveBeenCalled();

    expect(cValue.value).toBe(1);
    expect(getter).toHaveBeenCalledTimes(1);

    // should not compute again
    cValue.value;
    expect(getter).toHaveBeenCalledTimes(1);

    // should not compute until needed
    value.foo = 2;
    expect(getter).toHaveBeenCalledTimes(1);

    // now it should compute
    expect(cValue.value).toBe(2);
    expect(getter).toHaveBeenCalledTimes(2);

    // should not compute again
    cValue.value;
    expect(getter).toHaveBeenCalledTimes(2);
  });
});


================================================
FILE: packages/reactivity/__tests__/dep.spec.ts
================================================

describe('Dep', () => {


	it('new Set ', () => {
		// const set = new Set([1])

		// console.log(set)


	});


});

================================================
FILE: packages/reactivity/__tests__/effect.spec.ts
================================================
import { reactive } from "../src/reactive";
import { effect, stop } from "../src/effect";
import { vi } from "vitest";

describe("effect", () => {
  it("should run the passed function once (wrapped by a effect)", () => {
    const fnSpy = vi.fn(() => {});
    effect(fnSpy);
    expect(fnSpy).toHaveBeenCalledTimes(1);
  });

  it("should observe basic properties", () => {
    let dummy;
    const counter = reactive({ num: 0 });
    effect(() => (dummy = counter.num));

    expect(dummy).toBe(0);
    counter.num = 7;
    expect(dummy).toBe(7);
  });

  it("should observe multiple properties", () => {
    let dummy;
    const counter = reactive({ num1: 0, num2: 0 });
    effect(() => (dummy = counter.num1 + counter.num1 + counter.num2));

    expect(dummy).toBe(0);
    counter.num1 = counter.num2 = 7;
    expect(dummy).toBe(21);
  });
  it("should handle multiple effects", () => {
    let dummy1, dummy2;
    const counter = reactive({ num: 0 });
    effect(() => (dummy1 = counter.num));
    effect(() => (dummy2 = counter.num));

    expect(dummy1).toBe(0);
    expect(dummy2).toBe(0);
    counter.num++;
    expect(dummy1).toBe(1);
    expect(dummy2).toBe(1);
  });

  it("should observe nested properties", () => {
    let dummy;
    const counter = reactive({ nested: { num: 0 } });
    effect(() => (dummy = counter.nested.num));

    expect(dummy).toBe(0);
    counter.nested.num = 8;
    expect(dummy).toBe(8);
  });

  it("should observe function call chains", () => {
    let dummy;
    const counter = reactive({ num: 0 });
    effect(() => (dummy = getNum()));

    function getNum() {
      return counter.num;
    }

    expect(dummy).toBe(0);
    counter.num = 2;
    expect(dummy).toBe(2);
  });
  it("scheduler", () => {
    let dummy;
    let run: any;
    const scheduler = vi.fn(() => {
      run = runner;
    });
    const obj = reactive({ foo: 1 });
    const runner = effect(
      () => {
        dummy = obj.foo;
      },
      { scheduler }
    );
    expect(scheduler).not.toHaveBeenCalled();
    expect(dummy).toBe(1);
    // should be called on first trigger
    obj.foo++;
    expect(scheduler).toHaveBeenCalledTimes(1);
    // // should not run yet
    expect(dummy).toBe(1);
    // // manually run
    run();
    // // should have run
    expect(dummy).toBe(2);
  });

  it("stop", () => {
    let dummy;
    const obj = reactive({ prop: 1 });
    const runner = effect(() => {
      dummy = obj.prop;
    });
    obj.prop = 2;
    expect(dummy).toBe(2);
    stop(runner);
    // obj.prop = 3
    obj.prop++;
    expect(dummy).toBe(2);

    // stopped effect should still be manually callable
    runner();
    expect(dummy).toBe(3);
  });

  it("events: onStop", () => {
    const onStop = vi.fn();
    const runner = effect(() => {}, {
      onStop,
    });

    stop(runner);
    expect(onStop).toHaveBeenCalled();
  });
});


================================================
FILE: packages/reactivity/__tests__/reactive.spec.ts
================================================
import { reactive, isReactive, toRaw, reactiveMap } from "../src/reactive";
describe("reactive", () => {
  test("Object", () => {
    const original = { foo: 1 };
    const observed = reactive(original);
    expect(observed).not.toBe(original);
    expect(isReactive(observed)).toBe(true);
    expect(isReactive(original)).toBe(false);
    // get
    expect(observed.foo).toBe(1);
    //     // has
    expect("foo" in observed).toBe(true);
    //     // ownKeys
    expect(Object.keys(observed)).toEqual(["foo"]);
  });

  test("nested reactives", () => {
    const original = {
      nested: {
        foo: 1,
      },
      array: [{ bar: 2 }],
    };
    const observed = reactive(original);
    expect(isReactive(observed.nested)).toBe(true);
    expect(isReactive(observed.array)).toBe(true);
    expect(isReactive(observed.array[0])).toBe(true);
  });

  test("toRaw", () => {
    const original = { foo: 1 };
    const observed = reactive(original);
    expect(toRaw(observed)).toBe(original);
    expect(toRaw(original)).toBe(original);
  });
});


================================================
FILE: packages/reactivity/__tests__/readonly.spec.ts
================================================
import { isProxy, isReactive, isReadonly, readonly } from "../src/reactive";

describe("readonly", () => {
  it("should make nested values readonly", () => {
    const original = { foo: 1, bar: { baz: 2 } };
    const wrapped = readonly(original);
    expect(wrapped).not.toBe(original);
    expect(isProxy(wrapped)).toBe(true);
    expect(isReactive(wrapped)).toBe(false);
    expect(isReadonly(wrapped)).toBe(true);
    expect(isReactive(original)).toBe(false);
    expect(isReadonly(original)).toBe(false);
    expect(isReactive(wrapped.bar)).toBe(false);
    expect(isReadonly(wrapped.bar)).toBe(true);
    expect(isReactive(original.bar)).toBe(false);
    expect(isReadonly(original.bar)).toBe(false);
    // get
    expect(wrapped.foo).toBe(1);
  });
});


================================================
FILE: packages/reactivity/__tests__/ref.spec.ts
================================================
import { effect } from "../src/effect";
import { reactive } from "../src/reactive";
import { isRef, ref, unRef,proxyRefs } from "../src/ref";
describe("ref", () => {
  it("should be reactive", () => {
    const a = ref(1);
    let dummy;
    let calls = 0;
    effect(() => {
      calls++;
      dummy = a.value;
    });
    expect(calls).toBe(1);
    expect(dummy).toBe(1);
    a.value = 2;
    expect(calls).toBe(2);
    expect(dummy).toBe(2);
    // same value should not trigger
    a.value = 2;
    expect(calls).toBe(2);
    expect(dummy).toBe(2);
  });

  it("should make nested properties reactive", () => {
    const a = ref({
      count: 1,
    });
    let dummy;
    effect(() => {
      dummy = a.value.count;
    });
    expect(dummy).toBe(1);
    a.value.count = 2;
    expect(dummy).toBe(2);
  });

  it("proxyRefs", () => {
    const user = {
      age: ref(10),
      name: "xiaohong",
    };
    const proxyUser = proxyRefs(user);
    expect(user.age.value).toBe(10);
    expect(proxyUser.age).toBe(10);
    expect(proxyUser.name).toBe("xiaohong");

    (proxyUser as any).age = 20;
    expect(proxyUser.age).toBe(20);
    expect(user.age.value).toBe(20);

    proxyUser.age = ref(10);
    expect(proxyUser.age).toBe(10);
    expect(user.age.value).toBe(10);
  });

  it("isRef", () => {
    const a = ref(1);
    const user = reactive({
      age: 1,
    });
    expect(isRef(a)).toBe(true);
    expect(isRef(1)).toBe(false);
    expect(isRef(user)).toBe(false);
  });

  it("unRef", () => {
    const a = ref(1);
    expect(unRef(a)).toBe(1);
    expect(unRef(1)).toBe(1);
  });
});


================================================
FILE: packages/reactivity/__tests__/shallowReadonly.spec.ts
================================================
import { isReactive, isReadonly, readonly, shallowReadonly } from "../src/reactive";

describe("shallowReadonly", () => {
  test("should not make non-reactive properties reactive", () => {
    const props = shallowReadonly({ n: { foo: 1 } });
    expect(isReactive(props.n)).toBe(false);
  });
  test("should differentiate from normal readonly calls", async () => {
    const original = { foo: {} };
    const shallowProxy = shallowReadonly(original);
    const reactiveProxy = readonly(original);
    expect(shallowProxy).not.toBe(reactiveProxy);
    expect(isReadonly(shallowProxy.foo)).toBe(false);
    expect(isReadonly(reactiveProxy.foo)).toBe(true);
  });
});


================================================
FILE: packages/reactivity/package.json
================================================
{
  "name": "@mini-vue/reactivity",
  "version": "1.0.0",
  "description": "@mini-vue/reactivity",
  "scripts": {
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@mini-vue/shared": "workspace:^1.0.0"
  }
}


================================================
FILE: packages/reactivity/src/baseHandlers.ts
================================================
import { track, trigger } from "./effect";
import {
  reactive,
  ReactiveFlags,
  reactiveMap,
  readonly,
  readonlyMap,
  shallowReadonlyMap,
} from "./reactive";
import { isObject } from "@mini-vue/shared";

const get = createGetter();
const set = createSetter();
const readonlyGet = createGetter(true);
const shallowReadonlyGet = createGetter(true, true);

function createGetter(isReadonly = false, shallow = false) {
  return function get(target, key, receiver) {
    const isExistInReactiveMap = () =>
      key === ReactiveFlags.RAW && receiver === reactiveMap.get(target);

    const isExistInReadonlyMap = () =>
      key === ReactiveFlags.RAW && receiver === readonlyMap.get(target);

    const isExistInShallowReadonlyMap = () =>
      key === ReactiveFlags.RAW && receiver === shallowReadonlyMap.get(target);

    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly;
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly;
    } else if (
      isExistInReactiveMap() ||
      isExistInReadonlyMap() ||
      isExistInShallowReadonlyMap()
    ) {
      return target;
    }

    const res = Reflect.get(target, key, receiver);

    // 问题:为什么是 readonly 的时候不做依赖收集呢
    // readonly 的话,是不可以被 set 的, 那不可以被 set 就意味着不会触发 trigger
    // 所有就没有收集依赖的必要了

    if (!isReadonly) {
      // 在触发 get 的时候进行依赖收集
      track(target, "get", key);
    }

    if (shallow) {
      return res;
    }

    if (isObject(res)) {
      // 把内部所有的是 object 的值都用 reactive 包裹,变成响应式对象
      // 如果说这个 res 值是一个对象的话,那么我们需要把获取到的 res 也转换成 reactive
      // res 等于 target[key]
      return isReadonly ? readonly(res) : reactive(res);
    }

    return res;
  };
}

function createSetter() {
  return function set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);

    // 在触发 set 的时候进行触发依赖
    trigger(target, "set", key);

    return result;
  };
}

export const readonlyHandlers = {
  get: readonlyGet,
  set(target, key) {
    // readonly 的响应式对象不可以修改值
    console.warn(
      `Set operation on key "${String(key)}" failed: target is readonly.`,
      target
    );
    return true;
  },
};

export const mutableHandlers = {
  get,
  set,
};

export const shallowReadonlyHandlers = {
  get: shallowReadonlyGet,
  set(target, key) {
    // readonly 的响应式对象不可以修改值
    console.warn(
      `Set operation on key "${String(key)}" failed: target is readonly.`,
      target
    );
    return true;
  },
};


================================================
FILE: packages/reactivity/src/computed.ts
================================================
import { createDep } from "./dep";
import { ReactiveEffect } from "./effect";
import { trackRefValue, triggerRefValue } from "./ref";

export class ComputedRefImpl {
  public dep: any;
  public effect: ReactiveEffect;

  private _dirty: boolean;
  private _value

  constructor(getter) {
    this._dirty = true;
    this.dep = createDep();
    this.effect = new ReactiveEffect(getter, () => {
      // scheduler
      // 只要触发了这个函数说明响应式对象的值发生改变了
      // 那么就解锁,后续在调用 get 的时候就会重新执行,所以会得到最新的值
      if (this._dirty) return;

      this._dirty = true;
      triggerRefValue(this);
    });
  }

  get value() {
    // 收集依赖
    trackRefValue(this);
    // 锁上,只可以调用一次
    // 当数据改变的时候才会解锁
    // 这里就是缓存实现的核心
    // 解锁是在 scheduler 里面做的
    if (this._dirty) {
      this._dirty = false;
      // 这里执行 run 的话,就是执行用户传入的 fn
      this._value = this.effect.run();
    }

    return this._value;
  }
}

export function computed(getter) {
  return new ComputedRefImpl(getter);
}


================================================
FILE: packages/reactivity/src/dep.ts
================================================
// 用于存储所有的 effect 对象
export function createDep(effects?) {
  const dep = new Set(effects);
  return dep;
}


================================================
FILE: packages/reactivity/src/effect.ts
================================================
import { createDep } from "./dep";
import { extend } from "@mini-vue/shared";

let activeEffect = void 0;
let shouldTrack = false;
const targetMap = new WeakMap();

// 用于依赖收集
export class ReactiveEffect {
  active = true;
  deps = [];
  public onStop?: () => void;
  constructor(public fn, public scheduler?) {
    console.log("创建 ReactiveEffect 对象");
  }

  run() {
    console.log("run");
    // 运行 run 的时候,可以控制 要不要执行后续收集依赖的一步
    // 目前来看的话,只要执行了 fn 那么就默认执行了收集依赖
    // 这里就需要控制了

    // 是不是收集依赖的变量

    // 执行 fn  但是不收集依赖
    if (!this.active) {
      return this.fn();
    }

    // 执行 fn  收集依赖
    // 可以开始收集依赖了
    shouldTrack = true;

    // 执行的时候给全局的 activeEffect 赋值
    // 利用全局属性来获取当前的 effect
    activeEffect = this as any;
    // 执行用户传入的 fn
    console.log("执行用户传入的 fn");
    const result = this.fn();
    // 重置
    shouldTrack = false;
    activeEffect = undefined;

    return result;
  }

  stop() {
    if (this.active) {
      // 如果第一次执行 stop 后 active 就 false 了
      // 这是为了防止重复的调用,执行 stop 逻辑
      cleanupEffect(this);
      if (this.onStop) {
        this.onStop();
      }
      this.active = false;
    }
  }
}

function cleanupEffect(effect) {
  // 找到所有依赖这个 effect 的响应式对象
  // 从这些响应式对象里面把 effect 给删除掉
  effect.deps.forEach((dep) => {
    dep.delete(effect);
  });

  effect.deps.length = 0;
}

export function effect(fn, options = {}) {
  const _effect = new ReactiveEffect(fn);

  // 把用户传过来的值合并到 _effect 对象上去
  // 缺点就是不是显式的,看代码的时候并不知道有什么值
  extend(_effect, options);
  _effect.run();

  // 把 _effect.run 这个方法返回
  // 让用户可以自行选择调用的时机(调用 fn)
  const runner: any = _effect.run.bind(_effect);
  runner.effect = _effect;
  return runner;
}

export function stop(runner) {
  runner.effect.stop();
}

export function track(target, type, key) {
  if (!isTracking()) {
    return;
  }
  console.log(`触发 track -> target: ${target} type:${type} key:${key}`);
  // 1. 先基于 target 找到对应的 dep
  // 如果是第一次的话,那么就需要初始化
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    // 初始化 depsMap 的逻辑
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }

  let dep = depsMap.get(key);

  if (!dep) {
    dep = createDep();

    depsMap.set(key, dep);
  }

  trackEffects(dep);
}

export function trackEffects(dep) {
  // 用 dep 来存放所有的 effect

  // TODO
  // 这里是一个优化点
  // 先看看这个依赖是不是已经收集了,
  // 已经收集的话,那么就不需要在收集一次了
  // 可能会影响 code path change 的情况
  // 需要每次都 cleanupEffect
  // shouldTrack = !dep.has(activeEffect!);
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect);
    (activeEffect as any).deps.push(dep);
  }
}

export function trigger(target, type, key) {
  // 1. 先收集所有的 dep 放到 deps 里面,
  // 后面会统一处理
  let deps: Array<any> = [];
  // dep

  const depsMap = targetMap.get(target);

  if (!depsMap) return;

  // 暂时只实现了 GET 类型
  // get 类型只需要取出来就可以
  const dep = depsMap.get(key);

  // 最后收集到 deps 内
  deps.push(dep);

  const effects: Array<any> = [];
  deps.forEach((dep) => {
    // 这里解构 dep 得到的是 dep 内部存储的 effect
    effects.push(...dep);
  });
  // 这里的目的是只有一个 dep ,这个dep 里面包含所有的 effect
  // 这里的目前应该是为了 triggerEffects 这个函数的复用
  triggerEffects(createDep(effects));
}

export function isTracking() {
  return shouldTrack && activeEffect !== undefined;
}

export function triggerEffects(dep) {
  // 执行收集到的所有的 effect 的 run 方法
  for (const effect of dep) {
    if (effect.scheduler) {
      // scheduler 可以让用户自己选择调用的时机
      // 这样就可以灵活的控制调用了
      // 在 runtime-core 中,就是使用了 scheduler 实现了在 next ticker 中调用的逻辑
      effect.scheduler();
    } else {
      effect.run();
    }
  }
}


================================================
FILE: packages/reactivity/src/index.ts
================================================
export {
  reactive,
  readonly,
  shallowReadonly,
  isReadonly,
  isReactive,
  isProxy,
} from "./reactive";

export { ref, proxyRefs, unRef, isRef } from "./ref";

export { effect, stop, ReactiveEffect } from "./effect";

export { computed } from "./computed";


================================================
FILE: packages/reactivity/src/reactive.ts
================================================
import {
  mutableHandlers,
  readonlyHandlers,
  shallowReadonlyHandlers,
} from "./baseHandlers";

export const reactiveMap = new WeakMap();
export const readonlyMap = new WeakMap();
export const shallowReadonlyMap = new WeakMap();

export const enum ReactiveFlags {
  IS_REACTIVE = "__v_isReactive",
  IS_READONLY = "__v_isReadonly",
  RAW = "__v_raw",
}

export function reactive(target) {
  return createReactiveObject(target, reactiveMap, mutableHandlers);
}

export function readonly(target) {
  return createReactiveObject(target, readonlyMap, readonlyHandlers);
}

export function shallowReadonly(target) {
  return createReactiveObject(
    target,
    shallowReadonlyMap,
    shallowReadonlyHandlers
  );
}

export function isProxy(value) {
  return isReactive(value) || isReadonly(value);
}

export function isReadonly(value) {
  return !!value[ReactiveFlags.IS_READONLY];
}

export function isReactive(value) {
  // 如果 value 是 proxy 的话
  // 会触发 get 操作,而在 createGetter 里面会判断
  // 如果 value 是普通对象的话
  // 那么会返回 undefined ,那么就需要转换成布尔值
  return !!value[ReactiveFlags.IS_REACTIVE];
}

export function toRaw(value) {
  // 如果 value 是 proxy 的话 ,那么直接返回就可以了
  // 因为会触发 createGetter 内的逻辑
  // 如果 value 是普通对象的话,
  // 我们就应该返回普通对象
  // 只要不是 proxy ,只要是得到了 undefined 的话,那么就一定是普通对象
  // TODO 这里和源码里面实现的不一样,不确定后面会不会有问题
  if (!value[ReactiveFlags.RAW]) {
    return value;
  }

  return value[ReactiveFlags.RAW];
}

function createReactiveObject(target, proxyMap, baseHandlers) {
  // 核心就是 proxy
  // 目的是可以侦听到用户 get 或者 set 的动作

  // 如果命中的话就直接返回就好了
  // 使用缓存做的优化点
  const existingProxy = proxyMap.get(target);
  if (existingProxy) {
    return existingProxy;
  }

  const proxy = new Proxy(target, baseHandlers);

  // 把创建好的 proxy 给存起来,
  proxyMap.set(target, proxy);
  return proxy;
}


================================================
FILE: packages/reactivity/src/ref.ts
================================================
import { trackEffects, triggerEffects, isTracking } from "./effect";
import { createDep } from "./dep";
import { isObject, hasChanged } from "@mini-vue/shared";
import { reactive } from "./reactive";

export class RefImpl {
  private _rawValue: any;
  private _value: any;
  public dep;
  public __v_isRef = true;

  constructor(value) {
    this._rawValue = value;
    // 看看value 是不是一个对象,如果是一个对象的话
    // 那么需要用 reactive 包裹一下
    this._value = convert(value);
    this.dep = createDep();
  }

  get value() {
    // 收集依赖
    trackRefValue(this);
    return this._value;
  }

  set value(newValue) {
    // 当新的值不等于老的值的话,
    // 那么才需要触发依赖
    if (hasChanged(newValue, this._rawValue)) {
      // 更新值
      this._value = convert(newValue);
      this._rawValue = newValue;
      // 触发依赖
      triggerRefValue(this);
    }
  }
}

export function ref(value) {
  return createRef(value);
}

function convert(value) {
  return isObject(value) ? reactive(value) : value;
}

function createRef(value) {
  const refImpl = new RefImpl(value);

  return refImpl;
}

export function triggerRefValue(ref) {
  triggerEffects(ref.dep);
}

export function trackRefValue(ref) {
  if (isTracking()) {
    trackEffects(ref.dep);
  }
}

// 这个函数的目的是
// 帮助解构 ref
// 比如在 template 中使用 ref 的时候,直接使用就可以了
// 例如: const count = ref(0) -> 在 template 中使用的话 可以直接 count
// 解决方案就是通过 proxy 来对 ref 做处理

const shallowUnwrapHandlers = {
  get(target, key, receiver) {
    // 如果里面是一个 ref 类型的话,那么就返回 .value
    // 如果不是的话,那么直接返回value 就可以了
    return unRef(Reflect.get(target, key, receiver));
  },
  set(target, key, value, receiver) {
    const oldValue = target[key];
    if (isRef(oldValue) && !isRef(value)) {
      return (target[key].value = value);
    } else {
      return Reflect.set(target, key, value, receiver);
    }
  },
};

// 这里没有处理 objectWithRefs 是 reactive 类型的时候
// TODO reactive 里面如果有 ref 类型的 key 的话, 那么也是不需要调用 ref.value 的
// (but 这个逻辑在 reactive 里面没有实现)
export function proxyRefs(objectWithRefs) {
  return new Proxy(objectWithRefs, shallowUnwrapHandlers);
}

// 把 ref 里面的值拿到
export function unRef(ref) {
  return isRef(ref) ? ref.value : ref;
}

export function isRef(value) {
  return !!value.__v_isRef;
}


================================================
FILE: packages/runtime-core/__tests__/apiWatch.spec.ts
================================================
import { reactive } from "@mini-vue/reactivity";
import { watchEffect } from "../src/apiWatch";
import { nextTick } from "../src/scheduler";
import { vi } from "vitest";

describe("api: watch", () => {
  it("effect", async () => {
    const state = reactive({ count: 0 });
    let dummy;
    watchEffect(() => {
      dummy = state.count;
    });
    expect(dummy).toBe(0);

    state.count++;
    await nextTick();
    expect(dummy).toBe(1);
  });

  it("stopping the watcher (effect)", async () => {
    const state = reactive({ count: 0 });
    let dummy;
    const stop: any = watchEffect(() => {
      dummy = state.count;
    });
    expect(dummy).toBe(0);

    stop();
    state.count++;
    await nextTick();
    // should not update
    expect(dummy).toBe(0);
  });

  it("cleanup registration (effect)", async () => {
    const state = reactive({ count: 0 });
    const cleanup = vi.fn();
    let dummy;
    const stop: any = watchEffect((onCleanup) => {
      onCleanup(cleanup);
      dummy = state.count;
    });
    expect(dummy).toBe(0);

    state.count++;
    await nextTick();
    expect(cleanup).toHaveBeenCalledTimes(1);
    expect(dummy).toBe(1);

    stop();
    expect(cleanup).toHaveBeenCalledTimes(2);
  });
});


================================================
FILE: packages/runtime-core/__tests__/componentEmits.spec.ts
================================================
import { nodeOps, render, h } from "@mini-vue/runtime-test";
import {vi} from 'vitest'

describe("component: emits", () => {
  test("trigger handlers", () => {
    const Foo = {
      render() {
        return h("foo");
      },
      setup(props, { emit }) {
        // the `emit` function is bound on component instances
        emit("foo");
        emit("bar");
      },
    };

    const onfoo = vi.fn();
    const onBar = vi.fn();
    const Comp = {
      render() {
        return h(Foo, { onfoo, onBar });
      },
    };
    render(h(Comp), nodeOps.createElement("div"));

    expect(onfoo).not.toHaveBeenCalled();
    // only capitalized or special chars are considered event listeners
    expect(onBar).toHaveBeenCalled();
  });

  test("trigger camelCase handler", () => {
    const Foo = {
      render() {
        return h("foo");
      },
      setup(props, { emit }) {
        emit("test-event");
      },
    };

    const fooSpy = vi.fn();
    const Comp = {
      render() {
        return h(Foo, { onTestEvent: fooSpy });
      },
    };
    render(h(Comp), nodeOps.createElement("div"));

    expect(fooSpy).toHaveBeenCalledTimes(1);
  });

  test("trigger kebab-case handler", () => {
    const Foo = {
      render() {
        return h("foo");
      },
      setup(props, { emit }) {
        emit("test-event");
      },
    };

    const fooSpy = vi.fn();

    const Comp = {
      render() {
        return h(Foo, { "onTest-event": fooSpy });
      },
    };
    render(h(Comp), nodeOps.createElement("div"));

    expect(fooSpy).toHaveBeenCalledTimes(1);
  });
});


================================================
FILE: packages/runtime-core/__tests__/rendererComponent.spec.ts
================================================
import { h } from "@mini-vue/runtime-dom";
import { nodeOps, render, serializeInner } from "@mini-vue/runtime-test";

describe("renderer: component", () => {
  it("should create an Component ", () => {
    const Comp = {
      render: () => {
        return h("div");
      },
    };
    const root = nodeOps.createElement("div");
    render(h(Comp), root);
    expect(serializeInner(root)).toBe(`<div></div>`);
  });

  it("should create an Component with direct text children", () => {
    const Comp = {
      render: () => {
        return h("div", null, "test");
      },
    };
    const root = nodeOps.createElement("div");
    render(h(Comp), root);
    expect(serializeInner(root)).toBe(`<div>test</div>`);
  });
});


================================================
FILE: packages/runtime-core/__tests__/rendererElement.spec.ts
================================================
import { h } from "@mini-vue/runtime-core";
import { nodeOps, render, serializeInner as inner } from "@mini-vue/runtime-test";

describe("renderer: element", () => {
  let root;

  beforeEach(() => {
    root = nodeOps.createElement("div");
  });

  it("should create an element", () => {
    render(h("div"), root);
    expect(inner(root)).toBe("<div></div>");
  });

  it('should create an element with props', () => {
    render(h('div', { id: 'foo', class: 'bar' },[]), root)
    expect(inner(root)).toBe('<div id="foo" class="bar"></div>')
  })
  it('should create an element with direct text children and props', () => {
    render(h('div', { id: 'foo' }, "bar"), root)
    expect(inner(root)).toBe('<div id="foo">bar</div>')
  })
});




================================================
FILE: packages/runtime-core/package.json
================================================
{
  "name": "@mini-vue/runtime-core",
  "version": "1.0.0",
  "description": "@mini-vue/runtime-core",
  "scripts": {
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@mini-vue/reactivity": "workspace:^1.0.0",
    "@mini-vue/shared": "workspace:^1.0.0"
  }
}


================================================
FILE: packages/runtime-core/src/.pnpm-debug.log
================================================
{
  "0 debug pnpm:scope": {
    "selected": 1,
    "workspacePrefix": "/Users/cxr/projects/mini-vue/code/mini-vue"
  },
  "1 error pnpm": {
    "errno": 1,
    "code": "ELIFECYCLE",
    "pkgid": "@mini-vue/runtime-core@1.0.0",
    "stage": "test",
    "script": "jest \"runtime-core\"",
    "pkgname": "@mini-vue/runtime-core",
    "err": {
      "name": "pnpm",
      "message": "@mini-vue/runtime-core@1.0.0 test: `jest \"runtime-core\"`\nExit status 1",
      "code": "ELIFECYCLE",
      "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)"
    }
  }
}

================================================
FILE: packages/runtime-core/src/apiInject.ts
================================================
import { getCurrentInstance } from "./component";

export function provide(key, value) {
  const currentInstance = getCurrentInstance();

  if (currentInstance) {
    let { provides } = currentInstance;

    const parentProvides = currentInstance.parent?.provides;

    // 这里要解决一个问题
    // 当父级 key 和 爷爷级别的 key 重复的时候,对于子组件来讲,需要取最近的父级别组件的值
    // 那这里的解决方案就是利用原型链来解决
    // provides 初始化的时候是在 createComponent 时处理的,当时是直接把 parent.provides 赋值给组件的 provides 的
    // 所以,如果说这里发现 provides 和 parentProvides 相等的话,那么就说明是第一次做 provide(对于当前组件来讲)
    // 我们就可以把 parent.provides 作为 currentInstance.provides 的原型重新赋值
    // 至于为什么不在 createComponent 的时候做这个处理,可能的好处是在这里初始化的话,是有个懒执行的效果(优化点,只有需要的时候在初始化)
    if (parentProvides === provides) {
      provides = currentInstance.provides = Object.create(parentProvides);
    }

    provides[key] = value;
  }
}

export function inject(key, defaultValue) {
  const currentInstance = getCurrentInstance();

  if (currentInstance) {
    const provides = currentInstance.parent?.provides;

    if (key in provides) {
      return provides[key];
    } else if (defaultValue) {
      if (typeof defaultValue === "function") {
        return defaultValue();
      }
      return defaultValue;
    }
  }
}


================================================
FILE: packages/runtime-core/src/apiWatch.ts
================================================
import { ReactiveEffect } from "@mini-vue/reactivity";
import { queuePreFlushCb } from "./scheduler";

// Simple effect.
export function watchEffect(effect) {
  return doWatch(effect);
}

function doWatch(source) {
  // 把 job 添加到 pre flush 里面
  // 也就是在视图更新完成之前进行渲染(待确认?)
  // 当逻辑执行到这里的时候 就已经触发了 watchEffect
  const job = () => {
    effect.run();
  };

  // 当触发 trigger 的时候会调用 scheduler
  // 这里用 scheduler 的目的就是在更新的时候
  // 让回调可以在 render 前执行 变成一个异步的行为(这里也可以通过 flush 来改变)
  const scheduler = () => queuePreFlushCb(job);

  // cleanup 的作用是为了解决初始化的时候不调用 fn(用户传过来的 cleanup)
  // 第一次执行 watchEffect 的时候 onCleanup 会被调用 而这时候只需要把 fn 赋值给 cleanup 就可以
  // 当第二次执行 watchEffect 的时候就需要执行 fn 了 也就是 cleanup  
  let cleanup;
  const onCleanup = (fn) => {
    // 当 effect stop 的时候也需要执行 cleanup 
    // 所以可以在 onStop 中直接执行 fn
    cleanup = effect.onStop = () => {
      fn();
    };
  };
  // 这里是在执行 effect.run 的时候就会调用的
  const getter = () => {
    // 这个的检测就是初始化不执行 cleanup 的关键点
    if (cleanup) {
      cleanup();
    }

    source(onCleanup);
  };

  const effect = new ReactiveEffect(getter, scheduler);

  // 这里执行的就是 getter
  effect.run();

  // 返回值为 StopHandle
  // 只需要调用 stop 即可
  return () => {
    effect.stop();
  };
}


================================================
FILE: packages/runtime-core/src/component.ts
================================================
import { initProps } from "./componentProps";
import { initSlots } from "./componentSlots";
import { emit } from "./componentEmits";
import { PublicInstanceProxyHandlers } from "./componentPublicInstance";
import { proxyRefs, shallowReadonly } from "@mini-vue/reactivity";
export function createComponentInstance(vnode, parent) {
  const instance = {
    type: vnode.type,
    vnode,
    next: null, // 需要更新的 vnode,用于更新 component 类型的组件
    props: {},
    parent,
    provides: parent ? parent.provides : {}, //  获取 parent 的 provides 作为当前组件的初始化值 这样就可以继承 parent.provides 的属性了
    proxy: null,
    isMounted: false,
    attrs: {}, // 存放 attrs 的数据
    slots: {}, // 存放插槽的数据
    ctx: {}, // context 对象
    setupState: {}, // 存储 setup 的返回值
    emit: () => {},
  };

  // 在 prod 坏境下的 ctx 只是下面简单的结构
  // 在 dev 环境下会更复杂
  instance.ctx = {
    _: instance,
  };

  // 赋值 emit
  // 这里使用 bind 把 instance 进行绑定
  // 后面用户使用的时候只需要给 event 和参数即可
  instance.emit = emit.bind(null, instance) as any;

  return instance;
}

export function setupComponent(instance) {
  // 1. 处理 props
  // 取出存在 vnode 里面的 props
  const { props, children } = instance.vnode;
  initProps(instance, props);
  // 2. 处理 slots
  initSlots(instance, children);

  // 源码里面有两种类型的 component
  // 一种是基于 options 创建的
  // 还有一种是 function 的
  // 这里处理的是 options 创建的
  // 叫做 stateful 类型
  setupStatefulComponent(instance);
}

function setupStatefulComponent(instance) {
  // todo
  // 1. 先创建代理 proxy
  console.log("创建 proxy");

  // proxy 对象其实是代理了 instance.ctx 对象
  // 我们在使用的时候需要使用 instance.proxy 对象
  // 因为 instance.ctx 在 prod 和 dev 坏境下是不同的
  instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);
  // 用户声明的对象就是 instance.type
  // const Component = {setup(),render()} ....
  const Component = instance.type;
  // 2. 调用 setup

  // 调用 setup 的时候传入 props
  const { setup } = Component;
  if (setup) {
    // 设置当前 currentInstance 的值
    // 必须要在调用 setup 之前
    setCurrentInstance(instance);

    const setupContext = createSetupContext(instance);
    // 真实的处理场景里面应该是只在 dev 环境才会把 props 设置为只读的
    const setupResult =
      setup && setup(shallowReadonly(instance.props), setupContext);

    setCurrentInstance(null);

    // 3. 处理 setupResult
    handleSetupResult(instance, setupResult);
  } else {
    finishComponentSetup(instance);
  }
}

function createSetupContext(instance) {
  console.log("初始化 setup context");
  return {
    attrs: instance.attrs,
    slots: instance.slots,
    emit: instance.emit,
    expose: () => {}, // TODO 实现 expose 函数逻辑
  };
}

function handleSetupResult(instance, setupResult) {
  // setup 返回值不一样的话,会有不同的处理
  // 1. 看看 setupResult 是个什么
  if (typeof setupResult === "function") {
    // 如果返回的是 function 的话,那么绑定到 render 上
    // 认为是 render 逻辑
    // setup(){ return ()=>(h("div")) }
    instance.render = setupResult;
  } else if (typeof setupResult === "object") {
    // 返回的是一个对象的话
    // 先存到 setupState 上
    // 先使用 @vue/reactivity 里面的 proxyRefs
    // 后面我们自己构建
    // proxyRefs 的作用就是把 setupResult 对象做一层代理
    // 方便用户直接访问 ref 类型的值
    // 比如 setupResult 里面有个 count 是个 ref 类型的对象,用户使用的时候就可以直接使用 count 了,而不需要在 count.value
    // 这里也就是官网里面说到的自动结构 Ref 类型
    instance.setupState = proxyRefs(setupResult);
  }

  finishComponentSetup(instance);
}

function finishComponentSetup(instance) {
  // 给 instance 设置 render

  // 先取到用户设置的 component options
  const Component = instance.type;

  if (!instance.render) {
    // 如果 compile 有值 并且当组件没有 render 函数,那么就需要把 template 编译成 render 函数
    if (compile && !Component.render) {
      if (Component.template) {
        // 这里就是 runtime 模块和 compile 模块结合点
        const template = Component.template;
        Component.render = compile(template);
      }
    }

    instance.render = Component.render;
  }

  // applyOptions()
}

function applyOptions() {
  // 兼容 vue2.x
  // todo
  // options api
}

let currentInstance = {};
// 这个接口暴露给用户,用户可以在 setup 中获取组件实例 instance
export function getCurrentInstance(): any {
  return currentInstance;
}

export function setCurrentInstance(instance) {
  currentInstance = instance;
}

let compile;
export function registerRuntimeCompiler(_compile) {
  compile = _compile;
}


================================================
FILE: packages/runtime-core/src/componentEmits.ts
================================================
import { camelize, hyphenate, toHandlerKey } from "@mini-vue/shared";
export function emit(instance, event: string, ...rawArgs) {
  // 1. emit 是基于 props 里面的 onXXX 的函数来进行匹配的
  // 所以我们先从 props 中看看是否有对应的 event handler
  const props = instance.props;
  // ex: event -> click 那么这里取的就是 onClick
  // 让事情变的复杂一点如果是烤肉串命名的话,需要转换成  change-page -> changePage
  // 需要得到事件名称
  let handler = props[toHandlerKey(camelize(event))];

  // 如果上面没有匹配的话 那么在检测一下 event 是不是 kebab-case 类型
  if (!handler) {
    handler = props[(toHandlerKey(hyphenate(event)))]
  }


  if (handler) {
    handler(...rawArgs);
  }
}


================================================
FILE: packages/runtime-core/src/componentProps.ts
================================================
export function initProps(instance, rawProps) {
  console.log("initProps");

  // TODO
  // 应该还有 attrs 的概念
  // attrs
  // 如果组件声明了 props 的话,那么才可以进入 props 属性内
  // 不然的话是需要存储在 attrs 内
  // 这里暂时直接赋值给 instance.props 即可
  instance.props = rawProps;
}


================================================
FILE: packages/runtime-core/src/componentPublicInstance.ts
================================================
import { hasOwn } from "@mini-vue/shared";

const publicPropertiesMap = {
  // 当用户调用 instance.proxy.$emit 时就会触发这个函数
  // i 就是 instance 的缩写 也就是组件实例对象
  $el: (i) => i.vnode.el,
  $emit: (i) => i.emit,
  $slots: (i) => i.slots,
  $props: (i) => i.props,
};

// todo 需要让用户可以直接在 render 函数内直接使用 this 来触发 proxy
export const PublicInstanceProxyHandlers = {
  get({ _: instance }, key) {
    // 用户访问 proxy[key]
    // 这里就匹配一下看看是否有对应的 function
    // 有的话就直接调用这个 function
    const { setupState, props } = instance;
    console.log(`触发 proxy hook , key -> : ${key}`);

    if (key[0] !== "$") {
      // 说明不是访问 public api
      // 先检测访问的 key 是否存在于 setupState 中, 是的话直接返回
      if (hasOwn(setupState, key)) {
        return setupState[key];
      } else if (hasOwn(props, key)) {
        // 看看 key 是不是在 props 中
        // 代理是可以访问到 props 中的 key 的
        return props[key];
      }
    }

    const publicGetter = publicPropertiesMap[key];

    if (publicGetter) {
      return publicGetter(instance);
    }
  },

  set({ _: instance }, key, value) {
    const { setupState } = instance;

    if (hasOwn(setupState, key)) {
      // 有的话 那么就直接赋值
      setupState[key] = value;
    }

    return true
  },
};


================================================
FILE: packages/runtime-core/src/componentRenderUtils.ts
================================================
export function shouldUpdateComponent(prevVNode, nextVNode) {
  const { props: prevProps } = prevVNode;
  const { props: nextProps } = nextVNode;
  //   const emits = component!.emitsOptions;

  // 这里主要是检测组件的 props
  // 核心:只要是 props 发生改变了,那么这个 component 就需要更新

  // 1. props 没有变化,那么不需要更新
  if (prevProps === nextProps) {
    return false;
  }
  // 如果之前没有 props,那么就需要看看现在有没有 props 了
  // 所以这里基于 nextProps 的值来决定是否更新
  if (!prevProps) {
    return !!nextProps;
  }
  // 之前有值,现在没值,那么肯定需要更新
  if (!nextProps) {
    return true;
  }

  // 以上都是比较明显的可以知道 props 是否是变化的
  // 在 hasPropsChanged 会做更细致的对比检测
  return hasPropsChanged(prevProps, nextProps);
}

function hasPropsChanged(prevProps, nextProps): boolean {
  // 依次对比每一个 props.key

  // 提前对比一下 length ,length 不一致肯定是需要更新的
  const nextKeys = Object.keys(nextProps);
  if (nextKeys.length !== Object.keys(prevProps).length) {
    return true;
  }

  // 只要现在的 prop 和之前的 prop 不一样那么就需要更新
  for (let i = 0; i < nextKeys.length; i++) {
    const key = nextKeys[i];
    if (nextProps[key] !== prevProps[key]) {
      return true;
    }
  }
  return false;
}


================================================
FILE: packages/runtime-core/src/componentSlots.ts
================================================
import { ShapeFlags } from "@mini-vue/shared";
export function initSlots(instance, children) {
  const { vnode } = instance;

  console.log("初始化 slots");

  if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
    normalizeObjectSlots(children, (instance.slots = {}));
  }
}

const normalizeSlotValue = (value) => {
  // 把 function 返回的值转换成 array ,这样 slot 就可以支持多个元素了
  return Array.isArray(value) ? value : [value];
};

const normalizeObjectSlots = (rawSlots, slots) => {
  for (const key in rawSlots) {
    const value = rawSlots[key];
    if (typeof value === "function") {
      // 把这个函数给到slots 对象上存起来
      // 后续在 renderSlots 中调用
      // TODO 这里没有对 value 做 normalize,
      // 默认 slots 返回的就是一个 vnode 对象
      slots[key] = (props) => normalizeSlotValue(value(props));
    }
  }
};


================================================
FILE: packages/runtime-core/src/createApp.ts
================================================
import { createVNode } from "./vnode";

export function createAppAPI(render) {
  return function createApp(rootComponent) {
    const app = {
      _component: rootComponent,
      mount(rootContainer) {
        console.log("基于根组件创建 vnode");
        const vnode = createVNode(rootComponent);
        console.log("调用 render,基于 vnode 进行开箱");
        render(vnode, rootContainer);
      },
    };

    return app;
  };
}


================================================
FILE: packages/runtime-core/src/h.ts
================================================
import { createVNode } from "./vnode";
export const h = (type: any , props: any = null, children: string | Array<any> = []) => {
  return createVNode(type, props, children);
};


================================================
FILE: packages/runtime-core/src/helpers/renderSlot.ts
================================================
import { createVNode, Fragment } from "../vnode";

/**
 * Compiler runtime helper for rendering `<slot/>`
 * 用来 render slot 的
 * 之前是把 slot 的数据都存在 instance.slots 内(可以看 componentSlot.ts),
 * 这里就是取数据然后渲染出来的点
 * 这个是由 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
 * 其最终目的就是在 render 函数中调用 renderSlot 取 instance.slots 内的数据
 * TODO 这里应该是一个返回一个 block ,但是暂时还没有支持 block ,所以这个暂时只需要返回一个 vnode 即可
 * 因为 block 的本质就是返回一个 vnode
 *
 * @private
 */
export function renderSlot(slots, name: string, props = {}) {
  const slot = slots[name];
  console.log(`渲染插槽 slot -> ${name}`);
  if (slot) {
    // 因为 slot 是一个返回 vnode 的函数,我们只需要把这个结果返回出去即可
    // slot 就是一个函数,所以就可以把当前组件的一些数据给传出去,这个就是作用域插槽
    // 参数就是 props
    const slotContent = slot(props);
    return createVNode(Fragment, {}, slotContent);
  }
}


================================================
FILE: packages/runtime-core/src/index.ts
================================================
export * from "./h";
export * from "./createApp";
export { getCurrentInstance, registerRuntimeCompiler } from "./component";
export { inject, provide } from "./apiInject";
export { renderSlot } from "./helpers/renderSlot";
export { createTextVNode, createElementVNode } from "./vnode";
export { createRenderer } from "./renderer";
export { toDisplayString } from "@mini-vue/shared";
export { watchEffect } from "./apiWatch";
export {
  // core
  reactive,
  ref,
  readonly,
  // utilities
  unRef,
  proxyRefs,
  isReadonly,
  isReactive,
  isProxy,
  isRef,
  // advanced
  shallowReadonly,
  // effect
  effect,
  stop,
  computed,
} from "@mini-vue/reactivity";


================================================
FILE: packages/runtime-core/src/renderer.ts
================================================
import { ShapeFlags } from "@mini-vue/shared";
import { createComponentInstance } from "./component";
import { queueJob } from "./scheduler";
import { effect } from "@mini-vue/reactivity";
import { setupComponent } from "./component";
import { Fragment, normalizeVNode, Text } from "./vnode";
import { shouldUpdateComponent } from "./componentRenderUtils";
import { createAppAPI } from "./createApp";

export function createRenderer(options) {
  const {
    createElement: hostCreateElement,
    setElementText: hostSetElementText,
    patchProp: hostPatchProp,
    insert: hostInsert,
    remove: hostRemove,
    setText: hostSetText,
    createText: hostCreateText,
  } = options;

  const render = (vnode, container) => {
    console.log("调用 patch")
    patch(null, vnode, container);
  };

  function patch(
    n1,
    n2,
    container = null,
    anchor = null,
    parentComponent = null
  ) {
    // 基于 n2 的类型来判断
    // 因为 n2 是新的 vnode
    const { type, shapeFlag } = n2;
    switch (type) {
      case Text:
        processText(n1, n2, container);
        break;
      // 其中还有几个类型比如: static fragment comment
      case Fragment:
        processFragment(n1, n2, container);
        break;
      default:
        // 这里就基于 shapeFlag 来处理
        if (shapeFlag & ShapeFlags.ELEMENT) {
          console.log("处理 element");
          processElement(n1, n2, container, anchor, parentComponent);
        } else if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
          console.log("处理 component");
          processComponent(n1, n2, container, parentComponent);
        }
    }
  }

  function processFragment(n1: any, n2: any, container: any) {
    // 只需要渲染 children ,然后给添加到 container 内
    if (!n1) {
      // 初始化 Fragment 逻辑点
      console.log("初始化 Fragment 类型的节点");
      mountChildren(n2.children, container);
    }
  }

  function processText(n1, n2, container) {
    console.log("处理 Text 节点");
    if (n1 === null) {
      // n1 是 null 说明是 init 的阶段
      // 基于 createText 创建出 text 节点,然后使用 insert 添加到 el 内
      console.log("初始化 Text 类型的节点");
      hostInsert((n2.el = hostCreateText(n2.children as string)), container);
    } else {
      // update
      // 先对比一下 updated 之后的内容是否和之前的不一样
      // 在不一样的时候才需要 update text
      // 这里抽离出来的接口是 setText
      // 注意,这里一定要记得把 n1.el 赋值给 n2.el, 不然后续是找不到值的
      const el = (n2.el = n1.el!);
      if (n2.children !== n1.children) {
        console.log("更新 Text 类型的节点");
        hostSetText(el, n2.children as string);
      }
    }
  }

  function processElement(n1, n2, container, anchor, parentComponent) {
    if (!n1) {
      mountElement(n2, container, anchor);
    } else {
      // todo
      updateElement(n1, n2, container, anchor, parentComponent);
    }
  }

  function updateElement(n1, n2, container, anchor, parentComponent) {
    const oldProps = (n1 && n1.props) || {};
    const newProps = n2.props || {};
    // 应该更新 element
    console.log("应该更新 element");
    console.log("旧的 vnode", n1);
    console.log("新的 vnode", n2);

    // 需要把 el 挂载到新的 vnode
    const el = (n2.el = n1.el);

    // 对比 props
    patchProps(el, oldProps, newProps);

    // 对比 children
    patchChildren(n1, n2, el, anchor, parentComponent);
  }

  function patchProps(el, oldProps, newProps) {
    // 对比 props 有以下几种情况
    // 1. oldProps 有,newProps 也有,但是 val 值变更了
    // 举个栗子
    // 之前: oldProps.id = 1 ,更新后:newProps.id = 2

    // key 存在 oldProps 里 也存在 newProps 内
    // 以 newProps 作为基准
    for (const key in newProps) {
      const prevProp = oldProps[key];
      const nextProp = newProps[key];
      if (prevProp !== nextProp) {
        // 对比属性
        // 需要交给 host 来更新 key
        hostPatchProp(el, key, prevProp, nextProp);
      }
    }

    // 2. oldProps 有,而 newProps 没有了
    // 之前: {id:1,tId:2}  更新后: {id:1}
    // 这种情况下我们就应该以 oldProps 作为基准,因为在 newProps 里面是没有的 tId 的
    // 还需要注意一点,如果这个 key 在 newProps 里面已经存在了,说明已经处理过了,就不要在处理了
    for (const key in oldProps) {
      const prevProp = oldProps[key];
      const nextProp = null;
      if (!(key in newProps)) {
        // 这里是以 oldProps 为基准来遍历,
        // 而且得到的值是 newProps 内没有的
        // 所以交给 host 更新的时候,把新的值设置为 null
        hostPatchProp(el, key, prevProp, nextProp);
      }
    }
  }

  function patchChildren(n1, n2, container, anchor, parentComponent) {
    const { shapeFlag: prevShapeFlag, children: c1 } = n1;
    const { shapeFlag, children: c2 } = n2;

    // 如果 n2 的 children 是 text 类型的话
    // 就看看和之前的 n1 的 children 是不是一样的
    // 如果不一样的话直接重新设置一下 text 即可
    if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
      if (c2 !== c1) {
        console.log("类型为 text_children, 当前需要更新");
        hostSetElementText(container, c2 as string);
      }
    } else {
      // 看看之前的是不是 text
      if (prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
        // 先清空
        // 然后在把新的 children 给 mount 生成 element
        hostSetElementText(container, "");
        mountChildren(c2, container);
      } else {
        // array diff array
      // 如果之前是 array_children
      // 现在还是 array_children 的话
      // 那么我们就需要对比两个 children 啦
        patchKeyedChildren(c1, c2, container, parentComponent, anchor);
      }

    }
  }

  function patchKeyedChildren(
    c1: any[],
    c2: any[],
    container,
    parentAnchor,
    parentComponent
  ) {
    let i = 0;
    const l2 = c2.length;
    let e1 = c1.length - 1;
    let e2 = l2 - 1;

    const isSameVNodeType = (n1, n2) => {
      return n1.type === n2.type && n1.key === n2.key;
    };

    while (i <= e1 && i <= e2) {
      const prevChild = c1[i];
      const nextChild = c2[i];

      if (!isSameVNodeType(prevChild, nextChild)) {
        console.log("两个 child 不相等(从左往右比对)");
        console.log(`prevChild:${prevChild}`);
        console.log(`nextChild:${nextChild}`);
        break;
      }

      console.log("两个 child 相等,接下来对比这两个 child 节点(从左往右比对)");
      patch(prevChild, nextChild, container, parentAnchor, parentComponent);
      i++;
    }

    while (i <= e1 && i <= e2) {
      // 从右向左取值
      const prevChild = c1[e1];
      const nextChild = c2[e2];

      if (!isSameVNodeType(prevChild, nextChild)) {
        console.log("两个 child 不相等(从右往左比对)");
        console.log(`prevChild:${prevChild}`);
        console.log(`nextChild:${nextChild}`);
        break;
      }
      console.log("两个 child 相等,接下来对比这两个 child 节点(从右往左比对)");
      patch(prevChild, nextChild, container, parentAnchor, parentComponent);
      e1--;
      e2--;
    }

    if (i > e1 && i <= e2) {
      // 如果是这种情况的话就说明 e2 也就是新节点的数量大于旧节点的数量
      // 也就是说新增了 vnode
      // 应该循环 c2
      // 锚点的计算:新的节点有可能需要添加到尾部,也可能添加到头部,所以需要指定添加的问题
      // 要添加的位置是当前的位置(e2 开始)+1
      // 因为对于往左侧添加的话,应该获取到 c2 的第一个元素
      // 所以我们需要从 e2 + 1 取到锚点的位置
      const nextPos = e2 + 1;
      const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;
      while (i <= e2) {
        console.log(`需要新创建一个 vnode: ${c2[i].key}`);
        patch(null, c2[i], container, anchor, parentComponent);
        i++;
      }
    } else if (i > e2 && i <= e1) {
      // 这种情况的话说明新节点的数量是小于旧节点的数量的
      // 那么我们就需要把多余的
      while (i <= e1) {
        console.log(`需要删除当前的 vnode: ${c1[i].key}`);
        hostRemove(c1[i].el);
        i++;
      }
    } else {
      // 左右两边都比对完了,然后剩下的就是中间部位顺序变动的
      // 例如下面的情况
      // a,b,[c,d,e],f,g
      // a,b,[e,c,d],f,g

      let s1 = i;
      let s2 = i;
      const keyToNewIndexMap = new Map();
      let moved = false;
      let maxNewIndexSoFar = 0;
      // 先把 key 和 newIndex 绑定好,方便后续基于 key 找到 newIndex
      // 时间复杂度是 O(1)
      for (let i = s2; i <= e2; i++) {
        const nextChild = c2[i];
        keyToNewIndexMap.set(nextChild.key, i);
      }

      // 需要处理新节点的数量
      const toBePatched = e2 - s2 + 1;
      let patched = 0;
      // 初始化 从新的index映射为老的index
      // 创建数组的时候给定数组的长度,这个是性能最快的写法
      const newIndexToOldIndexMap = new Array(toBePatched);
      // 初始化为 0 , 后面处理的时候 如果发现是 0 的话,那么就说明新值在老的里面不存在
      for (let i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0;

      // 遍历老节点
      // 1. 需要找出老节点有,而新节点没有的 -> 需要把这个节点删除掉
      // 2. 新老节点都有的,—> 需要 patch
      for (i = s1; i <= e1; i++) {
        const prevChild = c1[i];

        // 优化点
        // 如果老的节点大于新节点的数量的话,那么这里在处理老节点的时候就直接删除即可
        if (patched >= toBePatched) {
          hostRemove(prevChild.el);
          continue;
        }

        let newIndex;
        if (prevChild.key != null) {
          // 这里就可以通过key快速的查找了, 看看在新的里面这个节点存在不存在
          // 时间复杂度O(1)
          newIndex = keyToNewIndexMap.get(prevChild.key);
        } else {
          // 如果没key 的话,那么只能是遍历所有的新节点来确定当前节点存在不存在了
          // 时间复杂度O(n)
          for (let j = s2; j <= e2; j++) {
            if (isSameVNodeType(prevChild, c2[j])) {
              newIndex = j;
              break;
            }
          }
        }

        // 因为有可能 nextIndex 的值为0(0也是正常值)
        // 所以需要通过值是不是 undefined 或者 null 来判断
        if (newIndex === undefined) {
          // 当前节点的key 不存在于 newChildren 中,需要把当前节点给删除掉
          hostRemove(prevChild.el);
        } else {
          // 新老节点都存在
          console.log("新老节点都存在");
          // 把新节点的索引和老的节点的索引建立映射关系
          // i + 1 是因为 i 有可能是0 (0 的话会被认为新节点在老的节点中不存在)
          newIndexToOldIndexMap[newIndex - s2] = i + 1;
          // 来确定中间的节点是不是需要移动
          // 新的 newIndex 如果一直是升序的话,那么就说明没有移动
          // 所以我们可以记录最后一个节点在新的里面的索引,然后看看是不是升序
          // 不是升序的话,我们就可以确定节点移动过了
          if (newIndex >= maxNewIndexSoFar) {
            maxNewIndexSoFar = newIndex;
          } else {
            moved = true;
          }

          patch(prevChild, c2[newIndex], container, null, parentComponent);
          patched++;
        }
      }

      // 利用最长递增子序列来优化移动逻辑
      // 因为元素是升序的话,那么这些元素就是不需要移动的
      // 而我们就可以通过最长递增子序列来获取到升序的列表
      // 在移动的时候我们去对比这个列表,如果对比上的话,就说明当前元素不需要移动
      // 通过 moved 来进行优化,如果没有移动过的话 那么就不需要执行算法
      // getSequence 返回的是 newIndexToOldIndexMap 的索引值
      // 所以后面我们可以直接遍历索引值来处理,也就是直接使用 toBePatched 即可
      const increasingNewIndexSequence = moved
        ? getSequence(newIndexToOldIndexMap)
        : [];
      let j = increasingNewIndexSequence.length - 1;

      // 遍历新节点
      // 1. 需要找出老节点没有,而新节点有的 -> 需要把这个节点创建
      // 2. 最后需要移动一下位置,比如 [c,d,e] -> [e,c,d]

      // 这里倒循环是因为在 insert 的时候,需要保证锚点是处理完的节点(也就是已经确定位置了)
      // 因为 insert 逻辑是使用的 insertBefore()
      for (let i = toBePatched - 1; i >= 0; i--) {
        // 确定当前要处理的节点索引
        const nextIndex = s2 + i;
        const nextChild = c2[nextIndex];
        // 锚点等于当前节点索引+1
        // 也就是当前节点的后面一个节点(又因为是倒遍历,所以锚点是位置确定的节点)
        const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;

        if (newIndexToOldIndexMap[i] === 0) {
          // 说明新节点在老的里面不存在
          // 需要创建
          patch(null, nextChild, container, anchor, parentComponent);
        } else if (moved) {
          // 需要移动
          // 1. j 已经没有了 说明剩下的都需要移动了
          // 2. 最长子序列里面的值和当前的值匹配不上, 说明当前元素需要移动
          if (j < 0 || increasingNewIndexSequence[j] !== i) {
            // 移动的话使用 insert 即可
            hostInsert(nextChild.el, container, anchor);
          } else {
            // 这里就是命中了  index 和 最长递增子序列的值
            // 所以可以移动指针了
            j--;
          }
        }
      }
    }
  }

  function mountElement(vnode, container, anchor) {
    const { shapeFlag, props } = vnode;
    // 1. 先创建 element
    // 基于可扩展的渲染 api
    const el = (vnode.el = hostCreateElement(vnode.type));

    // 支持单子组件和多子组件的创建
    if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
      // 举个栗子
      // render(){
      //     return h("div",{},"test")
      // }
      // 这里 children 就是 test ,只需要渲染一下就完事了
      console.log(`处理文本:${vnode.children}`);
      hostSetElementText(el, vnode.children);
    } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
      // 举个栗子
      // render(){
      // Hello 是个 component
      //     return h("div",{},[h("p"),h(Hello)])
      // }
      // 这里 children 就是个数组了,就需要依次调用 patch 递归来处理
      mountChildren(vnode.children, el);
    }

    // 处理 props
    if (props) {
      for (const key in props) {
        // todo
        // 需要过滤掉vue自身用的key
        // 比如生命周期相关的 key: beforeMount、mounted
        const nextVal = props[key];
        hostPatchProp(el, key, null, nextVal);
      }
    }

    // todo
    // 触发 beforeMount() 钩子
    console.log("vnodeHook  -> onVnodeBeforeMount");
    console.log("DirectiveHook  -> beforeMount");
    console.log("transition  -> beforeEnter");

    // 插入
    hostInsert(el, container, anchor);

    // todo
    // 触发 mounted() 钩子
    console.log("vnodeHook  -> onVnodeMounted");
    console.log("DirectiveHook  -> mounted");
    console.log("transition  -> enter");
  }

  function mountChildren(children, container) {
    children.forEach((VNodeChild) => {
      // todo
      // 这里应该需要处理一下 vnodeChild
      // 因为有可能不是 vnode 类型
      console.log("mountChildren:", VNodeChild);
      patch(null, VNodeChild, container);
    });
  }

  function processComponent(n1, n2, container, parentComponent) {
    // 如果 n1 没有值的话,那么就是 mount
    if (!n1) {
      // 初始化 component
      mountComponent(n2, container, parentComponent);
    } else {
      updateComponent(n1, n2, container);
    }
  }

  // 组件的更新
  function updateComponent(n1, n2, container) {
    console.log("更新组件", n1, n2);
    // 更新组件实例引用
    const instance = (n2.component = n1.component);
    // 先看看这个组件是否应该更新
    if (shouldUpdateComponent(n1, n2)) {
      console.log(`组件需要更新: ${instance}`);
      // 那么 next 就是新的 vnode 了(也就是 n2)
      instance.next = n2;
      // 这里的 update 是在 setupRenderEffect 里面初始化的,update 函数除了当内部的响应式对象发生改变的时候会调用
      // 还可以直接主动的调用(这是属于 effect 的特性)
      // 调用 update 再次更新调用 patch 逻辑
      // 在update 中调用的 next 就变成了 n2了
      // ps:可以详细的看看 update 中 next 的应用
      // TODO 需要在 update 中处理支持 next 的逻辑
      instance.update();
    } else {
      console.log(`组件不需要更新: ${instance}`);
      // 不需要更新的话,那么只需要覆盖下面的属性即可
      n2.component = n1.component;
      n2.el = n1.el;
      instance.vnode = n2;
    }
  }

  function mountComponent(initialVNode, container, parentComponent) {
    // 1. 先创建一个 component instance
    const instance = (initialVNode.component = createComponentInstance(
      initialVNode,
      parentComponent
    ));
    console.log(`创建组件实例:${instance.type.name}`);
    // 2. 给 instance 加工加工
    setupComponent(instance);

    setupRenderEffect(instance, initialVNode, container);
  }

  function setupRenderEffect(instance, initialVNode, container) {
    // 调用 render
    // 应该传入 ctx 也就是 proxy
    // ctx 可以选择暴露给用户的 api
    // 源代码里面是调用的 renderComponentRoot 函数
    // 这里为了简化直接调用 render

    // obj.name  = "111"
    // obj.name = "2222"
    // 从哪里做一些事
    // 收集数据改变之后要做的事 (函数)
    // 依赖收集   effect 函数
    // 触发依赖
    function componentUpdateFn() {
      if (!instance.isMounted) {
        // 组件初始化的时候会执行这里
        // 为什么要在这里调用 render 函数呢
        // 是因为在 effect 内调用 render 才能触发依赖收集
        // 等到后面响应式的值变更后会再次触发这个函数
        console.log(`${instance.type.name}:调用 render,获取 subTree`);
        const proxyToUse = instance.proxy;
        // 可在 render 函数中通过 this 来使用 proxy
        const subTree = (instance.subTree = normalizeVNode(
          instance.render.call(proxyToUse, proxyToUse)
        ));
        console.log("subTree", subTree);

        // todo
        console.log(`${instance.type.name}:触发 beforeMount hook`);
        console.log(`${instance.type.name}:触发 onVnodeBeforeMount hook`);

        // 这里基于 subTree 再次调用 patch
        // 基于 render 返回的 vnode ,再次进行渲染
        // 这里我把这个行为隐喻成开箱
        // 一个组件就是一个箱子
        // 里面有可能是 element (也就是可以直接渲染的)
        // 也有可能还是 component
        // 这里就是递归的开箱
        // 而 subTree 就是当前的这个箱子(组件)装的东西
        // 箱子(组件)只是个概念,它实际是不需要渲染的
        // 要渲染的是箱子里面的 subTree
        patch(null, subTree, container, null, instance);
        // 把 root element 赋值给 组件的vnode.el ,为后续调用 $el 的时候获取值
        initialVNode.el = subTree.el;

        console.log(`${instance.type.name}:触发 mounted hook`);
        instance.isMounted = true;
      } else {
        // 响应式的值变更后会从这里执行逻辑
        // 主要就是拿到新的 vnode ,然后和之前的 vnode 进行对比
        console.log(`${instance.type.name}:调用更新逻辑`);
        // 拿到最新的 subTree
        const { next, vnode } = instance;

        // 如果有 next 的话, 说明需要更新组件的数据(props,slots 等)
        // 先更新组件的数据,然后更新完成后,在继续对比当前组件的子元素
        if (next) {
          // 问题是 next 和 vnode 的区别是什么
          next.el = vnode.el;
          updateComponentPreRender(instance, next);
        }

        const proxyToUse = instance.proxy;
        const nextTree = normalizeVNode(
          instance.render.call(proxyToUse, proxyToUse)
        );
        // 替换之前的 subTree
        const prevTree = instance.subTree;
        instance.subTree = nextTree;

        // 触发 beforeUpdated hook
        console.log(`${instance.type.name}:触发 beforeUpdated hook`);
        console.log(`${instance.type.name}:触发 onVnodeBeforeUpdate hook`);

        // 用旧的 vnode 和新的 vnode 交给 patch 来处理
        patch(prevTree, nextTree, prevTree.el, null, instance);

        // 触发 updated hook
        console.log(`${instance.type.name}:触发 updated hook`);
        console.log(`${instance.type.name}:触发 onVnodeUpdated hook`);
      }
    }

    // 在 vue3.2 版本里面是使用的 new ReactiveEffect
    // 至于为什么不直接用 effect ,是因为需要一个 scope  参数来收集所有的 effect
    // 而 effect 这个函数是对外的 api ,是不可以轻易改变参数的,所以会使用  new ReactiveEffect
    // 因为 ReactiveEffect 是内部对象,加一个参数是无所谓的
    // 后面如果要实现 scope 的逻辑的时候 需要改过来
    // 现在就先算了
    instance.update = effect(componentUpdateFn, {
      scheduler: () => {
        // 把 effect 推到微任务的时候在执行
        // queueJob(effect);
        queueJob(instance.update);
      },
    });
  }

  function updateComponentPreRender(instance, nextVNode) {
    // 更新 nextVNode 的组件实例
    // 现在 instance.vnode 是组件实例更新前的
    // 所以之前的 props 就是基于 instance.vnode.props 来获取
    // 接着需要更新 vnode ,方便下一次更新的时候获取到正确的值
    nextVNode.component = instance;
    // TODO 后面更新 props 的时候需要对比
    // const prevProps = instance.vnode.props;
    instance.vnode = nextVNode;
    instance.next = null;

    const { props } = nextVNode;
    console.log("更新组件的 props", props);
    instance.props = props;
    console.log("更新组件的 slots");
    // TODO 更新组件的 slots
    // 需要重置 vnode
  }

  return {
    render,
    createApp: createAppAPI(render),
  };
}

function getSequence(arr: number[]): number[] {
  const p = arr.slice();
  const result = [0];
  let i, j, u, v, c;
  const len = arr.length;
  for (i = 0; i < len; i++) {
    const arrI = arr[i];
    if (arrI !== 0) {
      j = result[result.length - 1];
      if (arr[j] < arrI) {
        p[i] = j;
        result.push(i);
        continue;
      }
      u = 0;
      v = result.length - 1;
      while (u < v) {
        c = (u + v) >> 1;
        if (arr[result[c]] < arrI) {
          u = c + 1;
        } else {
          v = c;
        }
      }
      if (arrI < arr[result[u]]) {
        if (u > 0) {
          p[i] = result[u - 1];
        }
        result[u] = i;
      }
    }
  }
  u = result.length;
  v = result[u - 1];
  while (u-- > 0) {
    result[u] = v;
    v = p[v];
  }
  return result;
}


================================================
FILE: packages/runtime-core/src/scheduler.ts
================================================
const queue: any[] = [];
const activePreFlushCbs: any = [];

const p = Promise.resolve();
let isFlushPending = false;

export function nextTick(fn?) {
  return fn ? p.then(fn) : p;
}

export function queueJob(job) {
  if (!queue.includes(job)) {
    queue.push(job);
    // 执行所有的 job
    queueFlush();
  }
}

function queueFlush() {
  // 如果同时触发了两个组件的更新的话
  // 这里就会触发两次 then (微任务逻辑)
  // 但是着是没有必要的
  // 我们只需要触发一次即可处理完所有的 job 调用
  // 所以需要判断一下 如果已经触发过 nextTick 了
  // 那么后面就不需要再次触发一次 nextTick 逻辑了
  if (isFlushPending) return;
  isFlushPending = true;
  nextTick(flushJobs);
}

export function queuePreFlushCb(cb) {
  queueCb(cb, activePreFlushCbs);
}

function queueCb(cb, activeQueue) {
  // 直接添加到对应的列表内就ok
  // todo 这里没有考虑 activeQueue 是否已经存在 cb 的情况
  // 然后在执行 flushJobs 的时候就可以调用 activeQueue 了
  activeQueue.push(cb);

  // 然后执行队列里面所有的 job
  queueFlush()
}

function flushJobs() {
  isFlushPending = false;

  // 先执行 pre 类型的 job
  // 所以这里执行的job 是在渲染前的
  // 也就意味着执行这里的 job 的时候 页面还没有渲染
  flushPreFlushCbs();

  // 这里是执行 queueJob 的
  // 比如 render 渲染就是属于这个类型的 job
  let job;
  while ((job = queue.shift())) {
    if (job) {
      job();
    }
  }
}

function flushPreFlushCbs() {
  // 执行所有的 pre 类型的 job
  for (let i = 0; i < activePreFlushCbs.length; i++) {
    activePreFlushCbs[i]();
  }
}


================================================
FILE: packages/runtime-core/src/vnode.ts
================================================
import { ShapeFlags } from "@mini-vue/shared";

export { createVNode as createElementVNode }

export const createVNode = function (
  type: any,
  props?: any,
  children?: string | Array<any>
) {
  // 注意 type 有可能是 string 也有可能是对象
  // 如果是对象的话,那么就是用户设置的 options
  // type 为 string 的时候
  // createVNode("div")
  // type 为组件对象的时候
  // createVNode(App)
  const vnode = {
    el: null,
    component: null,
    key: props?.key,
    type,
    props: props || {},
    children,
    shapeFlag: getShapeFlag(type),
  };

  // 基于 children 再次设置 shapeFlag
  if (Array.isArray(children)) {
    vnode.shapeFlag |= ShapeFlags.ARRAY_CHILDREN;
  } else if (typeof children === "string") {
    vnode.shapeFlag |= ShapeFlags.TEXT_CHILDREN;
  }

  normalizeChildren(vnode, children);

  return vnode;
};

export function normalizeChildren(vnode, children) {
  if (typeof children === "object") {
    // 暂时主要是为了标识出 slots_children 这个类型来
    // 暂时我们只有 element 类型和 component 类型的组件
    // 所以我们这里除了 element ,那么只要是 component 的话,那么children 肯定就是 slots 了
    if (vnode.shapeFlag & ShapeFlags.ELEMENT) {
      // 如果是 element 类型的话,那么 children 肯定不是 slots
    } else {
      // 这里就必然是 component 了,
      vnode.shapeFlag |= ShapeFlags.SLOTS_CHILDREN;
    }
  }
}
// 用 symbol 作为唯一标识
export const Text = Symbol("Text");
export const Fragment = Symbol("Fragment");

/**
 * @private
 */
export function createTextVNode(text: string = " ") {
  return createVNode(Text, {}, text);
}

// 标准化 vnode 的格式
// 其目的是为了让 child 支持多种格式
export function normalizeVNode(child) {
  // 暂时只支持处理 child 为 string 和 number 的情况
  if (typeof child === "string" || typeof child === "number") {
    return createVNode(Text, null, String(child));
  } else {
    return child;
  }
}

// 基于 type 来判断是什么类型的组件
function getShapeFlag(type: any) {
  return typeof type === "string"
    ? ShapeFlags.ELEMENT
    : ShapeFlags.STATEFUL_COMPONENT;
}


================================================
FILE: packages/runtime-dom/package.json
================================================
{
  "name": "@mini-vue/runtime-dom",
  "version": "1.0.0",
  "description": "@mini-vue/runtime-dom",
  "module": "dist/shared.esm-bundler.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@mini-vue/runtime-core": "workspace:^1.0.0",
    "@mini-vue/shared": "workspace:^1.0.0"
  }
}


================================================
FILE: packages/runtime-dom/src/index.ts
================================================
// 源码里面这些接口是由 runtime-dom 来实现
// 这里先简单实现

import { isOn } from "@mini-vue/shared";
import { createRenderer } from "@mini-vue/runtime-core";

// 后面也修改成和源码一样的实现
function createElement(type) {
  console.log("CreateElement", type);
  const element = document.createElement(type);
  return element;
}

function createText(text) {
  return document.createTextNode(text);
}

function setText(node, text) {
  node.nodeValue = text;
}

function setElementText(el, text) {
  console.log("SetElementText", el, text);
  el.textContent = text;
}

function patchProp(el, key, preValue, nextValue) {
  // preValue 之前的值
  // 为了之后 update 做准备的值
  // nextValue 当前的值
  console.log(`PatchProp 设置属性:${key} 值:${nextValue}`);
  console.log(`key: ${key} 之前的值是:${preValue}`);

  if (isOn(key)) {
    // 添加事件处理函数的时候需要注意一下
    // 1. 添加的和删除的必须是一个函数,不然的话 删除不掉
    //    那么就需要把之前 add 的函数给存起来,后面删除的时候需要用到
    // 2. nextValue 有可能是匿名函数,当对比发现不一样的时候也可以通过缓存的机制来避免注册多次
    // 存储所有的事件函数
    const invokers = el._vei || (el._vei = {});
    const existingInvoker = invokers[key];
    if (nextValue && existingInvoker) {
      // patch
      // 直接修改函数的值即可
      existingInvoker.value = nextValue;
    } else {
      const eventName = key.slice(2).toLowerCase();
      if (nextValue) {
        const invoker = (invokers[key] = nextValue);
        el.addEventListener(eventName, invoker);
      } else {
        el.removeEventListener(eventName, existingInvoker);
        invokers[key] = undefined;
      }
    }
  } else {
    if (nextValue === null || nextValue === "") {
      el.removeAttribute(key);
    } else {
      el.setAttribute(key, nextValue);
    }
  }
}

function insert(child, parent, anchor = null) {
  console.log("Insert");
  parent.insertBefore(child, anchor);
}

function remove(child) {
  const parent = child.parentNode;
  if (parent) {
    parent.removeChild(child);
  }
}

let renderer;

function ensureRenderer() {
  // 如果 renderer 有值的话,那么以后都不会初始化了
  return (
    renderer ||
    (renderer = createRenderer({
      createElement,
      createText,
      setText,
      setElementText,
      patchProp,
      insert,
      remove,
    }))
  );
}

export const createApp = (...args) => {
  return ensureRenderer().createApp(...args);
};

export * from "@mini-vue/runtime-core"


================================================
FILE: packages/runtime-test/src/index.ts
================================================
// todo
// 实现 render 的渲染接口
// 实现序列化
import { createRenderer } from "@mini-vue/runtime-core";
import { extend } from "@mini-vue/shared";
import { nodeOps } from "./nodeOps";
import { patchProp } from "./patchProp";

export const { render } = createRenderer(extend({ patchProp }, nodeOps));

export * from "./nodeOps";
export * from "./serialize"
export * from '@mini-vue/runtime-core'

================================================
FILE: packages/runtime-test/src/nodeOps.ts
================================================
export const enum NodeTypes {
  ELEMENT = "element",
  TEXT = "TEXT",
}

let nodeId = 0;
// 这个函数会在 runtime-core 初始化 element 的时候调用
function createElement(tag: string) {
  // 如果是基于 dom 的话 那么这里会返回 dom 元素
  // 这里是为了测试 所以只需要反正一个对象就可以了
  // 后面的话 通过这个对象来做测试
  const node = {
    tag,
    id: nodeId++,
    type: NodeTypes.ELEMENT,
    props: {},
    children: [],
    parentNode: null,
  };

  return node;
}

function insert(child, parent) {
  parent.children.push(child);
  child.parentNode = parent;
}

function parentNode(node) {
  return node.parentNode;
}

function setElementText(el, text) {
  el.children = [
    {
      id: nodeId++,
      type: NodeTypes.TEXT,
      text,
      parentNode: el,
    },
  ];
}

export const nodeOps = { createElement, insert, parentNode, setElementText };


================================================
FILE: packages/runtime-test/src/patchProp.ts
================================================
export function patchProp(el, key, prevValue, nextValue) {
  el.props[key] = nextValue;
}


================================================
FILE: packages/runtime-test/src/serialize.ts
================================================
// 把 node 给序列化
// 测试的时候好对比

import { NodeTypes } from "./nodeOps";

// 序列化: 把一个对象给处理成 string (进行流化)
export function serialize(node) {
  if (node.type === NodeTypes.ELEMENT) {
    return serializeElement(node);
  } else {
    return serializeText(node);
  }
}

function serializeText(node) {
  return node.text;
}

export function serializeInner(node) {
  // 把所有节点变成一个string
  return node.children.map((c) => serialize(c)).join(``);
}

function serializeElement(node) {
  // 把 props 处理成字符串
  // 规则:
  // 如果 value 是 null 的话 那么直接返回 ``
  // 如果 value 是 `` 的话,那么返回 key
  // 不然的话返回 key = value(这里的值需要字符串化)
  const props = Object.keys(node.props)
    .map((key) => {
      const value = node.props[key];
      return value == null
        ? ``
        : value === ``
        ? key
        : `${key}=${JSON.stringify(value)}`;
    })
    .filter(Boolean)
    .join(" ");

  console.log("node---------", node.children);
  return `<${node.tag}${props ? ` ${props}` : ``}>${serializeInner(node)}</${
    node.tag
  }>`;
}


================================================
FILE: packages/shared/package.json
================================================
{
  "name": "@mini-vue/shared",
  "version": "1.0.0",
  "description": "@mini-vue/shared",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}


================================================
FILE: packages/shared/src/index.ts
================================================
export * from "../src/shapeFlags";
export * from "../src/toDisplayString";

export const isObject = (val) => {
  return val !== null && typeof val === "object";
};

export const isString = (val) => typeof val === "string";

const camelizeRE = /-(\w)/g;
/**
 * @private
 * 把烤肉串命名方式转换成驼峰命名方式
 */
export const camelize = (str: string): string => {
  return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ""));
};

export const extend = Object.assign;

// 必须是 on+一个大写字母的格式开头
export const isOn = (key) => /^on[A-Z]/.test(key);

export function hasChanged(value, oldValue) {
  return !Object.is(value, oldValue);
}

export function hasOwn(val, key) {
  return Object.prototype.hasOwnProperty.call(val, key);
}

/**
 * @private
 * 首字母大写
 */
export const capitalize = (str: string) =>
  str.charAt(0).toUpperCase() + str.slice(1);

/**
 * @private
 * 添加 on 前缀,并且首字母大写
 */
export const toHandlerKey = (str: string) =>
  str ? `on${capitalize(str)}` : ``;

// 用来匹配 kebab-case 的情况
// 比如 onTest-event 可以匹配到 T
// 然后取到 T 在前面加一个 - 就可以
// \BT 就可以匹配到 T 前面是字母的位置
const hyphenateRE = /\B([A-Z])/g;
/**
 * @private
 */
export const hyphenate = (str: string) =>
  str.replace(hyphenateRE, "-$1").toLowerCase();


================================================
FILE: packages/shared/src/shapeFlags.ts
================================================
// 组件的类型
export const enum ShapeFlags {
    // 最后要渲染的 element 类型
    ELEMENT = 1,
    // 组件类型
    STATEFUL_COMPONENT = 1 << 2,
    // vnode 的 children 为 string 类型
    TEXT_CHILDREN = 1 << 3,
    // vnode 的 children 为数组类型
    ARRAY_CHILDREN = 1 << 4,
    // vnode 的 children 为 slots 类型
    SLOTS_CHILDREN = 1 << 5
  }
  

================================================
FILE: packages/shared/src/toDisplayString.ts
================================================
export const toDisplayString = (val) => {
  return String(val);
};


================================================
FILE: packages/vue/cypress/e2e/apiInject.cy.js
================================================
describe("apiInject", () => {
  it("render", () => {
    cy.visit("http://localhost:3000/example/apiInject/");
    cy.contains("apiInject")
    cy.contains("fooOverride-bar-baz")
  });
});

================================================
FILE: packages/vue/cypress/e2e/componentEmit.cy.js
================================================
describe("componentEmit", () => {
  it("render", () => {
    cy.visit("http://localhost:3000/example/componentEmit/");
    cy.contains("你好")
    cy.contains("child")
  });
});


================================================
FILE: packages/vue/cypress/e2e/componentSlots.cy.js
================================================
describe("componentSlots", () => {
  it("render", () => {
    cy.visit("http://localhost:3000/example/componentSlots/");
    cy.contains("你好");
    cy.get("[data-test='child']").within(() => {
      cy.contains("child");
      cy.contains("我是通过 slot 渲染出来的第一个元素");
      cy.contains("我是通过 slot 渲染出来的第一个元素");
      cy.contains("我可以接收到");
      cy.contains("age: 16");
    });
  });
});





================================================
FILE: packages/vue/cypress/e2e/componentUpdate.cy.js
================================================
describe("componentUpdate", () => {
  it("render", () => {
    cy.visit("http://localhost:3000/example/componentUpdate/");
    cy.contains("child123")
    cy.get("button").click()
    cy.contains("child456")
  });
});

================================================
FILE: packages/vue/cypress/e2e/customRenderer.cy.js
================================================
describe("customRenderer", () => {
  it("render", () => {
    cy.visit("http://localhost:3000/example/customRenderer/");
    cy.get("canvas").should("exist")
  });
});

================================================
FILE: packages/vue/cypress/e2e/getCurrentInstance.cy.js
================================================
describe("currentInstance", () => {
  it("render", () => {
    cy.visit("http://localhost:3000/example/getCurrentInstance/");
    cy.contains("getCurrentInstance")
  });
});

================================================
FILE: packages/vue/cypress/e2e/helloworld.cy.js
================================================
describe("helloworld", () => {
  it("render", () => {
    cy.visit("http://localhost:3000/example/helloWorld/");
    cy.contains("主页");
    cy.contains("hello world:");
    cy.contains("count: 0");
  });
});


================================================
FILE: packages/vue/cypress/e2e/nextTicker.cy.js
================================================
describe("nextTicker", () => {
  it("render", () => {
    cy.visit("http://localhost:3000/example/nextTicker/");
    cy.contains("主页")
    cy.contains("child1")
    cy.contains("count:1")
    cy.contains("child2")
    cy.contains("count:1")
  });
});

================================================
FILE: packages/vue/cypress/e2e/patchChildren.cy.js
================================================
describe("patchChildren", () => {
  it("render", () => {
    cy.visit("http://localhost:3000/example/patchChildren/");

    cy.get("[data-cy='contain']").should("text", "ABCEFG");

    cy.get("button").click();
    
    cy.get("[data-cy='contain']").should("text", "ABECDFG");

    cy.get("button").click();

    cy.get("[data-cy='contain']").should("text", "ABCEFG");
  });
});


================================================
FILE: packages/vue/cypress/fixtures/example.json
================================================
{
  "name": "Using fixtures to represent data",
  "email": "hello@cypress.io",
  "body": "Fixtures are a great way to mock data for responses to routes"
}


================================================
FILE: packages/vue/cypress/support/commands.js
================================================
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

================================================
FILE: packages/vue/cypress/support/e2e.js
================================================
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')

================================================
FILE: packages/vue/cypress.config.js
================================================
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
    video:false
  }
})

================================================
FILE: packages/vue/dist/mini-vue.cjs.js
================================================
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var ShapeFlags;
(function (ShapeFlags) {
    ShapeFlags[ShapeFlags["ELEMENT"] = 1] = "ELEMENT";
    ShapeFlags[ShapeFlags["STATEFUL_COMPONENT"] = 4] = "STATEFUL_COMPONENT";
    ShapeFlags[ShapeFlags["TEXT_CHILDREN"] = 8] = "TEXT_CHILDREN";
    ShapeFlags[ShapeFlags["ARRAY_CHILDREN"] = 16] = "ARRAY_CHILDREN";
    ShapeFlags[ShapeFlags["SLOTS_CHILDREN"] = 32] = "SLOTS_CHILDREN";
})(ShapeFlags || (ShapeFlags = {}));

const toDisplayString = (val) => {
    return String(val);
};

const isObject = (val) => {
    return val !== null && typeof val === "object";
};
const isString = (val) => typeof val === "string";
const camelizeRE = /-(\w)/g;
const camelize = (str) => {
    return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ""));
};
const extend = Object.assign;
const isOn = (key) => /^on[A-Z]/.test(key);
function hasChanged(value, oldValue) {
    return !Object.is(value, oldValue);
}
function hasOwn(val, key) {
    return Object.prototype.hasOwnProperty.call(val, key);
}
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
const toHandlerKey = (str) => str ? `on${capitalize(str)}` : ``;
const hyphenateRE = /\B([A-Z])/g;
const hyphenate = (str) => str.replace(hyphenateRE, "-$1").toLowerCase();

const createVNode = function (type, props, children) {
    const vnode = {
        el: null,
        component: null,
        key: props === null || props === void 0 ? void 0 : props.key,
        type,
        props: props || {},
        children,
        shapeFlag: getShapeFlag(type),
    };
    if (Array.isArray(children)) {
        vnode.shapeFlag |= 16;
    }
    else if (typeof children === "string") {
        vnode.shapeFlag |= 8;
    }
    normalizeChildren(vnode, children);
    return vnode;
};
function normalizeChildren(vnode, children) {
    if (typeof children === "object") {
        if (vnode.shapeFlag & 1) ;
        else {
            vnode.shapeFlag |= 32;
        }
    }
}
const Text = Symbol("Text");
const Fragment = Symbol("Fragment");
function createTextVNode(text = " ") {
    return createVNode(Text, {}, text);
}
function normalizeVNode(child) {
    if (typeof child === "string" || typeof child === "number") {
        return createVNode(Text, null, String(child));
    }
    else {
        return child;
    }
}
function getShapeFlag(type) {
    return typeof type === "string"
        ? 1
        : 4;
}

const h = (type, props = null, children = []) => {
    return createVNode(type, props, children);
};

function createAppAPI(render) {
    return function createApp(rootComponent) {
        const app = {
            _component: rootComponent,
            mount(rootContainer) {
                console.log("基于根组件创建 vnode");
                const vnode = createVNode(rootComponent);
                console.log("调用 render,基于 vnode 进行开箱");
                render(vnode, rootContainer);
            },
        };
        return app;
    };
}

function initProps(instance, rawProps) {
    console.log("initProps");
    instance.props = rawProps;
}

function initSlots(instance, children) {
    const { vnode } = instance;
    console.log("初始化 slots");
    if (vnode.shapeFlag & 32) {
        normalizeObjectSlots(children, (instance.slots = {}));
    }
}
const normalizeSlotValue = (value) => {
    return Array.isArray(value) ? value : [value];
};
const normalizeObjectSlots = (rawSlots, slots) => {
    for (const key in rawSlots) {
        const value = rawSlots[key];
        if (typeof value === "function") {
            slots[key] = (props) => normalizeSlotValue(value(props));
        }
    }
};

function emit(instance, event, ...rawArgs) {
    const props = instance.props;
    let handler = props[toHandlerKey(camelize(event))];
    if (!handler) {
        handler = props[(toHandlerKey(hyphenate(event)))];
    }
    if (handler) {
        handler(...rawArgs);
    }
}

const publicPropertiesMap = {
    $el: (i) => i.vnode.el,
    $emit: (i) => i.emit,
    $slots: (i) => i.slots,
    $props: (i) => i.props,
};
const PublicInstanceProxyHandlers = {
    get({ _: instance }, key) {
        const { setupState, props } = instance;
        console.log(`触发 proxy hook , key -> : ${key}`);
        if (key[0] !== "$") {
            if (hasOwn(setupState, key)) {
                return setupState[key];
            }
            else if (hasOwn(props, key)) {
                return props[key];
            }
        }
        const publicGetter = publicPropertiesMap[key];
        if (publicGetter) {
            return publicGetter(instance);
        }
    },
    set({ _: instance }, key, value) {
        const { setupState } = instance;
        if (hasOwn(setupState, key)) {
            setupState[key] = value;
        }
        return true;
    },
};

function createDep(effects) {
    const dep = new Set(effects);
    return dep;
}

let activeEffect = void 0;
let shouldTrack = false;
const targetMap = new WeakMap();
class ReactiveEffect {
    constructor(fn, scheduler) {
        this.fn = fn;
        this.scheduler = scheduler;
        this.active = true;
        this.deps = [];
        console.log("创建 ReactiveEffect 对象");
    }
    run() {
        console.log("run");
        if (!this.active) {
            return this.fn();
        }
        shouldTrack = true;
        activeEffect = this;
        console.log("执行用户传入的 fn");
        const result = this.fn();
        shouldTrack = false;
        activeEffect = undefined;
        return result;
    }
    stop() {
        if (this.active) {
            cleanupEffect(this);
            if (this.onStop) {
                this.onStop();
            }
            this.active = false;
        }
    }
}
function cleanupEffect(effect) {
    effect.deps.forEach((dep) => {
        dep.delete(effect);
    });
    effect.deps.length = 0;
}
function effect(fn, options = {}) {
    const _effect = new ReactiveEffect(fn);
    extend(_effect, options);
    _effect.run();
    const runner = _effect.run.bind(_effect);
    runner.effect = _effect;
    return runner;
}
function stop(runner) {
    runner.effect.stop();
}
function track(target, type, key) {
    if (!isTracking()) {
        return;
    }
    console.log(`触发 track -> target: ${target} type:${type} key:${key}`);
    let depsMap = targetMap.get(target);
    if (!depsMap) {
        depsMap = new Map();
        targetMap.set(target, depsMap);
    }
    let dep = depsMap.get(key);
    if (!dep) {
        dep = createDep();
        depsMap.set(key, dep);
    }
    trackEffects(dep);
}
function trackEffects(dep) {
    if (!dep.has(activeEffect)) {
        dep.add(activeEffect);
        activeEffect.deps.push(dep);
    }
}
function trigger(target, type, key) {
    let deps = [];
    const depsMap = targetMap.get(target);
    if (!depsMap)
        return;
    const dep = depsMap.get(key);
    deps.push(dep);
    const effects = [];
    deps.forEach((dep) => {
        effects.push(...dep);
    });
    triggerEffects(createDep(effects));
}
function isTracking() {
    return shouldTrack && activeEffect !== undefined;
}
function triggerEffects(dep) {
    for (const effect of dep) {
        if (effect.scheduler) {
            effect.scheduler();
        }
        else {
            effect.run();
        }
    }
}

const get = createGetter();
const set = createSetter();
const readonlyGet = createGetter(true);
const shallowReadonlyGet = createGetter(true, true);
function createGetter(isReadonly = false, shallow = false) {
    return function get(target, key, receiver) {
        const isExistInReactiveMap = () => key === "__v_raw" && receiver === reactiveMap.get(target);
        const isExistInReadonlyMap = () => key === "__v_raw" && receiver === readonlyMap.get(target);
        const isExistInShallowReadonlyMap = () => key === "__v_raw" && receiver === shallowReadonlyMap.get(target);
        if (key === "__v_isReactive") {
            return !isReadonly;
        }
        else if (key === "__v_isReadonly") {
            return isReadonly;
        }
        else if (isExistInReactiveMap() ||
            isExistInReadonlyMap() ||
            isExistInShallowReadonlyMap()) {
            return target;
        }
        const res = Reflect.get(target, key, receiver);
        if (!isReadonly) {
            track(target, "get", key);
        }
        if (shallow) {
            return res;
        }
        if (isObject(res)) {
            return isReadonly ? readonly(res) : reactive(res);
        }
        return res;
    };
}
function createSetter() {
    return function set(target, key, value, receiver) {
        const result = Reflect.set(target, key, value, receiver);
        trigger(target, "set", key);
        return result;
    };
}
const readonlyHandlers = {
    get: readonlyGet,
    set(target, key) {
        console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`, target);
        return true;
    },
};
const mutableHandlers = {
    get,
    set,
};
const shallowReadonlyHandlers = {
    get: shallowReadonlyGet,
    set(target, key) {
        console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`, target);
        return true;
    },
};

const reactiveMap = new WeakMap();
const readonlyMap = new WeakMap();
const shallowReadonlyMap = new WeakMap();
var ReactiveFlags;
(function (ReactiveFlags) {
    ReactiveFlags["IS_REACTIVE"] = "__v_isReactive";
    ReactiveFlags["IS_READONLY"] = "__v_isReadonly";
    ReactiveFlags["RAW"] = "__v_raw";
})(ReactiveFlags || (ReactiveFlags = {}));
function reactive(target) {
    return createReactiveObject(target, reactiveMap, mutableHandlers);
}
function readonly(target) {
    return createReactiveObject(target, readonlyMap, readonlyHandlers);
}
function shallowReadonly(target) {
    return createReactiveObject(target, shallowReadonlyMap, shallowReadonlyHandlers);
}
function isProxy(value) {
    return isReactive(value) || isReadonly(value);
}
function isReadonly(value) {
    return !!value["__v_isReadonly"];
}
function isReactive(value) {
    return !!value["__v_isReactive"];
}
function createReactiveObject(target, proxyMap, baseHandlers) {
    const existingProxy = proxyMap.get(target);
    if (existingProxy) {
        return existingProxy;
    }
    const proxy = new Proxy(target, baseHandlers);
    proxyMap.set(target, proxy);
    return proxy;
}

class RefImpl {
    constructor(value) {
        this.__v_isRef = true;
        this._rawValue = value;
        this._value = convert(value);
        this.dep = createDep();
    }
    get value() {
        trackRefValue(this);
        return this._value;
    }
    set value(newValue) {
        if (hasChanged(newValue, this._rawValue)) {
            this._value = convert(newValue);
            this._rawValue = newValue;
            triggerRefValue(this);
        }
    }
}
function ref(value) {
    return createRef(value);
}
function convert(value) {
    return isObject(value) ? reactive(value) : value;
}
function createRef(value) {
    const refImpl = new RefImpl(value);
    return refImpl;
}
function triggerRefValue(ref) {
    triggerEffects(ref.dep);
}
function trackRefValue(ref) {
    if (isTracking()) {
        trackEffects(ref.dep);
    }
}
const shallowUnwrapHandlers = {
    get(target, key, receiver) {
        return unRef(Reflect.get(target, key, receiver));
    },
    set(target, key, value, receiver) {
        const oldValue = target[key];
        if (isRef(oldValue) && !isRef(value)) {
            return (target[key].value = value);
        }
        else {
            return Reflect.set(target, key, value, receiver);
        }
    },
};
function proxyRefs(objectWithRefs) {
    return new Proxy(objectWithRefs, shallowUnwrapHandlers);
}
function unRef(ref) {
    return isRef(ref) ? ref.value : ref;
}
function isRef(value) {
    return !!value.__v_isRef;
}

class ComputedRefImpl {
    constructor(getter) {
        this._dirty = true;
        this.dep = createDep();
        this.effect = new ReactiveEffect(getter, () => {
            if (this._dirty)
                return;
            this._dirty = true;
            triggerRefValue(this);
        });
    }
    get value() {
        trackRefValue(this);
        if (this._dirty) {
            this._dirty = false;
            this._value = this.effect.run();
        }
        return this._value;
    }
}
function computed(getter) {
    return new ComputedRefImpl(getter);
}

function createComponentInstance(vnode, parent) {
    const instance = {
        type: vnode.type,
        vnode,
        next: null,
        props: {},
        parent,
        provides: parent ? parent.provides : {},
        proxy: null,
        isMounted: false,
        attrs: {},
        slots: {},
        ctx: {},
        setupState: {},
        emit: () => { },
    };
    instance.ctx = {
        _: instance,
    };
    instance.emit = emit.bind(null, instance);
    return instance;
}
function setupComponent(instance) {
    const { props, children } = instance.vnode;
    initProps(instance, props);
    initSlots(instance, children);
    setupStatefulComponent(instance);
}
function setupStatefulComponent(instance) {
    console.log("创建 proxy");
    instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);
    const Component = instance.type;
    const { setup } = Component;
    if (setup) {
        setCurrentInstance(instance);
        const setupContext = createSetupContext(instance);
        const setupResult = setup && setup(shallowReadonly(instance.props), setupContext);
        setCurrentInstance(null);
        handleSetupResult(instance, setupResult);
    }
    else {
        finishComponentSetup(instance);
    }
}
function createSetupContext(instance) {
    console.log("初始化 setup context");
    return {
        attrs: instance.attrs,
        slots: instance.slots,
        emit: instance.emit,
        expose: () => { },
    };
}
function handleSetupResult(instance, setupResult) {
    if (typeof setupResult === "function") {
        instance.render = setupResult;
    }
    else if (typeof setupResult === "object") {
        instance.setupState = proxyRefs(setupResult);
    }
    finishComponentSetup(instance);
}
function finishComponentSetup(instance) {
    const Component = instance.type;
    if (!instance.render) {
        if (compile && !Component.render) {
            if (Component.template) {
                const template = Component.template;
                Component.render = compile(template);
            }
        }
        instance.render = Component.render;
    }
}
let currentInstance = {};
function getCurrentInstance() {
    return currentInstance;
}
function setCurrentInstance(instance) {
    currentInstance = instance;
}
let compile;
function registerRuntimeCompiler(_compile) {
    compile = _compile;
}

function provide(key, value) {
    var _a;
    const currentInstance = getCurrentInstance();
    if (currentInstance) {
        let { provides } = currentInstance;
        const parentProvides = (_a = currentInstance.parent) === null || _a === void 0 ? void 0 : _a.provides;
        if (parentProvides === provides) {
            provides = currentInstance.provides = Object.create(parentProvides);
        }
        provides[key] = value;
    }
}
function inject(key, defaultValue) {
    var _a;
    const currentInstance = getCurrentInstance();
    if (currentInstance) {
        const provides = (_a = currentInstance.parent) === null || _a === void 0 ? void 0 : _a.provides;
        if (key in provides) {
            return provides[key];
        }
        else if (defaultValue) {
            if (typeof defaultValue === "function") {
                return defaultValue();
            }
            return defaultValue;
        }
    }
}

function renderSlot(slots, name, props = {}) {
    const slot = slots[name];
    console.log(`渲染插槽 slot -> ${name}`);
    if (slot) {
        const slotContent = slot(props);
        return createVNode(Fragment, {}, slotContent);
    }
}

const queue = [];
const activePreFlushCbs = [];
const p = Promise.resolve();
let isFlushPending = false;
function nextTick(fn) {
    return fn ? p.then(fn) : p;
}
function queueJob(job) {
    if (!queue.includes(job)) {
        queue.push(job);
        queueFlush();
    }
}
function queueFlush() {
    if (isFlushPending)
        return;
    isFlushPending = true;
    nextTick(flushJobs);
}
function queuePreFlushCb(cb) {
    queueCb(cb, activePreFlushCbs);
}
function queueCb(cb, activeQueue) {
    activeQueue.push(cb);
    queueFlush();
}
function flushJobs() {
    isFlushPending = false;
    flushPreFlushCbs();
    let job;
    while ((job = queue.shift())) {
        if (job) {
            job();
        }
    }
}
function flushPreFlushCbs() {
    for (let i = 0; i < activePreFlushCbs.length; i++) {
        activePreFlushCbs[i]();
    }
}

function shouldUpdateComponent(prevVNode, nextVNode) {
    const { props: prevProps } = prevVNode;
    const { props: nextProps } = nextVNode;
    if (prevProps === nextProps) {
        return false;
    }
    if (!prevProps) {
        return !!nextProps;
    }
    if (!nextProps) {
        return true;
    }
    return hasPropsChanged(prevProps, nextProps);
}
function hasPropsChanged(prevProps, nextProps) {
    const nextKeys = Object.keys(nextProps);
    if (nextKeys.length !== Object.keys(prevProps).length) {
        return true;
    }
    for (let i = 0; i < nextKeys.length; i++) {
        const key = nextKeys[i];
        if (nextProps[key] !== prevProps[key]) {
            return true;
        }
    }
    return false;
}

function createRenderer(options) {
    const { createElement: hostCreateElement, setElementText: hostSetElementText, patchProp: hostPatchProp, insert: hostInsert, remove: hostRemove, setText: hostSetText, createText: hostCreateText, } = options;
    const render = (vnode, container) => {
        console.log("调用 patch");
        patch(null, vnode, container);
    };
    function patch(n1, n2, container = null, anchor = null, parentComponent = null) {
        const { type, shapeFlag } = n2;
        switch (type) {
            case Text:
                processText(n1, n2, container);
                break;
            case Fragment:
                processFragment(n1, n2, container);
                break;
            default:
                if (shapeFlag & 1) {
                    console.log("处理 element");
                    processElement(n1, n2, container, anchor, parentComponent);
                }
                else if (shapeFlag & 4) {
                    console.log("处理 component");
                    processComponent(n1, n2, container, parentComponent);
                }
        }
    }
    function processFragment(n1, n2, container) {
        if (!n1) {
            console.log("初始化 Fragment 类型的节点");
            mountChildren(n2.children, container);
        }
    }
    function processText(n1, n2, container) {
        console.log("处理 Text 节点");
        if (n1 === null) {
            console.log("初始化 Text 类型的节点");
            hostInsert((n2.el = hostCreateText(n2.children)), container);
        }
        else {
            const el = (n2.el = n1.el);
            if (n2.children !== n1.children) {
                console.log("更新 Text 类型的节点");
                hostSetText(el, n2.children);
            }
        }
    }
    function processElement(n1, n2, container, anchor, parentComponent) {
        if (!n1) {
            mountElement(n2, container, anchor);
        }
        else {
            updateElement(n1, n2, container, anchor, parentComponent);
        }
    }
    function updateElement(n1, n2, container, anchor, parentComponent) {
        const oldProps = (n1 && n1.props) || {};
        const newProps = n2.props || {};
        console.log("应该更新 element");
        console.log("旧的 vnode", n1);
        console.log("新的 vnode", n2);
        const el = (n2.el = n1.el);
        patchProps(el, oldProps, newProps);
        patchChildren(n1, n2, el, anchor, parentComponent);
    }
    function patchProps(el, oldProps, newProps) {
        for (const key in newProps) {
            const prevProp = oldProps[key];
            const nextProp = newProps[key];
            if (prevProp !== nextProp) {
                hostPatchProp(el, key, prevProp, nextProp);
            }
        }
        for (const key in oldProps) {
            const prevProp = oldProps[key];
            const nextProp = null;
            if (!(key in newProps)) {
                hostPatchProp(el, key, prevProp, nextProp);
            }
        }
    }
    function patchChildren(n1, n2, container, anchor, parentComponent) {
        const { shapeFlag: prevShapeFlag, children: c1 } = n1;
        const { shapeFlag, children: c2 } = n2;
        if (shapeFlag & 8) {
            if (c2 !== c1) {
                console.log("类型为 text_children, 当前需要更新");
                hostSetElementText(container, c2);
            }
        }
        else {
            if (prevShapeFlag & 8) {
                hostSetElementText(container, "");
                mountChildren(c2, container);
            }
            else {
                patchKeyedChildren(c1, c2, container, parentComponent, anchor);
            }
        }
    }
    function patchKeyedChildren(c1, c2, container, parentAnchor, parentComponent) {
        let i = 0;
        const l2 = c2.length;
        let e1 = c1.length - 1;
        let e2 = l2 - 1;
        const isSameVNodeType = (n1, n2) => {
            return n1.type === n2.type && n1.key === n2.key;
        };
        while (i <= e1 && i <= e2) {
            const prevChild = c1[i];
            const nextChild = c2[i];
            if (!isSameVNodeType(prevChild, nextChild)) {
                console.log("两个 child 不相等(从左往右比对)");
                console.log(`prevChild:${prevChild}`);
                console.log(`nextChild:${nextChild}`);
                break;
            }
            console.log("两个 child 相等,接下来对比这两个 child 节点(从左往右比对)");
            patch(prevChild, nextChild, container, parentAnchor, parentComponent);
            i++;
        }
        while (i <= e1 && i <= e2) {
            const prevChild = c1[e1];
            const nextChild = c2[e2];
            if (!isSameVNodeType(prevChild, nextChild)) {
                console.log("两个 child 不相等(从右往左比对)");
                console.log(`prevChild:${prevChild}`);
                console.log(`nextChild:${nextChild}`);
                break;
            }
            console.log("两个 child 相等,接下来对比这两个 child 节点(从右往左比对)");
            patch(prevChild, nextChild, container, parentAnchor, parentComponent);
            e1--;
            e2--;
        }
        if (i > e1 && i <= e2) {
            const nextPos = e2 + 1;
            const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;
            while (i <= e2) {
                console.log(`需要新创建一个 vnode: ${c2[i].key}`);
                patch(null, c2[i], container, anchor, parentComponent);
                i++;
            }
        }
        else if (i > e2 && i <= e1) {
            while (i <= e1) {
                console.log(`需要删除当前的 vnode: ${c1[i].key}`);
                hostRemove(c1[i].el);
                i++;
            }
        }
        else {
            let s1 = i;
            let s2 = i;
            const keyToNewIndexMap = new Map();
            let moved = false;
            let maxNewIndexSoFar = 0;
            for (let i = s2; i <= e2; i++) {
                const nextChild = c2[i];
                keyToNewIndexMap.set(nextChild.key, i);
            }
            const toBePatched = e2 - s2 + 1;
            let patched = 0;
            const newIndexToOldIndexMap = new Array(toBePatched);
            for (let i = 0; i < toBePatched; i++)
                newIndexToOldIndexMap[i] = 0;
            for (i = s1; i <= e1; i++) {
                const prevChild = c1[i];
                if (patched >= toBePatched) {
                    hostRemove(prevChild.el);
                    continue;
                }
                let newIndex;
                if (prevChild.key != null) {
                    newIndex = keyToNewIndexMap.get(prevChild.key);
                }
                else {
                    for (let j = s2; j <= e2; j++) {
                        if (isSameVNodeType(prevChild, c2[j])) {
                            newIndex = j;
                            break;
                        }
                    }
                }
                if (newIndex === undefined) {
                    hostRemove(prevChild.el);
                }
                else {
                    console.log("新老节点都存在");
                    newIndexToOldIndexMap[newIndex - s2] = i + 1;
                    if (newIndex >= maxNewIndexSoFar) {
                        maxNewIndexSoFar = newIndex;
                    }
                    else {
                        moved = true;
                    }
                    patch(prevChild, c2[newIndex], container, null, parentComponent);
                    patched++;
                }
            }
            const increasingNewIndexSequence = moved
                ? getSequence(newIndexToOldIndexMap)
                : [];
            let j = increasingNewIndexSequence.length - 1;
            for (let i = toBePatched - 1; i >= 0; i--) {
                const nextIndex = s2 + i;
                const nextChild = c2[nextIndex];
                const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;
                if (newIndexToOldIndexMap[i] === 0) {
                    patch(null, nextChild, container, anchor, parentComponent);
                }
                else if (moved) {
                    if (j < 0 || increasingNewIndexSequence[j] !== i) {
                        hostInsert(nextChild.el, container, anchor);
                    }
                    else {
                        j--;
                    }
                }
            }
        }
    }
    function mountElement(vnode, container, anchor) {
        const { shapeFlag, props } = vnode;
        const el = (vnode.el = hostCreateElement(vnode.type));
        if (shapeFlag & 8) {
            console.log(`处理文本:${vnode.children}`);
            hostSetElementText(el, vnode.children);
        }
        else if (shapeFlag & 16) {
            mountChildren(vnode.children, el);
        }
        if (props) {
            for (const key in props) {
                const nextVal = props[key];
                hostPatchProp(el, key, null, nextVal);
            }
        }
        console.log("vnodeHook  -> onVnodeBeforeMount");
        console.log("DirectiveHook  -> beforeMount");
        console.log("transition  -> beforeEnter");
        hostInsert(el, container, anchor);
        console.log("vnodeHook  -> onVnodeMounted");
        console.log("DirectiveHook  -> mounted");
        console.log("transition  -> enter");
    }
    function mountChildren(children, container) {
        children.forEach((VNodeChild) => {
            console.log("mountChildren:", VNodeChild);
            patch(null, VNodeChild, container);
        });
    }
    function processComponent(n1, n2, container, parentComponent) {
        if (!n1) {
            mountComponent(n2, container, parentComponent);
        }
        else {
            updateComponent(n1, n2);
        }
    }
    function updateComponent(n1, n2, container) {
        console.log("更新组件", n1, n2);
        const instance = (n2.component = n1.component);
        if (shouldUpdateComponent(n1, n2)) {
            console.log(`组件需要更新: ${instance}`);
            instance.next = n2;
            instance.update();
        }
        else {
            console.log(`组件不需要更新: ${instance}`);
            n2.component = n1.component;
            n2.el = n1.el;
            instance.vnode = n2;
        }
    }
    function mountComponent(initialVNode, container, parentComponent) {
        const instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent));
        console.log(`创建组件实例:${instance.type.name}`);
        setupComponent(instance);
        setupRenderEffect(instance, initialVNode, container);
    }
    function setupRenderEffect(instance, initialVNode, container) {
        function componentUpdateFn() {
            if (!instance.isMounted) {
                console.log(`${instance.type.name}:调用 render,获取 subTree`);
                const proxyToUse = instance.proxy;
                const subTree = (instance.subTree = normalizeVNode(instance.render.call(proxyToUse, proxyToUse)));
                console.log("subTree", subTree);
                console.log(`${instance.type.name}:触发 beforeMount hook`);
                console.log(`${instance.type.name}:触发 onVnodeBeforeMount hook`);
                patch(null, subTree, container, null, instance);
                initialVNode.el = subTree.el;
                console.log(`${instance.type.name}:触发 mounted hook`);
                instance.isMounted = true;
            }
            else {
                console.log(`${instance.type.name}:调用更新逻辑`);
                const { next, vnode } = instance;
                if (next) {
                    next.el = vnode.el;
                    updateComponentPreRender(instance, next);
                }
                const proxyToUse = instance.proxy;
                const nextTree = normalizeVNode(instance.render.call(proxyToUse, proxyToUse));
                const prevTree = instance.subTree;
                instance.subTree = nextTree;
                console.log(`${instance.type.name}:触发 beforeUpdated hook`);
                console.log(`${instance.type.name}:触发 onVnodeBeforeUpdate hook`);
                patch(prevTree, nextTree, prevTree.el, null, instance);
                console.log(`${instance.type.name}:触发 updated hook`);
                console.log(`${instance.type.name}:触发 onVnodeUpdated hook`);
            }
        }
        instance.update = effect(componentUpdateFn, {
            scheduler: () => {
                queueJob(instance.update);
            },
        });
    }
    function updateComponentPreRender(instance, nextVNode) {
        nextVNode.component = instance;
        instance.vnode = nextVNode;
        instance.next = null;
        const { props } = nextVNode;
        console.log("更新组件的 props", props);
        instance.props = props;
        console.log("更新组件的 slots");
    }
    return {
        render,
        createApp: createAppAPI(render),
    };
}
function getSequence(arr) {
    const p = arr.slice();
    const result = [0];
    let i, j, u, v, c;
    const len = arr.length;
    for (i = 0; i < len; i++) {
        const arrI = arr[i];
        if (arrI !== 0) {
            j = result[result.length - 1];
            if (arr[j] < arrI) {
                p[i] = j;
                result.push(i);
                continue;
            }
            u = 0;
            v = result.length - 1;
            while (u < v) {
                c = (u + v) >> 1;
                if (arr[result[c]] < arrI) {
                    u = c + 1;
                }
                else {
                    v = c;
                }
            }
            if (arrI < arr[result[u]]) {
                if (u > 0) {
                    p[i] = result[u - 1];
                }
                result[u] = i;
            }
        }
    }
    u = result.length;
    v = result[u - 1];
    while (u-- > 0) {
        result[u] = v;
        v = p[v];
    }
    return result;
}

function watchEffect(effect) {
    return doWatch(effect);
}
function doWatch(source) {
    const job = () => {
        effect.run();
    };
    const scheduler = () => queuePreFlushCb(job);
    let cleanup;
    const onCleanup = (fn) => {
        cleanup = effect.onStop = () => {
            fn();
        };
    };
    const getter = () => {
        if (cleanup) {
            cleanup();
        }
        source(onCleanup);
    };
    const effect = new ReactiveEffect(getter, scheduler);
    effect.run();
    return () => {
        effect.stop();
    };
}

function createElement(type) {
    console.log("CreateElement", type);
    const element = document.createElement(type);
    return element;
}
function createText(text) {
    return document.createTextNode(text);
}
function setText(node, text) {
    node.nodeValue = text;
}
function setElementText(el, text) {
    console.log("SetElementText", el, text);
    el.textContent = text;
}
function patchProp(el, key, preValue, nextValue) {
    console.log(`PatchProp 设置属性:${key} 值:${nextValue}`);
    console.log(`key: ${key} 之前的值是:${preValue}`);
    if (isOn(key)) {
        const invokers = el._vei || (el._vei = {});
        const existingInvoker = invokers[key];
        if (nextValue && existingInvoker) {
            existingInvoker.value = nextValue;
        }
        else {
            const eventName = key.slice(2).toLowerCase();
            if (nextValue) {
                const invoker = (invokers[key] = nextValue);
                el.addEventListener(eventName, invoker);
            }
            else {
                el.removeEventListener(eventName, existingInvoker);
                invokers[key] = undefined;
            }
        }
    }
    else {
        if (nextValue === null || nextValue === "") {
            el.removeAttribute(key);
        }
        else {
            el.setAttribute(key, nextValue);
        }
    }
}
function insert(child, parent, anchor = null) {
    console.log("Insert");
    parent.insertBefore(child, anchor);
}
function remove(child) {
    const parent = child.parentNode;
    if (parent) {
        parent.removeChild(child);
    }
}
let renderer;
function ensureRenderer() {
    return (renderer ||
        (renderer = createRenderer({
            createElement,
            createText,
            setText,
            setElementText,
            patchProp,
            insert,
            remove,
        })));
}
const createApp = (...args) => {
    return ensureRenderer().createApp(...args);
};

var runtimeDom = /*#__PURE__*/Object.freeze({
    __proto__: null,
    createApp: createApp,
    getCurrentInstance: getCurrentInstance,
    registerRuntimeCompiler: registerRuntimeCompiler,
    inject: inject,
    provide: provide,
    renderSlot: renderSlot,
    createTextVNode: createTextVNode,
    createElementVNode: createVNode,
    createRenderer: createRenderer,
    toDisplayString: toDisplayString,
    watchEffect: watchEffect,
    reactive: reactive,
    ref: ref,
    readonly: readonly,
    unRef: unRef,
    proxyRefs: proxyRefs,
    isReadonly: isReadonly,
    isReactive: isReactive,
    isProxy: isProxy,
    isRef: isRef,
    shallowReadonly: shallowReadonly,
    effect: effect,
    stop: stop,
    computed: computed,
    h: h,
    createAppAPI: createAppAPI
});

const TO_DISPLAY_STRING = Symbol(`toDisplayString`);
const CREATE_ELEMENT_VNODE = Symbol("createElementVNode");
const helperNameMap = {
    [TO_DISPLAY_STRING]: "toDisplayString",
    [CREATE_ELEMENT_VNODE]: "createElementVNode"
};

function generate(ast, options = {}) {
    const context = createCodegenContext(ast, options);
    const { push, mode } = context;
    if (mode === "module") {
        genModulePreamble(ast, context);
    }
    else {
        genFunctionPreamble(ast, context);
    }
    const functionName = "render";
    const args = ["_ctx"];
    const signature = args.join(", ");
    push(`function ${functionName}(${signature}) {`);
    push("return ");
    genNode(ast.codegenNode, context);
    push("}");
    return {
        code: context.code,
    };
}
function genFunctionPreamble(ast, context) {
    const { runtimeGlobalName, push, newline } = context;
    const VueBinging = runtimeGlobalName;
    const aliasHelper = (s) => `${helperNameMap[s]} : _${helperNameMap[s]}`;
    if (ast.helpers.length > 0) {
        push(`
        const { ${ast.helpers.map(aliasHelper).join(", ")}} = ${VueBinging} 

      `);
    }
    newline();
    push(`return `);
}
function genNode(node, context) {
    switch (node.type) {
        case 2:
            genInterpolation(node, context);
            break;
        case 3:
            genExpression(node, context);
            break;
        case 4:
            genElement(node, context);
            break;
        case 5:
            genCompoundExpression(node, context);
            break;
        case 0:
            genText(node, context);
            break;
    }
}
function genCompoundExpression(node, context) {
    const { push } = context;
    for (let i = 0; i < node.children.length; i++) {
        const child = node.children[i];
        if (isString(child)) {
            push(child);
        }
        else {
            genNode(child, context);
        }
    }
}
function genText(node, context) {
    const { push } = context;
    push(`'${node.content}'`);
}
function genElement(node, context) {
    const { push, helper } = context;
    const { tag, props, children } = node;
    push(`${helper(CREATE_ELEMENT_VNODE)}(`);
    genNodeList(genNullableArgs([tag, props, children]), context);
    push(`)`);
}
function genNodeList(nodes, context) {
    const { push } = context;
    for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
        if (isString(node)) {
            push(`${node}`);
        }
        else {
            genNode(node, context);
        }
        if (i < nodes.length - 1) {
            push(", ");
        }
    }
}
function genNullableArgs(args) {
    let i = args.length;
    while (i--) {
        if (args[i] != null)
            break;
    }
    return args.slice(0, i + 1).map((arg) => arg || "null");
}
function genExpression(node, context) {
    context.push(node.content, node);
}
function genInterpolation(node, context) {
    const { push, helper } = context;
    push(`${helper(TO_DISPLAY_STRING)}(`);
    genNode(node.content, context);
    push(")");
}
function genModulePreamble(ast, context) {
    const { push, newline, runtimeModuleName } = context;
    if (ast.helpers.length) {
        const code = `import {${ast.helpers
            .map((s) => `${helperNameMap[s]} as _${helperNameMap[s]}`)
            .join(", ")} } from ${JSON.stringify(runtimeModuleName)}`;
        push(code);
    }
    newline();
    push(`export `);
}
function createCodegenContext(ast, { runtimeModuleName = "vue", runtimeGlobalName = "Vue", mode = "function" }) {
    const context = {
        code: "",
        mode,
        runtimeModuleName,
        runtimeGlobalName,
        helper(key) {
            return `_${helperNameMap[key]}`;
        },
        push(code) {
            context.code += code;
        },
        newline() {
            context.code += "\n";
        },
    };
    return context;
}

var TagType;
(function (TagType) {
    TagType[TagType["Start"] = 0] = "Start";
    TagType[TagType["End"] = 1] = "End";
})(TagType || (TagType = {}));
function baseParse(content) {
    const context = createParserContext(content);
    return createRoot(parseChildren(context, []));
}
function createParserContext(content) {
    console.log("创建 paserContext");
    return {
        source: content,
    };
}
function parseChildren(context, ancestors) {
    console.log("开始解析 children");
    const nodes = [];
    while (!isEnd(context, ancestors)) {
        let node;
        const s = context.source;
        if (startsWith(s, "{{")) {
            node = parseInterpolation(context);
        }
        else if (s[0] === "<") {
            if (s[1] === "/") {
                if (/[a-z]/i.test(s[2])) {
                    parseTag(context, 1);
                    continue;
                }
            }
            else if (/[a-z]/i.test(s[1])) {
                node = parseElement(context, ancestors);
            }
        }
        if (!node) {
            node = parseText(context);
        }
        nodes.push(node);
    }
    return nodes;
}
function isEnd(context, ancestors) {
    const s = context.source;
    if (context.source.startsWith("</")) {
        for (let i = ancestors.length - 1; i >= 0; --i) {
            if (startsWithEndTagOpen(s, ancestors[i].tag)) {
                return true;
            }
        }
    }
    return !context.source;
}
function parseElement(context, ancestors) {
    const element = parseTag(context, 0);
    ancestors.push(element);
    const children = parseChildren(context, ancestors);
    ancestors.pop();
    if (startsWithEndTagOpen(context.source, element.tag)) {
        parseTag(context, 1);
    }
    else {
        throw new Error(`缺失结束标签:${element.tag}`);
    }
    element.children = children;
    return element;
}
function startsWithEndTagOpen(source, tag) {
    return (startsWith(source, "</") &&
        source.slice(2, 2 + tag.length).toLowerCase() === tag.toLowerCase());
}
function parseTag(context, type) {
    const match = /^<\/?([a-z][^\r\n\t\f />]*)/i.exec(context.source);
    const tag = match[1];
    advanceBy(context, match[0].length);
    advanceBy(context, 1);
    if (type === 1)
        return;
    let tagType = 0;
    return {
        type: 4,
        tag,
        tagType,
    };
}
function parseInterpolation(context) {
    const openDelimiter = "{{";
    const closeDelimiter = "}}";
    const closeIndex = context.source.indexOf(closeDelimiter, openDelimiter.length);
    advanceBy(context, 2);
    const rawContentLength = closeIndex - openDelimiter.length;
    const rawContent = context.source.slice(0, rawContentLength);
    const preTrimContent = parseTextData(context, rawContent.length);
    const content = preTrimContent.trim();
    advanceBy(context, closeDelimiter.length);
    return {
        type: 2,
        content: {
            type: 3,
            content,
        },
    };
}
function parseText(context) {
    console.log("解析 text", context);
    const endTokens = ["<", "{{"];
    let endIndex = context.source.length;
    for (let i = 0; i < endTokens.length; i++) {
        const index = context.source.indexOf(endTokens[i]);
        if (index !== -1 && endIndex > index) {
            endIndex = index;
        }
    }
    const content = parseTextData(context, endIndex);
    return {
        type: 0,
        content,
    };
}
function parseTextData(context, length) {
    console.log("解析 textData");
    const rawText = context.source.slice(0, length);
    advanceBy(context, length);
    return rawText;
}
function advanceBy(context, numberOfCharacters) {
    console.log("推进代码", context, numberOfCharacters);
    context.source = context.source.slice(numberOfCharacters);
}
function createRoot(children) {
    return {
        type: 1,
        children,
        helpers: [],
    };
}
function startsWith(source, searchString) {
    return source.startsWith(searchString);
}

function transform(root, options = {}) {
    const context = createTransformContext(root, options);
    traverseNode(root, context);
    createRootCodegen(root);
    root.helpers.push(...context.helpers.keys());
}
function traverseNode(node, context) {
    const type = node.type;
    const nodeTransforms = context.nodeTransforms;
    const exitFns = [];
    for (let i = 0; i < nodeTransforms.length; i++) {
        const transform = nodeTransforms[i];
        const onExit = transform(node, context);
        if (onExit) {
            exitFns.push(onExit);
        }
    }
    switch (type) {
        case 2:
            context.helper(TO_DISPLAY_STRING);
            break;
        case 1:
        case 4:
            traverseChildren(node, context);
            break;
    }
    let i = exitFns.length;
    while (i--) {
        exitFns[i]();
    }
}
function traverseChildren(parent, context) {
    parent.children.forEach((node) => {
        traverseNode(node, context);
    });
}
function createTransformContext(root, options) {
    const context = {
        root,
        nodeTransforms: options.nodeTransforms || [],
        helpers: new Map(),
        helper(name) {
            const count = context.helpers.get(name) || 0;
            context.helpers.set(name, count + 1);
        },
    };
    return context;
}
function createRootCodegen(root, context) {
    const { children } = root;
    const child = children[0];
    if (child.type === 4 && child.codegenNode) {
        const codegenNode = child.codegenNode;
        root.codegenNode = codegenNode;
    }
    else {
        root.codegenNode = child;
    }
}

function transformExpression(node) {
    if (node.type === 2) {
        node.content = processExpression(node.content);
    }
}
function processExpression(node) {
    node.content = `_ctx.${node.content}`;
    return node;
}

var NodeTypes;
(function (NodeTypes) {
    NodeTypes[NodeTypes["TEXT"] = 0] = "TEXT";
    NodeTypes[NodeTypes["ROOT"] = 1] = "ROOT";
    NodeTypes[NodeTypes["INTERPOLATION"] = 2] = "INTERPOLATION";
    NodeTypes[NodeTypes["SIMPLE_EXPRESSION"] = 3] = "SIMPLE_EXPRESSION";
    NodeTypes[NodeTypes["ELEMENT"] = 4] = "ELEMENT";
    NodeTypes[NodeTypes["COMPOUND_EXPRESSION"] = 5] = "COMPOUND_EXPRESSION";
})(NodeTypes || (NodeTypes = {}));
var ElementTypes;
(function (ElementTypes) {
    ElementTypes[ElementTypes["ELEMENT"] = 0] = "ELEMENT";
})(ElementTypes || (ElementTypes = {}));
function createVNodeCall(context, tag, props, children) {
    if (context) {
        context.helper(CREATE_ELEMENT_VNODE);
    }
    return {
        type: 4,
        tag,
        props,
        children,
    };
}

function transformElement(node, context) {
    if (node.type === 4) {
        return () => {
            const vnodeTag = `'${node.tag}'`;
            const vnodeProps = null;
            let vnodeChildren = null;
            if (node.children.length > 0) {
                if (node.children.length === 1) {
                    const child = node.children[0];
                    vnodeChildren = child;
                }
            }
            node.codegenNode = createVNodeCall(context, vnodeTag, vnodeProps, vnodeChildren);
        };
    }
}

function isText(node) {
    return node.type === 2 || node.type === 0;
}

function transformText(node, context) {
    if (node.type === 4) {
        return () => {
            const children = node.children;
            let currentContainer;
            for (let i = 0; i < children.length; i++) {
                const child = children[i];
                if (isText(child)) {
                    for (let j = i + 1; j < children.length; j++) {
                        const next = children[j];
                        if (isText(next)) {
                            if (!currentContainer) {
                                currentContainer = children[i] = {
                                    type: 5,
                                    loc: child.loc,
                                    children: [child],
                                };
                            }
                            currentContainer.children.push(` + `, next);
                            children.splice(j, 1);
                            j--;
                        }
                        else {
                            currentContainer = undefined;
                            break;
                        }
                    }
                }
            }
        };
    }
}

function baseCompile(template, options) {
    const ast = baseParse(template);
    transform(ast, Object.assign(options, {
        nodeTransforms: [transformElement, transformText, transformExpression],
    }));
    return generate(ast);
}

function compileToFunction(template, options = {}) {
    const { code } = baseCompile(template, options);
    const render = new Function("Vue", code)(runtimeDom);
    return render;
}
registerRuntimeCompiler(compileToFunction);

exports.computed = computed;
exports.createApp = createApp;
exports.createAppAPI = createAppAPI;
exports.createElementVNode = createVNode;
exports.createRenderer = createRenderer;
exports.createTextVNode = createTextVNode;
exports.effect = effect;
exports.getCurrentInstance = getCurrentInstance;
exports.h = h;
exports.inject = inject;
exports.isProxy = isProxy;
exports.isReactive = isReactive;
exports.isReadonly = isReadonly;
exports.isRef = isRef;
exports.provide = provide;
exports.proxyRefs = proxyRefs;
exports.reactive = reactive;
exports.readonly = readonly;
exports.ref = ref;
exports.registerRuntimeCompiler = registerRuntimeCompiler;
exports.renderSlot = renderSlot;
exports.shallowReadonly = shallowReadonly;
exports.stop = stop;
exports.toDisplayString = toDisplayString;
exports.unRef = unRef;
exports.watchEffect = watchEffect;
//# sourceMappingURL=mini-vue.cjs.js.map


================================================
FILE: packages/vue/dist/mini-vue.esm-bundler.js
================================================
var ShapeFlags;
(function (ShapeFlags) {
    ShapeFlags[ShapeFlags["ELEMENT"] = 1] = "ELEMENT";
    ShapeFlags[ShapeFlags["STATEFUL_COMPONENT"] = 4] = "STATEFUL_COMPONENT";
    ShapeFlags[ShapeFlags["TEXT_CHILDREN"] = 8] = "TEXT_CHILDREN";
    ShapeFlags[ShapeFlags["ARRAY_CHILDREN"] = 16] = "ARRAY_CHILDREN";
    ShapeFlags[ShapeFlags["SLOTS_CHILDREN"] = 32] = "SLOTS_CHILDREN";
})(ShapeFlags || (ShapeFlags = {}));

const toDisplayString = (val) => {
    return String(val);
};

const isObject = (val) => {
    return val !== null && typeof val === "object";
};
const isString = (val) => typeof val === "string";
const camelizeRE = /-(\w)/g;
const camelize = (str) => {
    return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ""));
};
const extend = Object.assign;
const isOn = (key) => /^on[A-Z]/.test(key);
function hasChanged(value, oldValue) {
    return !Object.is(value, oldValue);
}
function hasOwn(val, key) {
    return Object.prototype.hasOwnProperty.call(val, key);
}
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
const toHandlerKey = (str) => str ? `on${capitalize(str)}` : ``;
const hyphenateRE = /\B([A-Z])/g;
const hyphenate = (str) => str.replace(hyphenateRE, "-$1").toLowerCase();

const createVNode = function (type, props, children) {
    const vnode = {
        el: null,
        component: null,
        key: props === null || props === void 0 ? void 0 : props.key,
        type,
        props: props || {},
        children,
        shapeFlag: getShapeFlag(type),
    };
    if (Array.isArray(children)) {
        vnode.shapeFlag |= 16;
    }
    else if (typeof children === "string") {
        vnode.shapeFlag |= 8;
    }
    normalizeChildren(vnode, children);
    return vnode;
};
function normalizeChildren(vnode, children) {
    if (typeof children === "object") {
        if (vnode.shapeFlag & 1) ;
        else {
            vnode.shapeFlag |= 32;
        }
    }
}
const Text = Symbol("Text");
const Fragment = Symbol("Fragment");
function createTextVNode(text = " ") {
    return createVNode(Text, {}, text);
}
function normalizeVNode(child) {
    if (typeof child === "string" || typeof child === "number") {
        return createVNode(Text, null, String(child));
    }
    else {
        return child;
    }
}
function getShapeFlag(type) {
    return typeof type === "string"
        ? 1
        : 4;
}

const h = (type, props = null, children = []) => {
    return createVNode(type, props, children);
};

function createAppAPI(render) {
    return function createApp(rootComponent) {
        const app = {
            _component: rootComponent,
            mount(rootContainer) {
                console.log("基于根组件创建 vnode");
                const vnode = createVNode(rootComponent);
                console.log("调用 render,基于 vnode 进行开箱");
                render(vnode, rootContainer);
            },
        };
        return app;
    };
}

function initProps(instance, rawProps) {
    console.log("initProps");
    instance.props = rawProps;
}

function initSlots(instance, children) {
    const { vnode } = instance;
    console.log("初始化 slots");
    if (vnode.shapeFlag & 32) {
        normalizeObjectSlots(children, (instance.slots = {}));
    }
}
const normalizeSlotValue = (value) => {
    return Array.isArray(value) ? value : [value];
};
const normalizeObjectSlots = (rawSlots, slots) => {
    for (const key in rawSlots) {
        const value = rawSlots[key];
        if (typeof value === "function") {
            slots[key] = (props) => normalizeSlotValue(value(props));
        }
    }
};

function emit(instance, event, ...rawArgs) {
    const props = instance.props;
    let handler = props[toHandlerKey(camelize(event))];
    if (!handler) {
        handler = props[(toHandlerKey(hyphenate(event)))];
    }
    if (handler) {
        handler(...rawArgs);
    }
}

const publicPropertiesMap = {
    $el: (i) => i.vnode.el,
    $emit: (i) => i.emit,
    $slots: (i) => i.slots,
    $props: (i) => i.props,
};
const PublicInstanceProxyHandlers = {
    get({ _: instance }, key) {
        const { setupState, props } = instance;
        console.log(`触发 proxy hook , key -> : ${key}`);
        if (key[0] !== "$") {
            if (hasOwn(setupState, key)) {
                return setupState[key];
            }
            else if (hasOwn(props, key)) {
                return props[key];
            }
        }
        const publicGetter = publicPropertiesMap[key];
        if (publicGetter) {
            return publicGetter(instance);
        }
    },
    set({ _: instance }, key, value) {
        const { setupState } = instance;
        if (hasOwn(setupState, key)) {
            setupState[key] = value;
        }
        return true;
    },
};

function createDep(effects) {
    const dep = new Set(effects);
    return dep;
}

let activeEffect = void 0;
let shouldTrack = false;
const targetMap = new WeakMap();
class ReactiveEffect {
    constructor(fn, scheduler) {
        this.fn = fn;
        this.scheduler = scheduler;
        this.active = true;
        this.deps = [];
        console.log("创建 ReactiveEffect 对象");
    }
    run() {
        console.log("run");
        if (!this.active) {
            return this.fn();
        }
        shouldTrack = true;
        activeEffect = this;
        console.log("执行用户传入的 fn");
        const result = this.fn();
        shouldTrack = false;
        activeEffect = undefined;
        return result;
    }
    stop() {
        if (this.active) {
            cleanupEffect(this);
            if (this.onStop) {
                this.onStop();
            }
            this.active = false;
        }
    }
}
function cleanupEffect(effect) {
    effect.deps.forEach((dep) => {
        dep.delete(effect);
    });
    effect.deps.length = 0;
}
function effect(fn, options = {}) {
    const _effect = new ReactiveEffect(fn);
    extend(_effect, options);
    _effect.run();
    const runner = _effect.run.bind(_effect);
    runner.effect = _effect;
    return runner;
}
function stop(runner) {
    runner.effect.stop();
}
function track(target, type, key) {
    if (!isTracking()) {
        return;
    }
    console.log(`触发 track -> target: ${target} type:${type} key:${key}`);
    let depsMap = targetMap.get(target);
    if (!depsMap) {
        depsMap = new Map();
        targetMap.set(target, depsMap);
    }
    let dep = depsMap.get(key);
    if (!dep) {
        dep = createDep();
        depsMap.set(key, dep);
    }
    trackEffects(dep);
}
function trackEffects(dep) {
    if (!dep.has(activeEffect)) {
        dep.add(activeEffect);
        activeEffect.deps.push(dep);
    }
}
function trigger(target, type, key) {
    let deps = [];
    const depsMap = targetMap.get(target);
    if (!depsMap)
        return;
    const dep = depsMap.get(key);
    deps.push(dep);
    const effects = [];
    deps.forEach((dep) => {
        effects.push(...dep);
    });
    triggerEffects(createDep(effects));
}
function isTracking() {
    return shouldTrack && activeEffect !== undefined;
}
function triggerEffects(dep) {
    for (const effect of dep) {
        if (effect.scheduler) {
            effect.scheduler();
        }
        else {
            effect.run();
        }
    }
}

const get = createGetter();
const set = createSetter();
const readonlyGet = createGetter(true);
const shallowReadonlyGet = createGetter(true, true);
function createGetter(isReadonly = false, shallow = false) {
    return function get(target, key, receiver) {
        const isExistInReactiveMap = () => key === "__v_raw" && receiver === reactiveMap.get(target);
        const isExistInReadonlyMap = () => key === "__v_raw" && receiver === readonlyMap.get(target);
        const isExistInShallowReadonlyMap = () => key === "__v_raw" && receiver === shallowReadonlyMap.get(target);
        if (key === "__v_isReactive") {
            return !isReadonly;
        }
        else if (key === "__v_isReadonly") {
            return isReadonly;
        }
        else if (isExistInReactiveMap() ||
            isExistInReadonlyMap() ||
            isExistInShallowReadonlyMap()) {
            return target;
        }
        const res = Reflect.get(target, key, receiver);
        if (!isReadonly) {
            track(target, "get", key);
        }
        if (shallow) {
            return res;
        }
        if (isObject(res)) {
            return isReadonly ? readonly(res) : reactive(res);
        }
        return res;
    };
}
function createSetter() {
    return function set(target, key, value, receiver) {
        const result = Reflect.set(target, key, value, receiver);
        trigger(target, "set", key);
        return result;
    };
}
const readonlyHandlers = {
    get: readonlyGet,
    set(target, key) {
        console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`, target);
        return true;
    },
};
const mutableHandlers = {
    get,
    set,
};
const shallowReadonlyHandlers = {
    get: shallowReadonlyGet,
    set(target, key) {
        console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`, target);
        return true;
    },
};

const reactiveMap = new WeakMap();
const readonlyMap = new WeakMap();
const shallowReadonlyMap = new WeakMap();
var ReactiveFlags;
(function (ReactiveFlags) {
    ReactiveFlags["IS_REACTIVE"] = "__v_isReactive";
    ReactiveFlags["IS_READONLY"] = "__v_isReadonly";
    ReactiveFlags["RAW"] = "__v_raw";
})(ReactiveFlags || (ReactiveFlags = {}));
function reactive(target) {
    return createReactiveObject(target, reactiveMap, mutableHandlers);
}
function readonly(target) {
    return createReactiveObject(target, readonlyMap, readonlyHandlers);
}
function shallowReadonly(target) {
    return createReactiveObject(target, shallowReadonlyMap, shallowReadonlyHandlers);
}
function isProxy(value) {
    return isReactive(value) || isReadonly(value);
}
function isReadonly(value) {
    return !!value["__v_isReadonly"];
}
function isReactive(value) {
    return !!value["__v_isReactive"];
}
function createReactiveObject(target, proxyMap, baseHandlers) {
    const existingProxy = proxyMap.get(target);
    if (existingProxy) {
        return existingProxy;
    }
    const proxy = new Proxy(target, baseHandlers);
    proxyMap.set(target, proxy);
    return proxy;
}

class RefImpl {
    constructor(value) {
        this.__v_isRef = true;
        this._rawValue = value;
        this._value = convert(value);
        this.dep = createDep();
    }
    get value() {
        trackRefValue(this);
        return this._value;
    }
    set value(newValue) {
        if (hasChanged(newValue, this._rawValue)) {
            this._value = convert(newValue);
            this._rawValue = newValue;
            triggerRefValue(this);
        }
    }
}
function ref(value) {
    return createRef(value);
}
function convert(value) {
    return isObject(value) ? reactive(value) : value;
}
function createRef(value) {
    const refImpl = new RefImpl(value);
    return refImpl;
}
function triggerRefValue(ref) {
    triggerEffects(ref.dep);
}
function trackRefValue(ref) {
    if (isTracking()) {
        trackEffects(ref.dep);
    }
}
const shallowUnwrapHandlers = {
    get(target, key, receiver) {
        return unRef(Reflect.get(target, key, receiver));
    },
    set(target, key, value, receiver) {
        const oldValue = target[key];
        if (isRef(oldValue) && !isRef(value)) {
            return (target[key].value = value);
        }
        else {
            return Reflect.set(target, key, value, receiver);
        }
    },
};
function proxyRefs(objectWithRefs) {
    return new Proxy(objectWithRefs, shallowUnwrapHandlers);
}
function unRef(ref) {
    return isRef(ref) ? ref.value : ref;
}
function isRef(value) {
    return !!value.__v_isRef;
}

class ComputedRefImpl {
    constructor(getter) {
        this._dirty = true;
        this.dep = createDep();
        this.effect = new ReactiveEffect(getter, () => {
            if (this._dirty)
                return;
            this._dirty = true;
            triggerRefValue(this);
        });
    }
    get value() {
        trackRefValue(this);
        if (this._dirty) {
            this._dirty = false;
            this._value = this.effect.run();
        }
        return this._value;
    }
}
function computed(getter) {
    return new ComputedRefImpl(getter);
}

function createComponentInstance(vnode, parent) {
    const instance = {
        type: vnode.type,
        vnode,
        next: null,
        props: {},
        parent,
        provides: parent ? parent.provides : {},
        proxy: null,
        isMounted: false,
        attrs: {},
        slots: {},
        ctx: {},
        setupState: {},
        emit: () => { },
    };
    instance.ctx = {
        _: instance,
    };
    instance.emit = emit.bind(null, instance);
    return instance;
}
function setupComponent(instance) {
    const { props, children } = instance.vnode;
    initProps(instance, props);
    initSlots(instance, children);
    setupStatefulComponent(instance);
}
function setupStatefulComponent(instance) {
    console.log("创建 proxy");
    instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);
    const Component = instance.type;
    const { setup } = Component;
    if (setup) {
        setCurrentInstance(instance);
        const setupContext = createSetupContext(instance);
        const setupResult = setup && setup(shallowReadonly(instance.props), setupContext);
        setCurrentInstance(null);
        handleSetupResult(instance, setupResult);
    }
    else {
        finishComponentSetup(instance);
    }
}
function createSetupContext(instance) {
    console.log("初始化 setup context");
    return {
        attrs: instance.attrs,
        slots: instance.slots,
        emit: instance.emit,
        expose: () => { },
    };
}
function handleSetupResult(instance, setupResult) {
    if (typeof setupResult === "function") {
        instance.render = setupResult;
    }
    else if (typeof setupResult === "object") {
        instance.setupState = proxyRefs(setupResult);
    }
    finishComponentSetup(instance);
}
function finishComponentSetup(instance) {
    const Component = instance.type;
    if (!instance.render) {
        if (compile && !Component.render) {
            if (Component.template) {
                const template = Component.template;
                Component.render = compile(template);
            }
        }
        instance.render = Component.render;
    }
}
let currentInstance = {};
function getCurrentInstance() {
    return currentInstance;
}
function setCurrentInstance(instance) {
    currentInstance = instance;
}
let compile;
function registerRuntimeCompiler(_compile) {
    compile = _compile;
}

function provide(key, value) {
    var _a;
    const currentInstance = getCurrentInstance();
    if (currentInstance) {
        let { provides } = currentInstance;
        const parentProvides = (_a = currentInstance.parent) === null || _a === void 0 ? void 0 : _a.provides;
        if (parentProvides === provides) {
            provides = currentInstance.provides = Object.create(parentProvides);
        }
        provides[key] = value;
    }
}
function inject(key, defaultValue) {
    var _a;
    const currentInstance = getCurrentInstance();
    if (currentInstance) {
        const provides = (_a = currentInstance.parent) === null || _a === void 0 ? void 0 : _a.provides;
        if (key in provides) {
            return provides[key];
        }
        else if (defaultValue) {
            if (typeof defaultValue === "function") {
                return defaultValue();
            }
            return defaultValue;
        }
    }
}

function renderSlot(slots, name, props = {}) {
    const slot = slots[name];
    console.log(`渲染插槽 slot -> ${name}`);
    if (slot) {
        const slotContent = slot(props);
        return createVNode(Fragment, {}, slotContent);
    }
}

const queue = [];
const activePreFlushCbs = [];
const p = Promise.resolve();
let isFlushPending = false;
function nextTick(fn) {
    return fn ? p.then(fn) : p;
}
function queueJob(job) {
    if (!queue.includes(job)) {
        queue.push(job);
        queueFlush();
    }
}
function queueFlush() {
    if (isFlushPending)
        return;
    isFlushPending = true;
    nextTick(flushJobs);
}
function queuePreFlushCb(cb) {
    queueCb(cb, activePreFlushCbs);
}
function queueCb(cb, activeQueue) {
    activeQueue.push(cb);
    queueFlush();
}
function flushJobs() {
    isFlushPending = false;
    flushPreFlushCbs();
    let job;
    while ((job = queue.shift())) {
        if (job) {
            job();
        }
    }
}
function flushPreFlushCbs() {
    for (let i = 0; i < activePreFlushCbs.length; i++) {
        activePreFlushCbs[i]();
    }
}

function shouldUpdateComponent(prevVNode, nextVNode) {
    const { props: prevProps } = prevVNode;
    const { props: nextProps } = nextVNode;
    if (prevProps === nextProps) {
        return false;
    }
    if (!prevProps) {
        return !!nextProps;
    }
    if (!nextProps) {
        return true;
    }
    return hasPropsChanged(prevProps, nextProps);
}
function hasPropsChanged(prevProps, nextProps) {
    const nextKeys = Object.keys(nextProps);
    if (nextKeys.length !== Object.keys(prevProps).length) {
        return true;
    }
    for (let i = 0; i < nextKeys.length; i++) {
        const key = nextKeys[i];
        if (nextProps[key] !== prevProps[key]) {
            return true;
        }
    }
    return false;
}

function createRenderer(options) {
    const { createElement: hostCreateElement, setElementText: hostSetElementText, patchProp: hostPatchProp, insert: hostInsert, remove: hostRemove, setText: hostSetText, createText: hostCreateText, } = options;
    const render = (vnode, container) => {
        console.log("调用 patch");
        patch(null, vnode, container);
    };
    function patch(n1, n2, container = null, anchor = null, parentComponent = null) {
        const { type, shapeFlag } = n2;
        switch (type) {
            case Text:
                processText(n1, n2, container);
                break;
            case Fragment:
                processFragment(n1, n2, container);
                break;
            default:
                if (shapeFlag & 1) {
                    console.log("处理 element");
                    processElement(n1, n2, container, anchor, parentComponent);
                }
                else if (shapeFlag & 4) {
                    console.log("处理 component");
                    processComponent(n1, n2, container, parentComponent);
                }
        }
    }
    function processFragment(n1, n2, container) {
        if (!n1) {
            console.log("初始化 Fragment 类型的节点");
            mountChildren(n2.children, container);
        }
    }
    function processText(n1, n2, container) {
        console.log("处理 Text 节点");
        if (n1 === null) {
            console.log("初始化 Text 类型的节点");
            hostInsert((n2.el = hostCreateText(n2.children)), container);
        }
        else {
            const el = (n2.el = n1.el);
            if (n2.children !== n1.children) {
                console.log("更新 Text 类型的节点");
                hostSetText(el, n2.children);
            }
        }
    }
    function processElement(n1, n2, container, anchor, parentComponent) {
        if (!n1) {
            mountElement(n2, container, anchor);
        }
        else {
            updateElement(n1, n2, container, anchor, parentComponent);
        }
    }
    function updateElement(n1, n2, container, anchor, parentComponent) {
        const oldProps = (n1 && n1.props) || {};
        const newProps = n2.props || {};
        console.log("应该更新 element");
        console.log("旧的 vnode", n1);
        console.log("新的 vnode", n2);
        const el = (n2.el = n1.el);
        patchProps(el, oldProps, newProps);
        patchChildren(n1, n2, el, anchor, parentComponent);
    }
    function patchProps(el, oldProps, newProps) {
        for (const key in newProps) {
            const prevProp = oldProps[key];
            const nextProp = newProps[key];
            if (prevProp !== nextProp) {
                hostPatchProp(el, key, prevProp, nextProp);
            }
        }
        for (const key in oldProps) {
            const prevProp = oldProps[key];
            const nextProp = null;
            if (!(key in newProps)) {
                hostPatchProp(el, key, prevProp, nextProp);
            }
        }
    }
    function patchChildren(n1, n2, container, anchor, parentComponent) {
        const { shapeFlag: prevShapeFlag, children: c1 } = n1;
        const { shapeFlag, children: c2 } = n2;
        if (shapeFlag & 8) {
            if (c2 !== c1) {
                console.log("类型为 text_children, 当前需要更新");
                hostSetElementText(container, c2);
            }
        }
        else {
            if (prevShapeFlag & 8) {
                hostSetElementText(container, "");
                mountChildren(c2, container);
            }
            else {
                patchKeyedChildren(c1, c2, container, parentComponent, anchor);
            }
        }
    }
    function patchKeyedChildren(c1, c2, container, parentAnchor, parentComponent) {
        let i = 0;
        const l2 = c2.length;
        let e1 = c1.length - 1;
        let e2 = l2 - 1;
        const isSameVNodeType = (n1, n2) => {
            return n1.type === n2.type && n1.key === n2.key;
        };
        while (i <= e1 && i <= e2) {
            const prevChild = c1[i];
            const nextChild = c2[i];
            if (!isSameVNodeType(prevChild, nextChild)) {
                console.log("两个 child 不相等(从左往右比对)");
                console.log(`prevChild:${prevChild}`);
                console.log(`nextChild:${nextChild}`);
                break;
            }
            console.log("两个 child 相等,接下来对比这两个 child 节点(从左往右比对)");
            patch(prevChild, nextChild, container, parentAnchor, parentComponent);
            i++;
        }
        while (i <= e1 && i <= e2) {
            const prevChild = c1[e1];
            const nextChild = c2[e2];
            if (!isSameVNodeType(prevChild, nextChild)) {
                console.log("两个 child 不相等(从右往左比对)");
                console.log(`prevChild:${prevChild}`);
                console.log(`nextChild:${nextChild}`);
                break;
            }
            console.log("两个 child 相等,接下来对比这两个 child 节点(从右往左比对)");
            patch(prevChild, nextChild, container, parentAnchor, parentComponent);
            e1--;
            e2--;
        }
        if (i > e1 && i <= e2) {
            const nextPos = e2 + 1;
            const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;
            while (i <= e2) {
                console.log(`需要新创建一个 vnode: ${c2[i].key}`);
                patch(null, c2[i], container, anchor, parentComponent);
                i++;
            }
        }
        else if (i > e2 && i <= e1) {
            while (i <= e1) {
                console.log(`需要删除当前的 vnode: ${c1[i].key}`);
                hostRemove(c1[i].el);
                i++;
            }
        }
        else {
            let s1 = i;
            let s2 = i;
            const keyToNewIndexMap = new Map();
            let moved = false;
            let maxNewIndexSoFar = 0;
            for (let i = s2; i <= e2; i++) {
                const nextChild = c2[i];
                keyToNewIndexMap.set(nextChild.key, i);
            }
            const toBePatched = e2 - s2 + 1;
            let patched = 0;
            const newIndexToOldIndexMap = new Array(toBePatched);
            for (let i = 0; i < toBePatched; i++)
                newIndexToOldIndexMap[i] = 0;
            for (i = s1; i <= e1; i++) {
                const prevChild = c1[i];
                if (patched >= toBePatched) {
                    hostRemove(prevChild.el);
                    continue;
                }
                let newIndex;
                if (prevChild.key != null) {
                    newIndex = keyToNewIndexMap.get(prevChild.key);
                }
                else {
                    for (let j = s2; j <= e2; j++) {
                        if (isSameVNodeType(prevChild, c2[j])) {
                            newIndex = j;
                            break;
                        }
                    }
                }
                if (newIndex === undefined) {
                    hostRemove(prevChild.el);
                }
                else {
                    console.log("新老节点都存在");
                    newIndexToOldIndexMap[newIndex - s2] = i + 1;
                    if (newIndex >= maxNewIndexSoFar) {
                        maxNewIndexSoFar = newIndex;
                    }
                    else {
                        moved = true;
                    }
                    patch(prevChild, c2[newIndex], container, null, parentComponent);
                    patched++;
                }
            }
            const increasingNewIndexSequence = moved
                ? getSequence(newIndexToOldIndexMap)
                : [];
            let j = increasingNewIndexSequence.length - 1;
            for (let i = toBePatched - 1; i >= 0; i--) {
                const nextIndex = s2 + i;
                const nextChild = c2[nextIndex];
                const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;
                if (newIndexToOldIndexMap[i] === 0) {
                    patch(null, nextChild, container, anchor, parentComponent);
                }
                else if (moved) {
                    if (j < 0 || increasingNewIndexSequence[j] !== i) {
                        hostInsert(nextChild.el, container, anchor);
                    }
                    else {
                        j--;
                    }
                }
            }
        }
    }
    function mountElement(vnode, container, anchor) {
        const { shapeFlag, props } = vnode;
        const el = (vnode.el = hostCreateElement(vnode.type));
        if (shapeFlag & 8) {
            console.log(`处理文本:${vnode.children}`);
            hostSetElementText(el, vnode.children);
        }
        else if (shapeFlag & 16) {
            mountChildren(vnode.children, el);
        }
        if (props) {
            for (const key in props) {
                const nextVal = props[key];
                hostPatchProp(el, key, null, nextVal);
            }
        }
        console.log("vnodeHook  -> onVnodeBeforeMount");
        console.log("DirectiveHook  -> beforeMount");
        console.log("transition  -> beforeEnter");
        hostInsert(el, container, anchor);
        console.log("vnodeHook  -> onVnodeMounted");
        console.log("DirectiveHook  -> mounted");
        console.log("transition  -> enter");
    }
    function mountChildren(children, container) {
        children.forEach((VNodeChild) => {
            console.log("mountChildren:", VNodeChild);
            patch(null, VNodeChild, container);
        });
    }
    function processComponent(n1, n2, container, parentComponent) {
        if (!n1) {
            mountComponent(n2, container, parentComponent);
        }
        else {
            updateComponent(n1, n2);
        }
    }
    function updateComponent(n1, n2, container) {
        console.log("更新组件", n1, n2);
        const instance = (n2.component = n1.component);
        if (shouldUpdateComponent(n1, n2)) {
            console.log(`组件需要更新: ${instance}`);
            instance.next = n2;
            instance.update();
        }
        else {
            console.log(`组件不需要更新: ${instance}`);
            n2.component = n1.component;
            n2.el = n1.el;
            instance.vnode = n2;
        }
    }
    function mountComponent(initialVNode, container, parentComponent) {
        const instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent));
        console.log(`创建组件实例:${instance.type.name}`);
        setupComponent(instance);
        setupRenderEffect(instance, initialVNode, container);
    }
    function setupRenderEffect(instance, initialVNode, container) {
        function componentUpdateFn() {
            if (!instance.isMounted) {
                console.log(`${instance.type.name}:调用 render,获取 subTree`);
                const proxyToUse = instance.proxy;
                const subTree = (instance.subTree = normalizeVNode(instance.render.call(proxyToUse, proxyToUse)));
                console.log("subTree", subTree);
                console.log(`${instance.type.name}:触发 beforeMount hook`);
                console.log(`${instance.type.name}:触发 onVnodeBeforeMount hook`);
                patch(null, subTree, container, null, instance);
                initialVNode.el = subTree.el;
                console.log(`${instance.type.name}:触发 mounted hook`);
                instance.isMounted = true;
            }
            else {
                console.log(`${instance.type.name}:调用更新逻辑`);
                const { next, vnode } = instance;
                if (next) {
                    next.el = vnode.el;
                    updateComponentPreRender(instance, next);
                }
                const proxyToUse = instance.proxy;
                const nextTree = normalizeVNode(instance.render.call(proxyToUse, proxyToUse));
                const prevTree = instance.subTree;
                instance.subTree = nextTree;
                console.log(`${instance.type.name}:触发 beforeUpdated hook`);
                console.log(`${instance.type.name}:触发 onVnodeBeforeUpdate hook`);
                patch(prevTree, nextTree, prevTree.el, null, instance);
                console.log(`${instance.type.name}:触发 updated hook`);
                console.log(`${instance.type.name}:触发 onVnodeUpdated hook`);
            }
        }
        instance.update = effect(componentUpdateFn, {
            scheduler: () => {
                queueJob(instance.update);
            },
        });
    }
    function updateComponentPreRender(instance, nextVNode) {
        nextVNode.component = instance;
        instance.vnode = nextVNode;
        instance.next = null;
        const { props } = nextVNode;
        console.log("更新组件的 props", props);
        instance.props = props;
        console.log("更新组件的 slots");
    }
    return {
        render,
        createApp: createAppAPI(render),
    };
}
function getSequence(arr) {
    const p = arr.slice();
    const result = [0];
    let i, j, u, v, c;
    const len = arr.length;
    for (i = 0; i < len; i++) {
        const arrI = arr[i];
        if (arrI !== 0) {
            j = result[result.length - 1];
            if (arr[j] < arrI) {
                p[i] = j;
                result.push(i);
                continue;
            }
            u = 0;
            v = result.length - 1;
            while (u < v) {
                c = (u + v) >> 1;
                if (arr[result[c]] < arrI) {
                    u = c + 1;
                }
                else {
                    v = c;
                }
            }
            if (arrI < arr[result[u]]) {
                if (u > 0) {
                    p[i] = result[u - 1];
                }
                result[u] = i;
            }
        }
    }
    u = result.length;
    v = result[u - 1];
    while (u-- > 0) {
        result[u] = v;
        v = p[v];
    }
    return result;
}

function watchEffect(effect) {
    return doWatch(effect);
}
function doWatch(source) {
    const job = () => {
        effect.run();
    };
    const scheduler = () => queuePreFlushCb(job);
    let cleanup;
    const onCleanup = (fn) => {
        cleanup = effect.onStop = () => {
            fn();
        };
    };
    const getter = () => {
        if (cleanup) {
            cleanup();
        }
        source(onCleanup);
    };
    const effect = new ReactiveEffect(getter, scheduler);
    effect.run();
    return () => {
        effect.stop();
    };
}

function createElement(type) {
    console.log("CreateElement", type);
    const element = document.createElement(type);
    return element;
}
function createText(text) {
    return document.createTextNode(text);
}
function setText(node, text) {
    node.nodeValue = text;
}
function setElementText(el, text) {
    console.log("SetElementText", el, text);
    el.textContent = text;
}
function patchProp(el, key, preValue, nextValue) {
    console.log(`PatchProp 设置属性:${key} 值:${nextValue}`);
    console.log(`key: ${key} 之前的值是:${preValue}`);
    if (isOn(key)) {
        const invokers = el._vei || (el._vei = {});
        const existingInvoker = invokers[key];
        if (nextValue && existingInvoker) {
            existingInvoker.value = nextValue;
        }
        else {
            const eventName = key.slice(2).toLowerCase();
            if (nextValue) {
                const invoker = (invokers[key] = nextValue);
                el.addEventListener(eventName, invoker);
            }
            else {
                el.removeEventListener(eventName, existingInvoker);
                invokers[key] = undefined;
            }
        }
    }
    else {
        if (nextValue === null || nextValue === "") {
            el.removeAttribute(key);
        }
        else {
            el.setAttribute(key, nextValue);
        }
    }
}
function insert(child, parent, anchor = null) {
    console.log("Insert");
    parent.insertBefore(child, anchor);
}
function remove(child) {
    const parent = child.parentNode;
    if (parent) {
        parent.removeChild(child);
    }
}
let renderer;
function ensureRenderer() {
    return (renderer ||
        (renderer = createRenderer({
            createElement,
            createText,
            setText,
            setElementText,
            patchProp,
            insert,
            remove,
        })));
}
const createApp = (...args) => {
    return ensureRenderer().createApp(...args);
};

var runtimeDom = /*#__PURE__*/Object.freeze({
    __proto__: null,
    createApp: createApp,
    getCurrentInstance: getCurrentInstance,
    registerRuntimeCompiler: registerRuntimeCompiler,
    inject: inject,
    provide: provide,
    renderSlot: renderSlot,
    createTextVNode: createTextVNode,
    createElementVNode: createVNode,
    createRenderer: createRenderer,
    toDisplayString: toDisplayString,
    watchEffect: watchEffect,
    reactive: reactive,
    ref: ref,
    readonly: readonly,
    unRef: unRef,
    proxyRefs: proxyRefs,
    isReadonly: isReadonly,
    isReactive: isReactive,
    isProxy: isProxy,
    isRef: isRef,
    shallowReadonly: shallowReadonly,
    effect: effect,
    stop: stop,
    computed: computed,
    h: h,
    createAppAPI: createAppAPI
});

const TO_DISPLAY_STRING = Symbol(`toDisplayString`);
const CREATE_ELEMENT_VNODE = Symbol("createElementVNode");
const helperNameMap = {
    [TO_DISPLAY_STRING]: "toDisplayString",
    [CREATE_ELEMENT_VNODE]: "createElementVNode"
};

function generate(ast, options = {}) {
    const context = createCodegenContext(ast, options);
    const { push, mode } = context;
    if (mode === "module") {
        genModulePreamble(ast, context);
    }
    else {
        genFunctionPreamble(ast, context);
    }
    const functionName = "render";
    const args = ["_ctx"];
    const signature = args.join(", ");
    push(`function ${functionName}(${signature}) {`);
    push("return ");
    genNode(ast.codegenNode, context);
    push("}");
    return {
        code: context.code,
    };
}
function genFunctionPreamble(ast, context) {
    const { runtimeGlobalName, push, newline } = context;
    const VueBinging = runtimeGlobalName;
    const aliasHelper = (s) => `${helperNameMap[s]} : _${helperNameMap[s
Download .txt
gitextract_4amh303j/

├── .gitignore
├── .vscode/
│   ├── launch.json
│   └── settings.json
├── LICENSE
├── README.md
├── README_EN.md
├── package.json
├── packages/
│   ├── compiler-core/
│   │   ├── __tests__/
│   │   │   ├── __snapshots__/
│   │   │   │   └── codegen.spec.ts.snap
│   │   │   ├── codegen.spec.ts
│   │   │   ├── parse.spec.ts
│   │   │   └── transform.spec.ts
│   │   ├── package.json
│   │   └── src/
│   │       ├── ast.ts
│   │       ├── codegen.ts
│   │       ├── compile.ts
│   │       ├── index.ts
│   │       ├── parse.ts
│   │       ├── runtimeHelpers.ts
│   │       ├── transform.ts
│   │       ├── transforms/
│   │       │   ├── transformElement.ts
│   │       │   ├── transformExpression.ts
│   │       │   └── transformText.ts
│   │       └── utils.ts
│   ├── reactivity/
│   │   ├── __tests__/
│   │   │   ├── computed.spec.ts
│   │   │   ├── dep.spec.ts
│   │   │   ├── effect.spec.ts
│   │   │   ├── reactive.spec.ts
│   │   │   ├── readonly.spec.ts
│   │   │   ├── ref.spec.ts
│   │   │   └── shallowReadonly.spec.ts
│   │   ├── package.json
│   │   └── src/
│   │       ├── baseHandlers.ts
│   │       ├── computed.ts
│   │       ├── dep.ts
│   │       ├── effect.ts
│   │       ├── index.ts
│   │       ├── reactive.ts
│   │       └── ref.ts
│   ├── runtime-core/
│   │   ├── __tests__/
│   │   │   ├── apiWatch.spec.ts
│   │   │   ├── componentEmits.spec.ts
│   │   │   ├── rendererComponent.spec.ts
│   │   │   └── rendererElement.spec.ts
│   │   ├── package.json
│   │   └── src/
│   │       ├── .pnpm-debug.log
│   │       ├── apiInject.ts
│   │       ├── apiWatch.ts
│   │       ├── component.ts
│   │       ├── componentEmits.ts
│   │       ├── componentProps.ts
│   │       ├── componentPublicInstance.ts
│   │       ├── componentRenderUtils.ts
│   │       ├── componentSlots.ts
│   │       ├── createApp.ts
│   │       ├── h.ts
│   │       ├── helpers/
│   │       │   └── renderSlot.ts
│   │       ├── index.ts
│   │       ├── renderer.ts
│   │       ├── scheduler.ts
│   │       └── vnode.ts
│   ├── runtime-dom/
│   │   ├── package.json
│   │   └── src/
│   │       └── index.ts
│   ├── runtime-test/
│   │   └── src/
│   │       ├── index.ts
│   │       ├── nodeOps.ts
│   │       ├── patchProp.ts
│   │       └── serialize.ts
│   ├── shared/
│   │   ├── package.json
│   │   └── src/
│   │       ├── index.ts
│   │       ├── shapeFlags.ts
│   │       └── toDisplayString.ts
│   └── vue/
│       ├── cypress/
│       │   ├── e2e/
│       │   │   ├── apiInject.cy.js
│       │   │   ├── componentEmit.cy.js
│       │   │   ├── componentSlots.cy.js
│       │   │   ├── componentUpdate.cy.js
│       │   │   ├── customRenderer.cy.js
│       │   │   ├── getCurrentInstance.cy.js
│       │   │   ├── helloworld.cy.js
│       │   │   ├── nextTicker.cy.js
│       │   │   └── patchChildren.cy.js
│       │   ├── fixtures/
│       │   │   └── example.json
│       │   └── support/
│       │       ├── commands.js
│       │       └── e2e.js
│       ├── cypress.config.js
│       ├── dist/
│       │   ├── mini-vue.cjs.js
│       │   └── mini-vue.esm-bundler.js
│       ├── example/
│       │   ├── apiInject/
│       │   │   ├── App.js
│       │   │   └── index.html
│       │   ├── compiler-base/
│       │   │   ├── App.js
│       │   │   └── index.html
│       │   ├── componentEmit/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   ├── componentProxy/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   ├── componentSlots/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   ├── componentUpdate/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   ├── createTextVnode/
│       │   │   ├── App.js
│       │   │   └── index.html
│       │   ├── customRenderer/
│       │   │   ├── App.js
│       │   │   ├── game.js
│       │   │   ├── index.html
│       │   │   ├── main.js
│       │   │   └── renderer.js
│       │   ├── getCurrentInstance/
│       │   │   ├── App.js
│       │   │   └── index.html
│       │   ├── helloWorld/
│       │   │   ├── App.js
│       │   │   ├── index.html
│       │   │   └── main.js
│       │   ├── nextTicker/
│       │   │   ├── App.js
│       │   │   ├── NextTicker.js
│       │   │   ├── index.html
│       │   │   └── main.js
│       │   ├── patchChildren/
│       │   │   ├── App.js
│       │   │   ├── ArrayToArray.js
│       │   │   ├── ArrayToText.js
│       │   │   ├── TextToArray.js
│       │   │   ├── TextToText.js
│       │   │   ├── index.html
│       │   │   └── main.js
│       │   ├── renderComponent/
│       │   │   ├── App.js
│       │   │   ├── Child.js
│       │   │   └── index.html
│       │   └── setupStateRenderComponent/
│       │       ├── App.js
│       │       └── index.html
│       ├── index.js
│       ├── package.json
│       └── src/
│           └── index.ts
├── pnpm-workspace.yaml
├── rollup.config.js
├── tsconfig.json
└── vitest.config.ts
Download .txt
SYMBOL INDEX (470 symbols across 67 files)

FILE: packages/compiler-core/src/ast.ts
  type NodeTypes (line 3) | const enum NodeTypes {
  type ElementTypes (line 12) | const enum ElementTypes {
  function createSimpleExpression (line 16) | function createSimpleExpression(content) {
  function createInterpolation (line 23) | function createInterpolation(content) {
  function createVNodeCall (line 30) | function createVNodeCall(context, tag, props?, children?) {

FILE: packages/compiler-core/src/codegen.ts
  function generate (line 9) | function generate(ast, options = {}) {
  function genFunctionPreamble (line 42) | function genFunctionPreamble(ast: any, context: any) {
  function genNode (line 61) | function genNode(node: any, context: any) {
  function genCompoundExpression (line 90) | function genCompoundExpression(node: any, context: any) {
  function genText (line 102) | function genText(node: any, context: any) {
  function genElement (line 109) | function genElement(node, context) {
  function genNodeList (line 120) | function genNodeList(nodes: any, context: any) {
  function genNullableArgs (line 138) | function genNullableArgs(args) {
  function genExpression (line 153) | function genExpression(node: any, context: any) {
  function genInterpolation (line 157) | function genInterpolation(node: any, context: any) {
  function genModulePreamble (line 164) | function genModulePreamble(ast, context) {
  function createCodegenContext (line 182) | function createCodegenContext(

FILE: packages/compiler-core/src/compile.ts
  function baseCompile (line 8) | function baseCompile(template, options) {

FILE: packages/compiler-core/src/parse.ts
  type TagType (line 3) | const enum TagType {
  function baseParse (line 8) | function baseParse(content: string) {
  function createParserContext (line 13) | function createParserContext(content) {
  function parseChildren (line 20) | function parseChildren(context, ancestors) {
  function isEnd (line 57) | function isEnd(context: any, ancestors) {
  function parseElement (line 77) | function parseElement(context, ancestors) {
  function startsWithEndTagOpen (line 100) | function startsWithEndTagOpen(source: string, tag: string) {
  function parseTag (line 109) | function parseTag(context: any, type: TagType): any {
  function parseInterpolation (line 133) | function parseInterpolation(context: any) {
  function parseText (line 171) | function parseText(context): any {
  function parseTextData (line 200) | function parseTextData(context: any, length: number): any {
  function advanceBy (line 211) | function advanceBy(context, numberOfCharacters) {
  function createRoot (line 216) | function createRoot(children) {
  function startsWith (line 224) | function startsWith(source: string, searchString: string): boolean {

FILE: packages/compiler-core/src/runtimeHelpers.ts
  constant TO_DISPLAY_STRING (line 1) | const TO_DISPLAY_STRING = Symbol(`toDisplayString`);
  constant CREATE_ELEMENT_VNODE (line 2) | const CREATE_ELEMENT_VNODE = Symbol("createElementVNode");

FILE: packages/compiler-core/src/transform.ts
  function transform (line 4) | function transform(root, options = {}) {
  function traverseNode (line 17) | function traverseNode(node: any, context) {
  function traverseChildren (line 60) | function traverseChildren(parent: any, context: any) {
  function createTransformContext (line 68) | function createTransformContext(root, options): any {
  function createRootCodegen (line 85) | function createRootCodegen(root: any, context: any) {

FILE: packages/compiler-core/src/transforms/transformElement.ts
  function transformElement (line 3) | function transformElement(node, context) {

FILE: packages/compiler-core/src/transforms/transformExpression.ts
  function transformExpression (line 3) | function transformExpression(node) {
  function processExpression (line 9) | function processExpression(node) {

FILE: packages/compiler-core/src/transforms/transformText.ts
  function transformText (line 4) | function transformText(node, context) {

FILE: packages/compiler-core/src/utils.ts
  function isText (line 3) | function isText(node) {

FILE: packages/reactivity/__tests__/effect.spec.ts
  function getNum (line 59) | function getNum() {

FILE: packages/reactivity/src/baseHandlers.ts
  function createGetter (line 17) | function createGetter(isReadonly = false, shallow = false) {
  function createSetter (line 66) | function createSetter() {
  method set (line 79) | set(target, key) {
  method set (line 96) | set(target, key) {

FILE: packages/reactivity/src/computed.ts
  class ComputedRefImpl (line 5) | class ComputedRefImpl {
    method constructor (line 12) | constructor(getter) {
    method value (line 26) | get value() {
  function computed (line 43) | function computed(getter) {

FILE: packages/reactivity/src/dep.ts
  function createDep (line 2) | function createDep(effects?) {

FILE: packages/reactivity/src/effect.ts
  class ReactiveEffect (line 9) | class ReactiveEffect {
    method constructor (line 13) | constructor(public fn, public scheduler?) {
    method run (line 17) | run() {
    method stop (line 47) | stop() {
  function cleanupEffect (line 60) | function cleanupEffect(effect) {
  function effect (line 70) | function effect(fn, options = {}) {
  function stop (line 85) | function stop(runner) {
  function track (line 89) | function track(target, type, key) {
  function trackEffects (line 114) | function trackEffects(dep) {
  function trigger (line 130) | function trigger(target, type, key) {
  function isTracking (line 157) | function isTracking() {
  function triggerEffects (line 161) | function triggerEffects(dep) {

FILE: packages/reactivity/src/reactive.ts
  type ReactiveFlags (line 11) | const enum ReactiveFlags {
  function reactive (line 17) | function reactive(target) {
  function readonly (line 21) | function readonly(target) {
  function shallowReadonly (line 25) | function shallowReadonly(target) {
  function isProxy (line 33) | function isProxy(value) {
  function isReadonly (line 37) | function isReadonly(value) {
  function isReactive (line 41) | function isReactive(value) {
  function toRaw (line 49) | function toRaw(value) {
  function createReactiveObject (line 63) | function createReactiveObject(target, proxyMap, baseHandlers) {

FILE: packages/reactivity/src/ref.ts
  class RefImpl (line 6) | class RefImpl {
    method constructor (line 12) | constructor(value) {
    method value (line 20) | get value() {
    method value (line 26) | set value(newValue) {
  function ref (line 39) | function ref(value) {
  function convert (line 43) | function convert(value) {
  function createRef (line 47) | function createRef(value) {
  function triggerRefValue (line 53) | function triggerRefValue(ref) {
  function trackRefValue (line 57) | function trackRefValue(ref) {
  method get (line 70) | get(target, key, receiver) {
  method set (line 75) | set(target, key, value, receiver) {
  function proxyRefs (line 88) | function proxyRefs(objectWithRefs) {
  function unRef (line 93) | function unRef(ref) {
  function isRef (line 97) | function isRef(value) {

FILE: packages/runtime-core/__tests__/componentEmits.spec.ts
  method render (line 7) | render() {
  method setup (line 10) | setup(props, { emit }) {
  method render (line 20) | render() {
  method render (line 33) | render() {
  method setup (line 36) | setup(props, { emit }) {
  method render (line 43) | render() {
  method render (line 54) | render() {
  method setup (line 57) | setup(props, { emit }) {
  method render (line 65) | render() {

FILE: packages/runtime-core/src/apiInject.ts
  function provide (line 3) | function provide(key, value) {
  function inject (line 26) | function inject(key, defaultValue) {

FILE: packages/runtime-core/src/apiWatch.ts
  function watchEffect (line 5) | function watchEffect(effect) {
  function doWatch (line 9) | function doWatch(source) {

FILE: packages/runtime-core/src/component.ts
  function createComponentInstance (line 6) | function createComponentInstance(vnode, parent) {
  function setupComponent (line 37) | function setupComponent(instance) {
  function setupStatefulComponent (line 53) | function setupStatefulComponent(instance) {
  function createSetupContext (line 88) | function createSetupContext(instance) {
  function handleSetupResult (line 98) | function handleSetupResult(instance, setupResult) {
  function finishComponentSetup (line 121) | function finishComponentSetup(instance) {
  function applyOptions (line 143) | function applyOptions() {
  function getCurrentInstance (line 151) | function getCurrentInstance(): any {
  function setCurrentInstance (line 155) | function setCurrentInstance(instance) {
  function registerRuntimeCompiler (line 160) | function registerRuntimeCompiler(_compile) {

FILE: packages/runtime-core/src/componentEmits.ts
  function emit (line 2) | function emit(instance, event: string, ...rawArgs) {

FILE: packages/runtime-core/src/componentProps.ts
  function initProps (line 1) | function initProps(instance, rawProps) {

FILE: packages/runtime-core/src/componentPublicInstance.ts
  method get (line 14) | get({ _: instance }, key) {
  method set (line 40) | set({ _: instance }, key, value) {

FILE: packages/runtime-core/src/componentRenderUtils.ts
  function shouldUpdateComponent (line 1) | function shouldUpdateComponent(prevVNode, nextVNode) {
  function hasPropsChanged (line 28) | function hasPropsChanged(prevProps, nextProps): boolean {

FILE: packages/runtime-core/src/componentSlots.ts
  function initSlots (line 2) | function initSlots(instance, children) {

FILE: packages/runtime-core/src/createApp.ts
  function createAppAPI (line 3) | function createAppAPI(render) {

FILE: packages/runtime-core/src/helpers/renderSlot.ts
  function renderSlot (line 15) | function renderSlot(slots, name: string, props = {}) {

FILE: packages/runtime-core/src/renderer.ts
  function createRenderer (line 10) | function createRenderer(options) {
  function getSequence (line 612) | function getSequence(arr: number[]): number[] {

FILE: packages/runtime-core/src/scheduler.ts
  function nextTick (line 7) | function nextTick(fn?) {
  function queueJob (line 11) | function queueJob(job) {
  function queueFlush (line 19) | function queueFlush() {
  function queuePreFlushCb (line 31) | function queuePreFlushCb(cb) {
  function queueCb (line 35) | function queueCb(cb, activeQueue) {
  function flushJobs (line 45) | function flushJobs() {
  function flushPreFlushCbs (line 63) | function flushPreFlushCbs() {

FILE: packages/runtime-core/src/vnode.ts
  function normalizeChildren (line 38) | function normalizeChildren(vnode, children) {
  function createTextVNode (line 58) | function createTextVNode(text: string = " ") {
  function normalizeVNode (line 64) | function normalizeVNode(child) {
  function getShapeFlag (line 74) | function getShapeFlag(type: any) {

FILE: packages/runtime-dom/src/index.ts
  function createElement (line 8) | function createElement(type) {
  function createText (line 14) | function createText(text) {
  function setText (line 18) | function setText(node, text) {
  function setElementText (line 22) | function setElementText(el, text) {
  function patchProp (line 27) | function patchProp(el, key, preValue, nextValue) {
  function insert (line 65) | function insert(child, parent, anchor = null) {
  function remove (line 70) | function remove(child) {
  function ensureRenderer (line 79) | function ensureRenderer() {

FILE: packages/runtime-test/src/nodeOps.ts
  type NodeTypes (line 1) | const enum NodeTypes {
  function createElement (line 8) | function createElement(tag: string) {
  function insert (line 24) | function insert(child, parent) {
  function parentNode (line 29) | function parentNode(node) {
  function setElementText (line 33) | function setElementText(el, text) {

FILE: packages/runtime-test/src/patchProp.ts
  function patchProp (line 1) | function patchProp(el, key, prevValue, nextValue) {

FILE: packages/runtime-test/src/serialize.ts
  function serialize (line 7) | function serialize(node) {
  function serializeText (line 15) | function serializeText(node) {
  function serializeInner (line 19) | function serializeInner(node) {
  function serializeElement (line 24) | function serializeElement(node) {

FILE: packages/shared/src/index.ts
  function hasChanged (line 24) | function hasChanged(value, oldValue) {
  function hasOwn (line 28) | function hasOwn(val, key) {

FILE: packages/shared/src/shapeFlags.ts
  type ShapeFlags (line 2) | const enum ShapeFlags {

FILE: packages/vue/cypress.config.js
  method setupNodeEvents (line 5) | setupNodeEvents(on, config) {

FILE: packages/vue/dist/mini-vue.cjs.js
  function hasChanged (line 28) | function hasChanged(value, oldValue) {
  function hasOwn (line 31) | function hasOwn(val, key) {
  function normalizeChildren (line 58) | function normalizeChildren(vnode, children) {
  function createTextVNode (line 68) | function createTextVNode(text = " ") {
  function normalizeVNode (line 71) | function normalizeVNode(child) {
  function getShapeFlag (line 79) | function getShapeFlag(type) {
  function createAppAPI (line 89) | function createAppAPI(render) {
  function initProps (line 104) | function initProps(instance, rawProps) {
  function initSlots (line 109) | function initSlots(instance, children) {
  function emit (line 128) | function emit(instance, event, ...rawArgs) {
  method get (line 146) | get({ _: instance }, key) {
  method set (line 162) | set({ _: instance }, key, value) {
  function createDep (line 171) | function createDep(effects) {
  class ReactiveEffect (line 179) | class ReactiveEffect {
    method constructor (line 180) | constructor(fn, scheduler) {
    method run (line 187) | run() {
    method stop (line 200) | stop() {
  function cleanupEffect (line 210) | function cleanupEffect(effect) {
  function effect (line 216) | function effect(fn, options = {}) {
  function stop (line 224) | function stop(runner) {
  function track (line 227) | function track(target, type, key) {
  function trackEffects (line 244) | function trackEffects(dep) {
  function trigger (line 250) | function trigger(target, type, key) {
  function isTracking (line 263) | function isTracking() {
  function triggerEffects (line 266) | function triggerEffects(dep) {
  function createGetter (line 281) | function createGetter(isReadonly = false, shallow = false) {
  function createSetter (line 310) | function createSetter() {
  method set (line 319) | set(target, key) {
  method set (line 330) | set(target, key) {
  function reactive (line 345) | function reactive(target) {
  function readonly (line 348) | function readonly(target) {
  function shallowReadonly (line 351) | function shallowReadonly(target) {
  function isProxy (line 354) | function isProxy(value) {
  function isReadonly (line 357) | function isReadonly(value) {
  function isReactive (line 360) | function isReactive(value) {
  function createReactiveObject (line 363) | function createReactiveObject(target, proxyMap, baseHandlers) {
  class RefImpl (line 373) | class RefImpl {
    method constructor (line 374) | constructor(value) {
    method value (line 380) | get value() {
    method value (line 384) | set value(newValue) {
  function ref (line 392) | function ref(value) {
  function convert (line 395) | function convert(value) {
  function createRef (line 398) | function createRef(value) {
  function triggerRefValue (line 402) | function triggerRefValue(ref) {
  function trackRefValue (line 405) | function trackRefValue(ref) {
  method get (line 411) | get(target, key, receiver) {
  method set (line 414) | set(target, key, value, receiver) {
  function proxyRefs (line 424) | function proxyRefs(objectWithRefs) {
  function unRef (line 427) | function unRef(ref) {
  function isRef (line 430) | function isRef(value) {
  class ComputedRefImpl (line 434) | class ComputedRefImpl {
    method constructor (line 435) | constructor(getter) {
    method value (line 445) | get value() {
  function computed (line 454) | function computed(getter) {
  function createComponentInstance (line 458) | function createComponentInstance(vnode, parent) {
  function setupComponent (line 480) | function setupComponent(instance) {
  function setupStatefulComponent (line 486) | function setupStatefulComponent(instance) {
  function createSetupContext (line 502) | function createSetupContext(instance) {
  function handleSetupResult (line 511) | function handleSetupResult(instance, setupResult) {
  function finishComponentSetup (line 520) | function finishComponentSetup(instance) {
  function getCurrentInstance (line 533) | function getCurrentInstance() {
  function setCurrentInstance (line 536) | function setCurrentInstance(instance) {
  function registerRuntimeCompiler (line 540) | function registerRuntimeCompiler(_compile) {
  function provide (line 544) | function provide(key, value) {
  function inject (line 556) | function inject(key, defaultValue) {
  function renderSlot (line 573) | function renderSlot(slots, name, props = {}) {
  function nextTick (line 586) | function nextTick(fn) {
  function queueJob (line 589) | function queueJob(job) {
  function queueFlush (line 595) | function queueFlush() {
  function queuePreFlushCb (line 601) | function queuePreFlushCb(cb) {
  function queueCb (line 604) | function queueCb(cb, activeQueue) {
  function flushJobs (line 608) | function flushJobs() {
  function flushPreFlushCbs (line 618) | function flushPreFlushCbs() {
  function shouldUpdateComponent (line 624) | function shouldUpdateComponent(prevVNode, nextVNode) {
  function hasPropsChanged (line 638) | function hasPropsChanged(prevProps, nextProps) {
  function createRenderer (line 652) | function createRenderer(options) {
  function getSequence (line 984) | function getSequence(arr) {
  function watchEffect (line 1026) | function watchEffect(effect) {
  function doWatch (line 1029) | function doWatch(source) {
  function createElement (line 1053) | function createElement(type) {
  function createText (line 1058) | function createText(text) {
  function setText (line 1061) | function setText(node, text) {
  function setElementText (line 1064) | function setElementText(el, text) {
  function patchProp (line 1068) | function patchProp(el, key, preValue, nextValue) {
  function insert (line 1098) | function insert(child, parent, anchor = null) {
  function remove (line 1102) | function remove(child) {
  function ensureRenderer (line 1109) | function ensureRenderer() {
  constant TO_DISPLAY_STRING (line 1155) | const TO_DISPLAY_STRING = Symbol(`toDisplayString`);
  constant CREATE_ELEMENT_VNODE (line 1156) | const CREATE_ELEMENT_VNODE = Symbol("createElementVNode");
  function generate (line 1162) | function generate(ast, options = {}) {
  function genFunctionPreamble (line 1182) | function genFunctionPreamble(ast, context) {
  function genNode (line 1195) | function genNode(node, context) {
  function genCompoundExpression (line 1214) | function genCompoundExpression(node, context) {
  function genText (line 1226) | function genText(node, context) {
  function genElement (line 1230) | function genElement(node, context) {
  function genNodeList (line 1237) | function genNodeList(nodes, context) {
  function genNullableArgs (line 1252) | function genNullableArgs(args) {
  function genExpression (line 1260) | function genExpression(node, context) {
  function genInterpolation (line 1263) | function genInterpolation(node, context) {
  function genModulePreamble (line 1269) | function genModulePreamble(ast, context) {
  function createCodegenContext (line 1280) | function createCodegenContext(ast, { runtimeModuleName = "vue", runtimeG...
  function baseParse (line 1304) | function baseParse(content) {
  function createParserContext (line 1308) | function createParserContext(content) {
  function parseChildren (line 1314) | function parseChildren(context, ancestors) {
  function isEnd (line 1341) | function isEnd(context, ancestors) {
  function parseElement (line 1352) | function parseElement(context, ancestors) {
  function startsWithEndTagOpen (line 1366) | function startsWithEndTagOpen(source, tag) {
  function parseTag (line 1370) | function parseTag(context, type) {
  function parseInterpolation (line 1384) | function parseInterpolation(context) {
  function parseText (line 1402) | function parseText(context) {
  function parseTextData (line 1418) | function parseTextData(context, length) {
  function advanceBy (line 1424) | function advanceBy(context, numberOfCharacters) {
  function createRoot (line 1428) | function createRoot(children) {
  function startsWith (line 1435) | function startsWith(source, searchString) {
  function transform (line 1439) | function transform(root, options = {}) {
  function traverseNode (line 1445) | function traverseNode(node, context) {
  function traverseChildren (line 1470) | function traverseChildren(parent, context) {
  function createTransformContext (line 1475) | function createTransformContext(root, options) {
  function createRootCodegen (line 1487) | function createRootCodegen(root, context) {
  function transformExpression (line 1499) | function transformExpression(node) {
  function processExpression (line 1504) | function processExpression(node) {
  function createVNodeCall (line 1522) | function createVNodeCall(context, tag, props, children) {
  function transformElement (line 1534) | function transformElement(node, context) {
  function isText (line 1551) | function isText(node) {
  function transformText (line 1555) | function transformText(node, context) {
  function baseCompile (line 1588) | function baseCompile(template, options) {
  function compileToFunction (line 1596) | function compileToFunction(template, options = {}) {

FILE: packages/vue/dist/mini-vue.esm-bundler.js
  function hasChanged (line 24) | function hasChanged(value, oldValue) {
  function hasOwn (line 27) | function hasOwn(val, key) {
  function normalizeChildren (line 54) | function normalizeChildren(vnode, children) {
  function createTextVNode (line 64) | function createTextVNode(text = " ") {
  function normalizeVNode (line 67) | function normalizeVNode(child) {
  function getShapeFlag (line 75) | function getShapeFlag(type) {
  function createAppAPI (line 85) | function createAppAPI(render) {
  function initProps (line 100) | function initProps(instance, rawProps) {
  function initSlots (line 105) | function initSlots(instance, children) {
  function emit (line 124) | function emit(instance, event, ...rawArgs) {
  method get (line 142) | get({ _: instance }, key) {
  method set (line 158) | set({ _: instance }, key, value) {
  function createDep (line 167) | function createDep(effects) {
  class ReactiveEffect (line 175) | class ReactiveEffect {
    method constructor (line 176) | constructor(fn, scheduler) {
    method run (line 183) | run() {
    method stop (line 196) | stop() {
  function cleanupEffect (line 206) | function cleanupEffect(effect) {
  function effect (line 212) | function effect(fn, options = {}) {
  function stop (line 220) | function stop(runner) {
  function track (line 223) | function track(target, type, key) {
  function trackEffects (line 240) | function trackEffects(dep) {
  function trigger (line 246) | function trigger(target, type, key) {
  function isTracking (line 259) | function isTracking() {
  function triggerEffects (line 262) | function triggerEffects(dep) {
  function createGetter (line 277) | function createGetter(isReadonly = false, shallow = false) {
  function createSetter (line 306) | function createSetter() {
  method set (line 315) | set(target, key) {
  method set (line 326) | set(target, key) {
  function reactive (line 341) | function reactive(target) {
  function readonly (line 344) | function readonly(target) {
  function shallowReadonly (line 347) | function shallowReadonly(target) {
  function isProxy (line 350) | function isProxy(value) {
  function isReadonly (line 353) | function isReadonly(value) {
  function isReactive (line 356) | function isReactive(value) {
  function createReactiveObject (line 359) | function createReactiveObject(target, proxyMap, baseHandlers) {
  class RefImpl (line 369) | class RefImpl {
    method constructor (line 370) | constructor(value) {
    method value (line 376) | get value() {
    method value (line 380) | set value(newValue) {
  function ref (line 388) | function ref(value) {
  function convert (line 391) | function convert(value) {
  function createRef (line 394) | function createRef(value) {
  function triggerRefValue (line 398) | function triggerRefValue(ref) {
  function trackRefValue (line 401) | function trackRefValue(ref) {
  method get (line 407) | get(target, key, receiver) {
  method set (line 410) | set(target, key, value, receiver) {
  function proxyRefs (line 420) | function proxyRefs(objectWithRefs) {
  function unRef (line 423) | function unRef(ref) {
  function isRef (line 426) | function isRef(value) {
  class ComputedRefImpl (line 430) | class ComputedRefImpl {
    method constructor (line 431) | constructor(getter) {
    method value (line 441) | get value() {
  function computed (line 450) | function computed(getter) {
  function createComponentInstance (line 454) | function createComponentInstance(vnode, parent) {
  function setupComponent (line 476) | function setupComponent(instance) {
  function setupStatefulComponent (line 482) | function setupStatefulComponent(instance) {
  function createSetupContext (line 498) | function createSetupContext(instance) {
  function handleSetupResult (line 507) | function handleSetupResult(instance, setupResult) {
  function finishComponentSetup (line 516) | function finishComponentSetup(instance) {
  function getCurrentInstance (line 529) | function getCurrentInstance() {
  function setCurrentInstance (line 532) | function setCurrentInstance(instance) {
  function registerRuntimeCompiler (line 536) | function registerRuntimeCompiler(_compile) {
  function provide (line 540) | function provide(key, value) {
  function inject (line 552) | function inject(key, defaultValue) {
  function renderSlot (line 569) | function renderSlot(slots, name, props = {}) {
  function nextTick (line 582) | function nextTick(fn) {
  function queueJob (line 585) | function queueJob(job) {
  function queueFlush (line 591) | function queueFlush() {
  function queuePreFlushCb (line 597) | function queuePreFlushCb(cb) {
  function queueCb (line 600) | function queueCb(cb, activeQueue) {
  function flushJobs (line 604) | function flushJobs() {
  function flushPreFlushCbs (line 614) | function flushPreFlushCbs() {
  function shouldUpdateComponent (line 620) | function shouldUpdateComponent(prevVNode, nextVNode) {
  function hasPropsChanged (line 634) | function hasPropsChanged(prevProps, nextProps) {
  function createRenderer (line 648) | function createRenderer(options) {
  function getSequence (line 980) | function getSequence(arr) {
  function watchEffect (line 1022) | function watchEffect(effect) {
  function doWatch (line 1025) | function doWatch(source) {
  function createElement (line 1049) | function createElement(type) {
  function createText (line 1054) | function createText(text) {
  function setText (line 1057) | function setText(node, text) {
  function setElementText (line 1060) | function setElementText(el, text) {
  function patchProp (line 1064) | function patchProp(el, key, preValue, nextValue) {
  function insert (line 1094) | function insert(child, parent, anchor = null) {
  function remove (line 1098) | function remove(child) {
  function ensureRenderer (line 1105) | function ensureRenderer() {
  constant TO_DISPLAY_STRING (line 1151) | const TO_DISPLAY_STRING = Symbol(`toDisplayString`);
  constant CREATE_ELEMENT_VNODE (line 1152) | const CREATE_ELEMENT_VNODE = Symbol("createElementVNode");
  function generate (line 1158) | function generate(ast, options = {}) {
  function genFunctionPreamble (line 1178) | function genFunctionPreamble(ast, context) {
  function genNode (line 1191) | function genNode(node, context) {
  function genCompoundExpression (line 1210) | function genCompoundExpression(node, context) {
  function genText (line 1222) | function genText(node, context) {
  function genElement (line 1226) | function genElement(node, context) {
  function genNodeList (line 1233) | function genNodeList(nodes, context) {
  function genNullableArgs (line 1248) | function genNullableArgs(args) {
  function genExpression (line 1256) | function genExpression(node, context) {
  function genInterpolation (line 1259) | function genInterpolation(node, context) {
  function genModulePreamble (line 1265) | function genModulePreamble(ast, context) {
  function createCodegenContext (line 1276) | function createCodegenContext(ast, { runtimeModuleName = "vue", runtimeG...
  function baseParse (line 1300) | function baseParse(content) {
  function createParserContext (line 1304) | function createParserContext(content) {
  function parseChildren (line 1310) | function parseChildren(context, ancestors) {
  function isEnd (line 1337) | function isEnd(context, ancestors) {
  function parseElement (line 1348) | function parseElement(context, ancestors) {
  function startsWithEndTagOpen (line 1362) | function startsWithEndTagOpen(source, tag) {
  function parseTag (line 1366) | function parseTag(context, type) {
  function parseInterpolation (line 1380) | function parseInterpolation(context) {
  function parseText (line 1398) | function parseText(context) {
  function parseTextData (line 1414) | function parseTextData(context, length) {
  function advanceBy (line 1420) | function advanceBy(context, numberOfCharacters) {
  function createRoot (line 1424) | function createRoot(children) {
  function startsWith (line 1431) | function startsWith(source, searchString) {
  function transform (line 1435) | function transform(root, options = {}) {
  function traverseNode (line 1441) | function traverseNode(node, context) {
  function traverseChildren (line 1466) | function traverseChildren(parent, context) {
  function createTransformContext (line 1471) | function createTransformContext(root, options) {
  function createRootCodegen (line 1483) | function createRootCodegen(root, context) {
  function transformExpression (line 1495) | function transformExpression(node) {
  function processExpression (line 1500) | function processExpression(node) {
  function createVNodeCall (line 1518) | function createVNodeCall(context, tag, props, children) {
  function transformElement (line 1530) | function transformElement(node, context) {
  function isText (line 1547) | function isText(node) {
  function transformText (line 1551) | function transformText(node, context) {
  function baseCompile (line 1584) | function baseCompile(template, options) {
  function compileToFunction (line 1592) | function compileToFunction(template, options = {}) {

FILE: packages/vue/example/apiInject/App.js
  method setup (line 9) | setup() {
  method setup (line 17) | setup() {
  method setup (line 32) | setup() {
  method setup (line 44) | setup() {

FILE: packages/vue/example/compiler-base/App.js
  method setup (line 17) | setup() {

FILE: packages/vue/example/componentEmit/App.js
  method setup (line 9) | setup() {}
  method render (line 11) | render() {

FILE: packages/vue/example/componentEmit/Child.js
  method setup (line 4) | setup(props, { emit }) {
  method render (line 9) | render() {

FILE: packages/vue/example/componentProxy/App.js
  method setup (line 9) | setup() {}
  method render (line 11) | render() {

FILE: packages/vue/example/componentProxy/Child.js
  method setup (line 4) | setup(props, { emit }) {}
  method render (line 5) | render(proxy) {

FILE: packages/vue/example/componentSlots/App.js
  method setup (line 6) | setup() {}
  method render (line 8) | render() {

FILE: packages/vue/example/componentSlots/Child.js
  method setup (line 4) | setup(props, context) {}
  method render (line 5) | render() {

FILE: packages/vue/example/componentUpdate/App.js
  method setup (line 9) | setup() {
  method render (line 20) | render() {

FILE: packages/vue/example/componentUpdate/Child.js
  method setup (line 4) | setup(props, { emit }) {}
  method render (line 5) | render(proxy) {

FILE: packages/vue/example/createTextVnode/App.js
  method setup (line 5) | setup() {}
  method render (line 7) | render() {

FILE: packages/vue/example/customRenderer/App.js
  method setup (line 6) | setup() {
  method render (line 30) | render() {

FILE: packages/vue/example/customRenderer/game.js
  function createRootContainer (line 8) | function createRootContainer() {

FILE: packages/vue/example/customRenderer/renderer.js
  method createElement (line 5) | createElement(type) {
  method patchProp (line 14) | patchProp(el, key, prevValue, nextValue) {
  method insert (line 18) | insert(el, parent) {
  function createApp (line 23) | function createApp(rootComponent) {

FILE: packages/vue/example/getCurrentInstance/App.js
  method setup (line 6) | setup() {

FILE: packages/vue/example/helloWorld/App.js
  method setup (line 7) | setup() {}
  method render (line 15) | render() {
  method setup (line 26) | setup() {}
  method render (line 28) | render() {

FILE: packages/vue/example/nextTicker/App.js
  method setup (line 6) | setup() {}
  method render (line 8) | render() {

FILE: packages/vue/example/nextTicker/NextTicker.js
  method setup (line 15) | setup() {}
  method render (line 16) | render() {
  method setup (line 23) | setup() {}
  method render (line 24) | render() {
  method setup (line 31) | setup() {}
  method render (line 32) | render() {

FILE: packages/vue/example/patchChildren/App.js
  method setup (line 9) | setup() {}
  method render (line 11) | render() {

FILE: packages/vue/example/patchChildren/ArrayToArray.js
  method setup (line 228) | setup() {
  method render (line 236) | render() {

FILE: packages/vue/example/patchChildren/ArrayToText.js
  method setup (line 10) | setup() {
  method render (line 18) | render() {

FILE: packages/vue/example/patchChildren/TextToArray.js
  method setup (line 10) | setup() {
  method render (line 18) | render() {

FILE: packages/vue/example/patchChildren/TextToText.js
  method setup (line 10) | setup() {
  method render (line 18) | render() {

FILE: packages/vue/example/renderComponent/App.js
  method setup (line 6) | setup() {}
  method render (line 8) | render() {

FILE: packages/vue/example/renderComponent/Child.js
  method setup (line 4) | setup(props, context) {
  method render (line 8) | render() {

FILE: packages/vue/example/setupStateRenderComponent/App.js
  method setup (line 6) | setup() {
  method render (line 19) | render() {

FILE: packages/vue/src/index.ts
  function compileToFunction (line 10) | function compileToFunction(template, options = {}) {
Condensed preview — 135 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (248K chars).
[
  {
    "path": ".gitignore",
    "chars": 12,
    "preview": "node_modules"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 709,
    "preview": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  //"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 40,
    "preview": "{\n    \"liveServer.settings.port\": 5501\n}"
  },
  {
    "path": "LICENSE",
    "chars": 1064,
    "preview": "MIT License\n\nCopyright (c) 2019 zenoslin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
  },
  {
    "path": "README.md",
    "chars": 3944,
    "preview": "[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-m"
  },
  {
    "path": "README_EN.md",
    "chars": 2869,
    "preview": "[EN](README.md) / [CN](README.md)\n## mini-vue\n\nImplement the simplest vue3 model for in-depth study of vue3 source code\n"
  },
  {
    "path": "package.json",
    "chars": 790,
    "preview": "{\n  \"private\": true,\n  \"version\": \"0.0.1\",\n  \"description\": \"Help you learn more efficiently vue3 source code\",\n  \"main\""
  },
  {
    "path": "packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap",
    "chars": 467,
    "preview": "// Vitest Snapshot v1\n\nexports[`element and interpolation 1`] = `\n\"\n        const { toDisplayString : _toDisplayString, "
  },
  {
    "path": "packages/compiler-core/__tests__/codegen.spec.ts",
    "chars": 828,
    "preview": "import { generate } from \"../src/codegen\";\nimport { baseParse } from \"../src/parse\";\nimport { transform } from \"../src/t"
  },
  {
    "path": "packages/compiler-core/__tests__/parse.spec.ts",
    "chars": 3232,
    "preview": "import { ElementTypes, NodeTypes } from \"../src/ast\";\nimport { baseParse } from \"../src/parse\";\n\ndescribe(\"parser\", () ="
  },
  {
    "path": "packages/compiler-core/__tests__/transform.spec.ts",
    "chars": 1555,
    "preview": "import { baseParse } from \"../src/parse\";\nimport { TO_DISPLAY_STRING } from \"../src/runtimeHelpers\";\nimport { transform "
  },
  {
    "path": "packages/compiler-core/package.json",
    "chars": 306,
    "preview": "{\n  \"name\": \"@mini-vue/compiler-core\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/compiler-core\",\n  \"scripts\": {\n"
  },
  {
    "path": "packages/compiler-core/src/ast.ts",
    "chars": 793,
    "preview": "import { CREATE_ELEMENT_VNODE } from \"./runtimeHelpers\";\n\nexport const enum NodeTypes {\n  TEXT,\n  ROOT,\n  INTERPOLATION,"
  },
  {
    "path": "packages/compiler-core/src/codegen.ts",
    "chars": 4427,
    "preview": "import { isString } from \"@mini-vue/shared\";\nimport { NodeTypes } from \"./ast\";\nimport {\n  CREATE_ELEMENT_VNODE,\n  helpe"
  },
  {
    "path": "packages/compiler-core/src/compile.ts",
    "chars": 653,
    "preview": "import { generate } from \"./codegen\";\nimport { baseParse } from \"./parse\";\nimport { transform } from \"./transform\";\nimpo"
  },
  {
    "path": "packages/compiler-core/src/index.ts",
    "chars": 41,
    "preview": "export { baseCompile } from \"./compile\";\n"
  },
  {
    "path": "packages/compiler-core/src/parse.ts",
    "chars": 5088,
    "preview": "import { ElementTypes, NodeTypes } from \"./ast\";\n\nconst enum TagType {\n  Start,\n  End,\n}\n\nexport function baseParse(cont"
  },
  {
    "path": "packages/compiler-core/src/runtimeHelpers.ts",
    "chars": 250,
    "preview": "export const TO_DISPLAY_STRING = Symbol(`toDisplayString`);\nexport const CREATE_ELEMENT_VNODE = Symbol(\"createElementVNo"
  },
  {
    "path": "packages/compiler-core/src/transform.ts",
    "chars": 2318,
    "preview": "import { NodeTypes } from \"./ast\";\nimport { TO_DISPLAY_STRING } from \"./runtimeHelpers\";\n\nexport function transform(root"
  },
  {
    "path": "packages/compiler-core/src/transforms/transformElement.ts",
    "chars": 794,
    "preview": "import { createVNodeCall, NodeTypes } from \"../ast\";\n\nexport function transformElement(node, context) {\n  if (node.type "
  },
  {
    "path": "packages/compiler-core/src/transforms/transformExpression.ts",
    "chars": 280,
    "preview": "import { NodeTypes } from \"../ast\";\n\nexport function transformExpression(node) {\n  if (node.type === NodeTypes.INTERPOLA"
  },
  {
    "path": "packages/compiler-core/src/transforms/transformText.ts",
    "chars": 1538,
    "preview": "import { NodeTypes } from \"../ast\";\nimport { isText } from \"../utils\";\n\nexport function transformText(node, context) {\n "
  },
  {
    "path": "packages/compiler-core/src/utils.ts",
    "chars": 149,
    "preview": "import { NodeTypes } from \"./ast\";\n\nexport function isText(node) {\n  return node.type === NodeTypes.INTERPOLATION || nod"
  },
  {
    "path": "packages/reactivity/__tests__/computed.spec.ts",
    "chars": 1094,
    "preview": "import { computed } from \"../src/computed\";\nimport { reactive } from \"../src/reactive\";\nimport {vi} from 'vitest'\n\ndescr"
  },
  {
    "path": "packages/reactivity/__tests__/dep.spec.ts",
    "chars": 116,
    "preview": "\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",
    "chars": 2871,
    "preview": "import { reactive } from \"../src/reactive\";\nimport { effect, stop } from \"../src/effect\";\nimport { vi } from \"vitest\";\n\n"
  },
  {
    "path": "packages/reactivity/__tests__/reactive.spec.ts",
    "chars": 1056,
    "preview": "import { reactive, isReactive, toRaw, reactiveMap } from \"../src/reactive\";\ndescribe(\"reactive\", () => {\n  test(\"Object\""
  },
  {
    "path": "packages/reactivity/__tests__/readonly.spec.ts",
    "chars": 761,
    "preview": "import { isProxy, isReactive, isReadonly, readonly } from \"../src/reactive\";\n\ndescribe(\"readonly\", () => {\n  it(\"should "
  },
  {
    "path": "packages/reactivity/__tests__/ref.spec.ts",
    "chars": 1605,
    "preview": "import { effect } from \"../src/effect\";\nimport { reactive } from \"../src/reactive\";\nimport { isRef, ref, unRef,proxyRefs"
  },
  {
    "path": "packages/reactivity/__tests__/shallowReadonly.spec.ts",
    "chars": 666,
    "preview": "import { isReactive, isReadonly, readonly, shallowReadonly } from \"../src/reactive\";\n\ndescribe(\"shallowReadonly\", () => "
  },
  {
    "path": "packages/reactivity/package.json",
    "chars": 261,
    "preview": "{\n  \"name\": \"@mini-vue/reactivity\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/reactivity\",\n  \"scripts\": {\n    \"t"
  },
  {
    "path": "packages/reactivity/src/baseHandlers.ts",
    "chars": 2453,
    "preview": "import { track, trigger } from \"./effect\";\nimport {\n  reactive,\n  ReactiveFlags,\n  reactiveMap,\n  readonly,\n  readonlyMa"
  },
  {
    "path": "packages/reactivity/src/computed.ts",
    "chars": 963,
    "preview": "import { createDep } from \"./dep\";\nimport { ReactiveEffect } from \"./effect\";\nimport { trackRefValue, triggerRefValue } "
  },
  {
    "path": "packages/reactivity/src/dep.ts",
    "chars": 107,
    "preview": "// 用于存储所有的 effect 对象\nexport function createDep(effects?) {\n  const dep = new Set(effects);\n  return dep;\n}\n"
  },
  {
    "path": "packages/reactivity/src/effect.ts",
    "chars": 3495,
    "preview": "import { createDep } from \"./dep\";\nimport { extend } from \"@mini-vue/shared\";\n\nlet activeEffect = void 0;\nlet shouldTrac"
  },
  {
    "path": "packages/reactivity/src/index.ts",
    "chars": 265,
    "preview": "export {\n  reactive,\n  readonly,\n  shallowReadonly,\n  isReadonly,\n  isReactive,\n  isProxy,\n} from \"./reactive\";\n\nexport "
  },
  {
    "path": "packages/reactivity/src/reactive.ts",
    "chars": 1777,
    "preview": "import {\n  mutableHandlers,\n  readonlyHandlers,\n  shallowReadonlyHandlers,\n} from \"./baseHandlers\";\n\nexport const reacti"
  },
  {
    "path": "packages/reactivity/src/ref.ts",
    "chars": 2186,
    "preview": "import { trackEffects, triggerEffects, isTracking } from \"./effect\";\nimport { createDep } from \"./dep\";\nimport { isObjec"
  },
  {
    "path": "packages/runtime-core/__tests__/apiWatch.spec.ts",
    "chars": 1237,
    "preview": "import { reactive } from \"@mini-vue/reactivity\";\nimport { watchEffect } from \"../src/apiWatch\";\nimport { nextTick } from"
  },
  {
    "path": "packages/runtime-core/__tests__/componentEmits.spec.ts",
    "chars": 1590,
    "preview": "import { nodeOps, render, h } from \"@mini-vue/runtime-test\";\nimport {vi} from 'vitest'\n\ndescribe(\"component: emits\", () "
  },
  {
    "path": "packages/runtime-core/__tests__/rendererComponent.spec.ts",
    "chars": 726,
    "preview": "import { h } from \"@mini-vue/runtime-dom\";\nimport { nodeOps, render, serializeInner } from \"@mini-vue/runtime-test\";\n\nde"
  },
  {
    "path": "packages/runtime-core/__tests__/rendererElement.spec.ts",
    "chars": 743,
    "preview": "import { h } from \"@mini-vue/runtime-core\";\nimport { nodeOps, render, serializeInner as inner } from \"@mini-vue/runtime-"
  },
  {
    "path": "packages/runtime-core/package.json",
    "chars": 313,
    "preview": "{\n  \"name\": \"@mini-vue/runtime-core\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/runtime-core\",\n  \"scripts\": {\n  "
  },
  {
    "path": "packages/runtime-core/src/.pnpm-debug.log",
    "chars": 1067,
    "preview": "{\n  \"0 debug pnpm:scope\": {\n    \"selected\": 1,\n    \"workspacePrefix\": \"/Users/cxr/projects/mini-vue/code/mini-vue\"\n  },\n"
  },
  {
    "path": "packages/runtime-core/src/apiInject.ts",
    "chars": 1218,
    "preview": "import { getCurrentInstance } from \"./component\";\n\nexport function provide(key, value) {\n  const currentInstance = getCu"
  },
  {
    "path": "packages/runtime-core/src/apiWatch.ts",
    "chars": 1206,
    "preview": "import { ReactiveEffect } from \"@mini-vue/reactivity\";\nimport { queuePreFlushCb } from \"./scheduler\";\n\n// Simple effect."
  },
  {
    "path": "packages/runtime-core/src/component.ts",
    "chars": 4132,
    "preview": "import { initProps } from \"./componentProps\";\nimport { initSlots } from \"./componentSlots\";\nimport { emit } from \"./comp"
  },
  {
    "path": "packages/runtime-core/src/componentEmits.ts",
    "chars": 589,
    "preview": "import { camelize, hyphenate, toHandlerKey } from \"@mini-vue/shared\";\nexport function emit(instance, event: string, ...r"
  },
  {
    "path": "packages/runtime-core/src/componentProps.ts",
    "chars": 246,
    "preview": "export function initProps(instance, rawProps) {\n  console.log(\"initProps\");\n\n  // TODO\n  // 应该还有 attrs 的概念\n  // attrs\n  "
  },
  {
    "path": "packages/runtime-core/src/componentPublicInstance.ts",
    "chars": 1193,
    "preview": "import { hasOwn } from \"@mini-vue/shared\";\n\nconst publicPropertiesMap = {\n  // 当用户调用 instance.proxy.$emit 时就会触发这个函数\n  //"
  },
  {
    "path": "packages/runtime-core/src/componentRenderUtils.ts",
    "chars": 1094,
    "preview": "export function shouldUpdateComponent(prevVNode, nextVNode) {\n  const { props: prevProps } = prevVNode;\n  const { props:"
  },
  {
    "path": "packages/runtime-core/src/componentSlots.ts",
    "chars": 782,
    "preview": "import { ShapeFlags } from \"@mini-vue/shared\";\nexport function initSlots(instance, children) {\n  const { vnode } = insta"
  },
  {
    "path": "packages/runtime-core/src/createApp.ts",
    "chars": 418,
    "preview": "import { createVNode } from \"./vnode\";\n\nexport function createAppAPI(render) {\n  return function createApp(rootComponent"
  },
  {
    "path": "packages/runtime-core/src/h.ts",
    "chars": 177,
    "preview": "import { createVNode } from \"./vnode\";\nexport const h = (type: any , props: any = null, children: string | Array<any> = "
  },
  {
    "path": "packages/runtime-core/src/helpers/renderSlot.ts",
    "chars": 1517,
    "preview": "import { createVNode, Fragment } from \"../vnode\";\n\n/**\n * Compiler runtime helper for rendering `<slot/>`\n * 用来 render s"
  },
  {
    "path": "packages/runtime-core/src/index.ts",
    "chars": 666,
    "preview": "export * from \"./h\";\nexport * from \"./createApp\";\nexport { getCurrentInstance, registerRuntimeCompiler } from \"./compone"
  },
  {
    "path": "packages/runtime-core/src/renderer.ts",
    "chars": 18847,
    "preview": "import { ShapeFlags } from \"@mini-vue/shared\";\nimport { createComponentInstance } from \"./component\";\nimport { queueJob "
  },
  {
    "path": "packages/runtime-core/src/scheduler.ts",
    "chars": 1286,
    "preview": "const queue: any[] = [];\nconst activePreFlushCbs: any = [];\n\nconst p = Promise.resolve();\nlet isFlushPending = false;\n\ne"
  },
  {
    "path": "packages/runtime-core/src/vnode.ts",
    "chars": 1872,
    "preview": "import { ShapeFlags } from \"@mini-vue/shared\";\n\nexport { createVNode as createElementVNode }\n\nexport const createVNode ="
  },
  {
    "path": "packages/runtime-dom/package.json",
    "chars": 394,
    "preview": "{\n  \"name\": \"@mini-vue/runtime-dom\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/runtime-dom\",\n  \"module\": \"dist/s"
  },
  {
    "path": "packages/runtime-dom/src/index.ts",
    "chars": 2257,
    "preview": "// 源码里面这些接口是由 runtime-dom 来实现\n// 这里先简单实现\n\nimport { isOn } from \"@mini-vue/shared\";\nimport { createRenderer } from \"@mini"
  },
  {
    "path": "packages/runtime-test/src/index.ts",
    "chars": 383,
    "preview": "// todo\n// 实现 render 的渲染接口\n// 实现序列化\nimport { createRenderer } from \"@mini-vue/runtime-core\";\nimport { extend } from \"@mi"
  },
  {
    "path": "packages/runtime-test/src/nodeOps.ts",
    "chars": 791,
    "preview": "export const enum NodeTypes {\n  ELEMENT = \"element\",\n  TEXT = \"TEXT\",\n}\n\nlet nodeId = 0;\n// 这个函数会在 runtime-core 初始化 elem"
  },
  {
    "path": "packages/runtime-test/src/patchProp.ts",
    "chars": 90,
    "preview": "export function patchProp(el, key, prevValue, nextValue) {\n  el.props[key] = nextValue;\n}\n"
  },
  {
    "path": "packages/runtime-test/src/serialize.ts",
    "chars": 1010,
    "preview": "// 把 node 给序列化\n// 测试的时候好对比\n\nimport { NodeTypes } from \"./nodeOps\";\n\n// 序列化: 把一个对象给处理成 string (进行流化)\nexport function seri"
  },
  {
    "path": "packages/shared/package.json",
    "chars": 224,
    "preview": "{\n  \"name\": \"@mini-vue/shared\",\n  \"version\": \"1.0.0\",\n  \"description\": \"@mini-vue/shared\",\n  \"scripts\": {\n    \"test\": \"e"
  },
  {
    "path": "packages/shared/src/index.ts",
    "chars": 1200,
    "preview": "export * from \"../src/shapeFlags\";\nexport * from \"../src/toDisplayString\";\n\nexport const isObject = (val) => {\n  return "
  },
  {
    "path": "packages/shared/src/shapeFlags.ts",
    "chars": 319,
    "preview": "// 组件的类型\nexport const enum ShapeFlags {\n    // 最后要渲染的 element 类型\n    ELEMENT = 1,\n    // 组件类型\n    STATEFUL_COMPONENT = 1"
  },
  {
    "path": "packages/shared/src/toDisplayString.ts",
    "chars": 67,
    "preview": "export const toDisplayString = (val) => {\n  return String(val);\n};\n"
  },
  {
    "path": "packages/vue/cypress/e2e/apiInject.cy.js",
    "chars": 188,
    "preview": "describe(\"apiInject\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/apiInject/\");\n    cy.co"
  },
  {
    "path": "packages/vue/cypress/e2e/componentEmit.cy.js",
    "chars": 176,
    "preview": "describe(\"componentEmit\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/componentEmit/\");\n "
  },
  {
    "path": "packages/vue/cypress/e2e/componentSlots.cy.js",
    "chars": 387,
    "preview": "describe(\"componentSlots\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/componentSlots/\");"
  },
  {
    "path": "packages/vue/cypress/e2e/componentUpdate.cy.js",
    "chars": 217,
    "preview": "describe(\"componentUpdate\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/componentUpdate/\""
  },
  {
    "path": "packages/vue/cypress/e2e/customRenderer.cy.js",
    "chars": 167,
    "preview": "describe(\"customRenderer\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/customRenderer/\");"
  },
  {
    "path": "packages/vue/cypress/e2e/getCurrentInstance.cy.js",
    "chars": 173,
    "preview": "describe(\"currentInstance\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/getCurrentInstanc"
  },
  {
    "path": "packages/vue/cypress/e2e/helloworld.cy.js",
    "chars": 208,
    "preview": "describe(\"helloworld\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/helloWorld/\");\n    cy."
  },
  {
    "path": "packages/vue/cypress/e2e/nextTicker.cy.js",
    "chars": 250,
    "preview": "describe(\"nextTicker\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/nextTicker/\");\n    cy."
  },
  {
    "path": "packages/vue/cypress/e2e/patchChildren.cy.js",
    "chars": 379,
    "preview": "describe(\"patchChildren\", () => {\n  it(\"render\", () => {\n    cy.visit(\"http://localhost:3000/example/patchChildren/\");\n\n"
  },
  {
    "path": "packages/vue/cypress/fixtures/example.json",
    "chars": 155,
    "preview": "{\n  \"name\": \"Using fixtures to represent data\",\n  \"email\": \"hello@cypress.io\",\n  \"body\": \"Fixtures are a great way to mo"
  },
  {
    "path": "packages/vue/cypress/support/commands.js",
    "chars": 837,
    "preview": "// ***********************************************\n// This example commands.js shows you how to\n// create various custom"
  },
  {
    "path": "packages/vue/cypress/support/e2e.js",
    "chars": 667,
    "preview": "// ***********************************************************\n// This example support/e2e.js is processed and\n// loaded"
  },
  {
    "path": "packages/vue/cypress.config.js",
    "chars": 194,
    "preview": "const { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  e2e: {\n    setupNodeEvents(on, config) {\n"
  },
  {
    "path": "packages/vue/dist/mini-vue.cjs.js",
    "chars": 48582,
    "preview": "'use strict';\n\nObject.defineProperty(exports, '__esModule', { value: true });\n\nvar ShapeFlags;\n(function (ShapeFlags) {\n"
  },
  {
    "path": "packages/vue/dist/mini-vue.esm-bundler.js",
    "chars": 48001,
    "preview": "var ShapeFlags;\n(function (ShapeFlags) {\n    ShapeFlags[ShapeFlags[\"ELEMENT\"] = 1] = \"ELEMENT\";\n    ShapeFlags[ShapeFlag"
  },
  {
    "path": "packages/vue/example/apiInject/App.js",
    "chars": 919,
    "preview": "// 组件 provide 和 inject 功能\nimport {\n  h,\n  provide,\n  inject,\n} from \"../../dist/mini-vue.esm-bundler.js\";\n\nconst Provide"
  },
  {
    "path": "packages/vue/example/apiInject/index.html",
    "chars": 487,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/compiler-base/App.js",
    "chars": 342,
    "preview": "// 最简单的情况\n// template 只有一个 interpolation\n// export default {\n//   template: `{{msg}}`,\n//   setup() {\n//     return {\n//"
  },
  {
    "path": "packages/vue/example/compiler-base/index.html",
    "chars": 489,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/componentEmit/App.js",
    "chars": 651,
    "preview": "// 组件 emit 逻辑 demo\n// click emit 发出 change, 可以触发 App 组件内定义好的侦听函数\n// 允许接收多个参数\nimport { h, ref, reactive } from \"../../dis"
  },
  {
    "path": "packages/vue/example/componentEmit/Child.js",
    "chars": 292,
    "preview": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  setup(props, "
  },
  {
    "path": "packages/vue/example/componentEmit/index.html",
    "chars": 487,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/componentProxy/App.js",
    "chars": 492,
    "preview": "// 在 render 中使用 proxy 调用 emit 函数\n// 也可以直接使用 this\n// 验证 proxy 的实现逻辑\nimport { h, ref, reactive } from \"../../dist/mini-vue"
  },
  {
    "path": "packages/vue/example/componentProxy/Child.js",
    "chars": 540,
    "preview": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  setup(props, "
  },
  {
    "path": "packages/vue/example/componentProxy/index.html",
    "chars": 487,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/componentSlots/App.js",
    "chars": 533,
    "preview": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nimport Child from \"./Child.js\";\n\nexport default {"
  },
  {
    "path": "packages/vue/example/componentSlots/Child.js",
    "chars": 390,
    "preview": "import { h, ref, reactive, renderSlot } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  s"
  },
  {
    "path": "packages/vue/example/componentSlots/index.html",
    "chars": 487,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/componentUpdate/App.js",
    "chars": 628,
    "preview": "// 在 render 中使用 proxy 调用 emit 函数\n// 也可以直接使用 this\n// 验证 proxy 的实现逻辑\nimport { h, ref } from \"../../dist/mini-vue.esm-bundl"
  },
  {
    "path": "packages/vue/example/componentUpdate/Child.js",
    "chars": 228,
    "preview": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  setup(props, "
  },
  {
    "path": "packages/vue/example/componentUpdate/index.html",
    "chars": 487,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/createTextVnode/App.js",
    "chars": 270,
    "preview": "import { h, ref, reactive, createTextVNode } from \"../../dist/mini-vue.esm-bundler.js\";\n\nexport default {\n  name: \"App\","
  },
  {
    "path": "packages/vue/example/createTextVnode/index.html",
    "chars": 487,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/customRenderer/App.js",
    "chars": 534,
    "preview": "import { h, ref } from \"../../dist/mini-vue.esm-bundler.js\";\nimport { game } from \"./game.js\";\n\nexport default {\n  name:"
  },
  {
    "path": "packages/vue/example/customRenderer/game.js",
    "chars": 174,
    "preview": "export const game = new PIXI.Application({\n  width: 500,\n  height: 500,\n});\n\ndocument.body.append(game.view);\n\nexport fu"
  },
  {
    "path": "packages/vue/example/customRenderer/index.html",
    "chars": 443,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/customRenderer/main.js",
    "chars": 166,
    "preview": "import App from \"./App.js\";\nimport { createApp } from \"./renderer.js\";\nimport { createRootContainer } from \"./game.js\";\n"
  },
  {
    "path": "packages/vue/example/customRenderer/renderer.js",
    "chars": 519,
    "preview": "import { createRenderer } from \"../../dist/mini-vue.esm-bundler.js\";\n\n// 给基于 pixi.js 的渲染函数\nconst renderer = createRender"
  },
  {
    "path": "packages/vue/example/getCurrentInstance/App.js",
    "chars": 283,
    "preview": "// 可以在 setup 中使用 getCurrentInstance 获取组件实例对象\nimport { h, getCurrentInstance } from \"../../dist/mini-vue.esm-bundler.js\";"
  },
  {
    "path": "packages/vue/example/getCurrentInstance/index.html",
    "chars": 487,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/helloWorld/App.js",
    "chars": 550,
    "preview": "import { h, ref } from \"../../dist/mini-vue.esm-bundler.js\";\n\nconst count = ref(0);\n\nconst HelloWorld = {\n  name: \"Hello"
  },
  {
    "path": "packages/vue/example/helloWorld/index.html",
    "chars": 287,
    "preview": "\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-widt"
  },
  {
    "path": "packages/vue/example/helloWorld/main.js",
    "chars": 185,
    "preview": "import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\nimport App from \"./App.js\";\n\nconst rootContainer = docum"
  },
  {
    "path": "packages/vue/example/nextTicker/App.js",
    "chars": 250,
    "preview": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nimport NextTicker from \"./NextTicker.js\";\n\nexport"
  },
  {
    "path": "packages/vue/example/nextTicker/NextTicker.js",
    "chars": 806,
    "preview": "// 测试 nextTick 逻辑\nimport { h, ref } from \"../../dist/mini-vue.esm-bundler.js\";\n\n// 如果 for 循环改变 count 的值 100 次的话\n// 会同时触发"
  },
  {
    "path": "packages/vue/example/nextTicker/index.html",
    "chars": 287,
    "preview": "\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-widt"
  },
  {
    "path": "packages/vue/example/nextTicker/main.js",
    "chars": 183,
    "preview": "import {createApp} from \"../../dist/mini-vue.esm-bundler.js\";\nimport App from \"./App.js\";\n\nconst rootContainer = documen"
  },
  {
    "path": "packages/vue/example/patchChildren/App.js",
    "chars": 572,
    "preview": "import {h} from '../../dist/mini-vue.esm-bundler.js'\nimport ArrayToText from \"./ArrayToText.js\";\nimport TextToText from "
  },
  {
    "path": "packages/vue/example/patchChildren/ArrayToArray.js",
    "chars": 5470,
    "preview": "// 老的是 array\n// 新的是 array\n\nimport { ref, h } from \"../../dist/mini-vue.esm-bundler.js\";\n\n// 1. 左侧的对比\n// (a b) c\n// (a b)"
  },
  {
    "path": "packages/vue/example/patchChildren/ArrayToText.js",
    "chars": 494,
    "preview": "// 老的是 array\n// 新的是 text\n\nimport { ref, h } from \"../../dist/mini-vue.esm-bundler.js\";\nconst nextChildren = \"newChildren"
  },
  {
    "path": "packages/vue/example/patchChildren/TextToArray.js",
    "chars": 520,
    "preview": "// 新的是 array\n// 老的是 text\nimport { ref, h } from \"../../dist/mini-vue.esm-bundler.js\";\n\nconst prevChildren = \"oldChild\";\n"
  },
  {
    "path": "packages/vue/example/patchChildren/TextToText.js",
    "chars": 461,
    "preview": "// 新的是 text\n// 老的是 text\nimport { ref, h } from \"../../dist/mini-vue.esm-bundler.js\";\n\nconst prevChildren = \"oldChild\";\nc"
  },
  {
    "path": "packages/vue/example/patchChildren/index.html",
    "chars": 282,
    "preview": "\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-widt"
  },
  {
    "path": "packages/vue/example/patchChildren/main.js",
    "chars": 185,
    "preview": "import { createApp } from \"../../dist/mini-vue.esm-bundler.js\";\nimport App from \"./App.js\";\n\nconst rootContainer = docum"
  },
  {
    "path": "packages/vue/example/renderComponent/App.js",
    "chars": 294,
    "preview": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nimport Child from \"./Child.js\";\n\nexport default {"
  },
  {
    "path": "packages/vue/example/renderComponent/Child.js",
    "chars": 297,
    "preview": "import { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\nexport default {\n  name: \"Child\",\n  setup(props, "
  },
  {
    "path": "packages/vue/example/renderComponent/index.html",
    "chars": 487,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/example/setupStateRenderComponent/App.js",
    "chars": 515,
    "preview": "// 在 render 中可以通过 this.xxx 访问到 setup 返回的对象\nimport { h, ref, reactive } from \"../../dist/mini-vue.esm-bundler.js\";\n\nexpor"
  },
  {
    "path": "packages/vue/example/setupStateRenderComponent/index.html",
    "chars": 487,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "packages/vue/index.js",
    "chars": 184,
    "preview": "'use strict'\n\nif (process.env.NODE_ENV === 'production') {\n  // module.exports = require('./dist/mini-vue.cjs.prod.js')\n"
  },
  {
    "path": "packages/vue/package.json",
    "chars": 470,
    "preview": "{\n  \"name\": \"mini-vue\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"cyp"
  },
  {
    "path": "packages/vue/src/index.ts",
    "chars": 534,
    "preview": "// 这个文件充当 vue 模块\nimport * as runtimeDom from \"@mini-vue/runtime-dom\";\nimport { registerRuntimeCompiler } from \"@mini-vue"
  },
  {
    "path": "pnpm-workspace.yaml",
    "chars": 27,
    "preview": "packages:\n  - 'packages/*'\n"
  },
  {
    "path": "rollup.config.js",
    "chars": 970,
    "preview": "import typescript from \"@rollup/plugin-typescript\";\nimport sourceMaps from \"rollup-plugin-sourcemaps\";\nimport resolve fr"
  },
  {
    "path": "tsconfig.json",
    "chars": 524,
    "preview": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"strict\": true,\n    \"rootDir\": \".\",\n    \"moduleResolution\": \"node\",\n   "
  },
  {
    "path": "vitest.config.ts",
    "chars": 302,
    "preview": "import { defineConfig } from \"vitest/config\";\nimport path from \"path\";\n\nexport default defineConfig({\n  test: {\n    glob"
  }
]

About this extraction

This page contains the full source code of the cuixiaorui/mini-vue GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 135 files (221.7 KB), approximately 66.6k tokens, and a symbol index with 470 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!