├── pnpm-workspace.yaml
├── packages
├── compiler-core
│ ├── src
│ │ ├── index.ts
│ │ ├── utils.ts
│ │ ├── runtimeHelpers.ts
│ │ ├── transforms
│ │ │ ├── transformExpression.ts
│ │ │ ├── transformElement.ts
│ │ │ └── transformText.ts
│ │ ├── ast.ts
│ │ ├── compile.ts
│ │ ├── transform.ts
│ │ ├── codegen.ts
│ │ └── parse.ts
│ ├── index.js
│ ├── tsup.config.ts
│ ├── __test__
│ │ ├── __snapshots__
│ │ │ └── codegen.test.ts.snap
│ │ ├── transform.test.ts
│ │ ├── codegen.test.ts
│ │ └── parse.test.ts
│ └── package.json
├── vue3
│ ├── index.js
│ ├── tsup.config.ts
│ ├── src
│ │ └── index.ts
│ └── package.json
├── reactivity
│ ├── index.js
│ ├── tsup.config.ts
│ ├── src
│ │ ├── index.ts
│ │ ├── computed.ts
│ │ ├── reactive.ts
│ │ ├── baseHandlers.ts
│ │ ├── ref.ts
│ │ └── effect.ts
│ ├── __test__
│ │ ├── shallowReadonly.test.ts
│ │ ├── reactive.test.ts
│ │ ├── readonly.test.ts
│ │ ├── computed.test.ts
│ │ ├── ref.test.ts
│ │ └── effect.test.ts
│ └── package.json
├── shared
│ ├── index.js
│ ├── src
│ │ ├── toDisplayString.ts
│ │ ├── shapeFlags.ts
│ │ └── index.ts
│ ├── tsup.config.ts
│ └── package.json
├── runtime-core
│ ├── index.js
│ ├── src
│ │ ├── componentProps.ts
│ │ ├── h.ts
│ │ ├── helpers
│ │ │ └── renderSlots.ts
│ │ ├── componentUpdateUtils.ts
│ │ ├── componentEmit.ts
│ │ ├── createApp.ts
│ │ ├── index.ts
│ │ ├── componentSlots.ts
│ │ ├── scheduler.ts
│ │ ├── componentPublicInstance.ts
│ │ ├── apiInject.ts
│ │ ├── vnode.ts
│ │ ├── component.ts
│ │ └── renderer.ts
│ ├── tsup.config.ts
│ └── package.json
└── runtime-dom
│ ├── index.js
│ ├── tsup.config.ts
│ ├── package.json
│ └── src
│ └── index.ts
├── .prettierrc
├── examples
├── update
│ ├── main.js
│ ├── index.html
│ └── App.js
├── apiInject
│ ├── main.js
│ ├── index.html
│ └── App.js
├── compilerBase
│ ├── main.js
│ ├── App.js
│ └── index.html
├── helloword
│ ├── main.js
│ ├── Foo.js
│ ├── index.html
│ └── App.js
├── nextTick
│ ├── main.js
│ ├── index.html
│ └── App.js
├── componentEmit
│ ├── main.js
│ ├── index.html
│ ├── App.js
│ └── Foo.js
├── componentSlot
│ ├── main.js
│ ├── Foo.js
│ ├── index.html
│ └── App.js
├── componentUpdate
│ ├── main.js
│ ├── Child.js
│ ├── index.html
│ └── App.js
├── currentInstance
│ ├── main.js
│ ├── Foo.js
│ ├── App.js
│ └── index.html
├── patchChildren
│ ├── main.js
│ ├── index.html
│ ├── TextToText.js
│ ├── ArrayToText.js
│ ├── TextToArray.js
│ ├── App.js
│ └── ArrayToArray.js
└── customRender
│ ├── App.js
│ ├── index.html
│ └── main.js
├── vite.config.ts
├── .gitignore
├── tsconfig.json
├── .prettierignore
├── package.json
├── README.md
└── pnpm-lock.yaml
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "packages/**"
3 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/index.ts:
--------------------------------------------------------------------------------
1 | export { baseCompile } from "./compile"
2 |
--------------------------------------------------------------------------------
/packages/vue3/index.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = require("./dist/index.js")
4 |
--------------------------------------------------------------------------------
/packages/reactivity/index.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = require("./dist/index.js")
4 |
--------------------------------------------------------------------------------
/packages/shared/index.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = require("./dist/index.js")
4 |
--------------------------------------------------------------------------------
/packages/compiler-core/index.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = require("./dist/index.js")
4 |
--------------------------------------------------------------------------------
/packages/runtime-core/index.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = require("./dist/index.js")
4 |
--------------------------------------------------------------------------------
/packages/runtime-dom/index.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = require("./dist/index.js")
4 |
--------------------------------------------------------------------------------
/packages/shared/src/toDisplayString.ts:
--------------------------------------------------------------------------------
1 | export function toDisplayString(value) {
2 | return String(value)
3 | }
4 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/componentProps.ts:
--------------------------------------------------------------------------------
1 | export function initProps(instance, rawProps) {
2 | instance.props = rawProps || {}
3 |
4 | // todo attrs
5 | }
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": false,
3 | "tabWidth": 2,
4 | "printWidth": 80,
5 | "singleQuote": false,
6 | "trailingComma": "all",
7 | "semi": false
8 | }
9 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/h.ts:
--------------------------------------------------------------------------------
1 | import { createVNode } from "./vnode"
2 |
3 | export function h(type, props?, children?) {
4 | return createVNode(type, props, children)
5 | }
6 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { NodeTypes } from "./ast"
2 |
3 | export function isText(node) {
4 | return node.type === NodeTypes.TEXT || node.type === NodeTypes.INTERPOLATION
5 | }
6 |
--------------------------------------------------------------------------------
/examples/update/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/examples/apiInject/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/examples/compilerBase/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/examples/helloword/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/examples/nextTick/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/examples/componentEmit/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/examples/componentSlot/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/examples/componentUpdate/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/examples/currentInstance/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/examples/patchChildren/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const rootContainer = document.querySelector("#app")
5 | createApp(App).mount(rootContainer)
6 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vitest/config"
2 | import AutoImport from "unplugin-auto-import/vite"
3 |
4 | export default defineConfig({
5 | plugins: [
6 | AutoImport({
7 | imports: ["vitest"],
8 | dts: true,
9 | }),
10 | ],
11 | })
12 |
--------------------------------------------------------------------------------
/packages/shared/src/shapeFlags.ts:
--------------------------------------------------------------------------------
1 | export const enum ShapeFlags {
2 | ELEMENT = 1, // 0b00000001
3 | STATEFUL_COMPONENT = 1 << 1, // 0b00000010
4 | TEXT_CHILDREN = 1 << 2, // 0b00000100
5 | ARRAY_CHILDREN = 1 << 3, // 0b00001000
6 | SLOT_CHILDREN = 1 << 4, // 0b00010000
7 | }
8 |
--------------------------------------------------------------------------------
/packages/shared/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup"
2 |
3 | export default defineConfig({
4 | name: "@mini-vue3/shared",
5 | entry: [`./src/index.ts`],
6 | format: ["esm", "cjs"],
7 | clean: true,
8 | // minify: true,
9 | outDir: `dist`,
10 | target: "node16",
11 | })
12 |
--------------------------------------------------------------------------------
/examples/customRender/App.js:
--------------------------------------------------------------------------------
1 | import { h } from "../../packages/vue3/dist/index.mjs"
2 |
3 | export const App = {
4 | name: "App",
5 | setup() {
6 | return {
7 | x: 100,
8 | y: 100,
9 | }
10 | },
11 | render() {
12 | return h("rect", { x: this.x, y: this.y })
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/runtimeHelpers.ts:
--------------------------------------------------------------------------------
1 | export const TO_DISPLAY_STRING = Symbol("toDisplayString")
2 | export const CREATE_ELEMENT_VNODE = Symbol("createElementVNode")
3 |
4 | export const helperMapName = {
5 | [TO_DISPLAY_STRING]: "toDisplayString",
6 | [CREATE_ELEMENT_VNODE]: "createElementVNode",
7 | }
8 |
--------------------------------------------------------------------------------
/packages/reactivity/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup"
2 |
3 | export default defineConfig({
4 | name: "@mini-vue3/reactivity",
5 | entry: [`./src/index.ts`],
6 | format: ["esm", "cjs"],
7 | clean: true,
8 | // minify: true,
9 | outDir: `dist`,
10 | target: "node16",
11 | })
12 |
--------------------------------------------------------------------------------
/packages/runtime-dom/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup"
2 |
3 | export default defineConfig({
4 | name: "@mini-vue3/runtime-dom",
5 | entry: [`./src/index.ts`],
6 | format: ["esm", "cjs"],
7 | clean: true,
8 | // minify: true,
9 | outDir: `dist`,
10 | target: "node16",
11 | })
12 |
--------------------------------------------------------------------------------
/packages/compiler-core/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup"
2 |
3 | export default defineConfig({
4 | name: "@mini-vue3/compiler-core",
5 | entry: [`./src/index.ts`],
6 | format: ["esm", "cjs"],
7 | clean: true,
8 | // minify: true,
9 | outDir: `dist`,
10 | target: "node16",
11 | })
12 |
--------------------------------------------------------------------------------
/packages/reactivity/src/index.ts:
--------------------------------------------------------------------------------
1 | export { ref, isRef, unRef, proxyRefs } from "./ref"
2 | export {
3 | reactive,
4 | readonly,
5 | shallowReadonly,
6 | isReactive,
7 | isReadonly,
8 | isProxy,
9 | } from "./reactive"
10 | export { computed } from "./computed"
11 | export { effect } from "./effect"
12 |
--------------------------------------------------------------------------------
/packages/runtime-core/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup"
2 |
3 | export default defineConfig({
4 | name: "@mini-vue3/runtime-core",
5 | entry: [`./src/index.ts`],
6 | format: ["esm", "cjs"],
7 | clean: true,
8 | // minify: true,
9 | outDir: `dist`,
10 | target: "node16",
11 | })
12 |
--------------------------------------------------------------------------------
/examples/componentUpdate/Child.js:
--------------------------------------------------------------------------------
1 | import { h } from "../../packages/vue3/dist/index.mjs"
2 |
3 | export const Child = {
4 | name: "Child",
5 | setup(props) {},
6 | render() {
7 | return h("div", {}, [
8 | h("div", {}, [h("div", {}, "child - props - msg: " + this.$props.msg)]),
9 | ])
10 | },
11 | }
12 |
--------------------------------------------------------------------------------
/examples/currentInstance/Foo.js:
--------------------------------------------------------------------------------
1 | import { h, getCurrentInstance } from "../../packages/vue3/dist/index.mjs"
2 |
3 | export const Foo = {
4 | setup() {
5 | const instance = getCurrentInstance()
6 | console.log("Foo instance: ", instance)
7 | },
8 | render() {
9 | return h("div", {}, "foo")
10 | },
11 | }
12 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/helpers/renderSlots.ts:
--------------------------------------------------------------------------------
1 | import { createVNode, Fragment } from "../vnode"
2 |
3 | export function renderSlots(slots, name, props) {
4 | const slot = slots[name]
5 | if (slot) {
6 | if (typeof slot === "function") {
7 | return createVNode(Fragment, {}, slot(props))
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/componentUpdateUtils.ts:
--------------------------------------------------------------------------------
1 | export function shouldUpdateComponent(prevVNode, nextVNode) {
2 | const { props: prevProps } = prevVNode
3 | const { props: nextProps } = nextVNode
4 |
5 | for (const key in nextProps) {
6 | if (nextProps[key] !== prevProps[key]) return true
7 | }
8 |
9 | return false
10 | }
11 |
--------------------------------------------------------------------------------
/examples/compilerBase/App.js:
--------------------------------------------------------------------------------
1 | import { ref } from "../../packages/vue3/dist/index.mjs"
2 |
3 | export const App = {
4 | name: "App",
5 | template: `
hi,{{message}} count: {{count}}
`,
6 | setup() {
7 | const count = (window.count = ref(1))
8 |
9 | return {
10 | message: "mini-vue3",
11 | count,
12 | }
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/transforms/transformExpression.ts:
--------------------------------------------------------------------------------
1 | import { NodeTypes } from "../ast"
2 |
3 | export function transformExpression(node) {
4 | if (node.type === NodeTypes.INTERPOLATION)
5 | node.content = processExpression(node.content)
6 | }
7 |
8 | function processExpression(node) {
9 | node.content = `_ctx.${node.content}`
10 | return node
11 | }
12 |
--------------------------------------------------------------------------------
/examples/helloword/Foo.js:
--------------------------------------------------------------------------------
1 | import { h } from "../../packages/vue3/dist/index.mjs"
2 | export const Foo = {
3 | setup(props) {
4 | // props.count
5 | console.log("foo props before: ", props)
6 |
7 | // readonly props (imutable)
8 | props.count++
9 | console.log("foo props after: ", props)
10 | },
11 | render() {
12 | return h("div", {}, "foo: " + this.count)
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/examples/componentSlot/Foo.js:
--------------------------------------------------------------------------------
1 | import { h, renderSlots } from "../../packages/vue3/dist/index.mjs"
2 | export const Foo = {
3 | name: "Foo",
4 | setup(props) {},
5 | render() {
6 | const foo = h("p", {}, "foo")
7 | const age = 18
8 | return h("div", {}, [
9 | renderSlots(this.$slots, "header", { age }),
10 | foo,
11 | renderSlots(this.$slots, "footer"),
12 | ])
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/examples/currentInstance/App.js:
--------------------------------------------------------------------------------
1 | import { h, getCurrentInstance } from "../../packages/vue3/dist/index.mjs"
2 | import { Foo } from "./Foo.js"
3 |
4 | export const App = {
5 | name: "App",
6 | setup() {
7 | const instance = getCurrentInstance()
8 | console.log("App instance: ", instance)
9 | },
10 | render() {
11 | return h("div", {}, [h("p", {}, "currentInstance demo"), h(Foo)])
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/examples/update/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | update
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/vue3/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup"
2 |
3 | export default defineConfig({
4 | name: "mini-vue3",
5 | entry: [`./src/index.ts`],
6 | format: ["esm", "cjs"],
7 | clean: true,
8 | // minify: true,
9 | outDir: `dist`,
10 | target: "node16",
11 | noExternal: [
12 | "@mini-vue3/reactivity",
13 | "@mini-vue3/runtime-dom",
14 | "@mini-vue3/compiler-core",
15 | ],
16 | })
17 |
--------------------------------------------------------------------------------
/examples/apiInject/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | apiInject
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/nextTick/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | nextTick
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/compilerBase/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | compilerBase
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/componentEmit/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | componentEmit
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/componentSlot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | componentSlots
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/componentUpdate/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | componentUpdate
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/currentInstance/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | currentInstance
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/patchChildren/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | patchChildren
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/componentEmit.ts:
--------------------------------------------------------------------------------
1 | import { camelize, toHandlerKey } from "@mini-vue3/shared"
2 |
3 | export function emit(instance, event, ...args) {
4 | // instance.props => event
5 | // 取到对应的事件监听函数
6 | const { props } = instance
7 |
8 | // TPP
9 | // 先写一个特定的行为 => 重构成通用的行为
10 |
11 | const handlerName = toHandlerKey(camelize(event))
12 | const handler = props[handlerName]
13 | handler && handler(...args)
14 | }
15 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/createApp.ts:
--------------------------------------------------------------------------------
1 | import { createVNode } from "./vnode"
2 |
3 | export function createAppApi(render) {
4 | return function createApp(rootComponent) {
5 | return {
6 | mount(rootContainer) {
7 | // 先转换为 VNode,component => VNode
8 | // 后续所有逻辑操作,都基于 VNode
9 | const vnode = createVNode(rootComponent)
10 |
11 | render(vnode, rootContainer)
12 | },
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist
13 | dist-ssr
14 | coverage
15 | *.local
16 |
17 | /cypress/videos/
18 | /cypress/screenshots/
19 |
20 | # Editor directories and files
21 | .vscode/*
22 | !.vscode/extensions.json
23 | .idea
24 | *.suo
25 | *.ntvs*
26 | *.njsproj
27 | *.sln
28 | *.sw?
29 |
30 | auto-imports.d.ts
31 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/ast.ts:
--------------------------------------------------------------------------------
1 | import { CREATE_ELEMENT_VNODE } from "./runtimeHelpers"
2 |
3 | export const enum NodeTypes {
4 | INTERPOLATION,
5 | SIMPLE_EXPRESSION,
6 | ELEMENT,
7 | TEXT,
8 | ROOT,
9 | COMPOUND_EXPRESSION,
10 | }
11 |
12 | export function createVNodeCall(context, tag, props, children) {
13 | context.helper(CREATE_ELEMENT_VNODE)
14 |
15 | return {
16 | type: NodeTypes.ELEMENT,
17 | tag,
18 | props,
19 | children,
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/vue3/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "@mini-vue3/runtime-dom"
2 |
3 | import { baseCompile } from "@mini-vue3/compiler-core"
4 | import * as runtimeDom from "@mini-vue3/runtime-dom"
5 | import { registerRuntimeCompiler } from "@mini-vue3/runtime-dom"
6 |
7 | function compileToFunction(template) {
8 | const { code } = baseCompile(template)
9 | const render = new Function("Vue", code)(runtimeDom)
10 | return render
11 | }
12 |
13 | registerRuntimeCompiler(compileToFunction)
14 |
--------------------------------------------------------------------------------
/examples/componentEmit/App.js:
--------------------------------------------------------------------------------
1 | import { h } from "../../packages/vue3/dist/index.mjs"
2 | import { Foo } from "./Foo.js"
3 |
4 | export const App = {
5 | name: "App",
6 | setup(props) {},
7 | render() {
8 | return h("div", {}, [
9 | h("div", {}, "App"),
10 | h(Foo, {
11 | onAdd(a, b) {
12 | console.log("App onAdd", a, b)
13 | },
14 | onAddFoo() {
15 | console.log("App onAddFoo")
16 | },
17 | }),
18 | ])
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "useDefineForClassFields": true,
5 | "module": "esnext",
6 | "moduleResolution": "node",
7 | "strict": true,
8 | "jsx": "preserve",
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "esModuleInterop": true,
12 | "lib": ["esnext", "dom"],
13 | "types": ["vitest/globals"],
14 | "noImplicitAny": false
15 | },
16 | "include": ["packages/*/src", "packages/*/__test__"]
17 | }
18 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist
13 | dist-ssr
14 | coverage
15 | *.local
16 |
17 | /cypress/videos/
18 | /cypress/screenshots/
19 |
20 | # Editor directories and files
21 | .vscode/*
22 | !.vscode/extensions.json
23 | .idea
24 | *.suo
25 | *.ntvs*
26 | *.njsproj
27 | *.sln
28 | *.sw?
29 |
30 | auto-imports.d.ts
31 | pnpm-lock.yaml
32 | __snapshots__
33 |
--------------------------------------------------------------------------------
/examples/customRender/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | customRender
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/index.ts:
--------------------------------------------------------------------------------
1 | export { h } from "./h"
2 | export { renderSlots } from "./helpers/renderSlots"
3 | export { createTextVNode, createVNode as createElementVNode } from "./vnode"
4 | export { getCurrentInstance, registerRuntimeCompiler } from "./component"
5 | export { provide, inject } from "./apiInject"
6 | export { createRenderer } from "./renderer"
7 | export { nextTick } from "./scheduler"
8 | export { toDisplayString } from "@mini-vue3/shared"
9 | export * from "@mini-vue3/reactivity"
10 |
--------------------------------------------------------------------------------
/examples/componentSlot/App.js:
--------------------------------------------------------------------------------
1 | import { h, createTextVNode } from "../../packages/vue3/dist/index.mjs"
2 | import { Foo } from "./Foo.js"
3 |
4 | export const App = {
5 | name: "App",
6 | setup(props) {},
7 | render() {
8 | const foo = h(
9 | Foo,
10 | {},
11 | {
12 | header: ({ age }) => [
13 | h("p", {}, "123" + age),
14 | createTextVNode("text node"),
15 | ],
16 | footer: () => h("p", {}, "456"),
17 | },
18 | )
19 | return h("div", {}, [app, foo])
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/examples/patchChildren/TextToText.js:
--------------------------------------------------------------------------------
1 | import { h, ref } from "../../packages/vue3/dist/index.mjs"
2 |
3 | const nextChildren = "nextChildren"
4 | const prevChildren = "prevChildren"
5 |
6 | export default {
7 | name: "TextToText",
8 | setup() {
9 | const isChange = ref(false)
10 | window.isChange = isChange
11 |
12 | return {
13 | isChange,
14 | }
15 | },
16 | render() {
17 | const self = this
18 | return self.isChange === true
19 | ? h("div", {}, nextChildren)
20 | : h("div", {}, prevChildren)
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/examples/patchChildren/ArrayToText.js:
--------------------------------------------------------------------------------
1 | import { h, ref } from "../../packages/vue3/dist/index.mjs"
2 |
3 | const nextChildren = "nextChildren"
4 | const prevChildren = [h("div", {}, "A"), h("div", {}, "B")]
5 |
6 | export default {
7 | name: "ArrayToText",
8 | setup() {
9 | const isChange = ref(false)
10 | window.isChange = isChange
11 |
12 | return {
13 | isChange,
14 | }
15 | },
16 | render() {
17 | const self = this
18 | return self.isChange === true
19 | ? h("div", {}, nextChildren)
20 | : h("div", {}, prevChildren)
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/examples/patchChildren/TextToArray.js:
--------------------------------------------------------------------------------
1 | import { h, ref } from "../../packages/vue3/dist/index.mjs"
2 |
3 | const nextChildren = [h("div", {}, "A"), h("div", {}, "B")]
4 | const prevChildren = "prevChildren"
5 |
6 | export default {
7 | name: "TextToArray",
8 | setup() {
9 | const isChange = ref(false)
10 | window.isChange = isChange
11 |
12 | return {
13 | isChange,
14 | }
15 | },
16 | render() {
17 | const self = this
18 | return self.isChange === true
19 | ? h("div", {}, nextChildren)
20 | : h("div", {}, prevChildren)
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/compile.ts:
--------------------------------------------------------------------------------
1 | import { generate } from "./codegen"
2 | import { baseParse } from "./parse"
3 | import { transform } from "./transform"
4 | import { transformElement } from "./transforms/transformElement"
5 | import { transformExpression } from "./transforms/transformExpression"
6 | import { transformText } from "./transforms/transformText"
7 |
8 | export function baseCompile(template) {
9 | const ast = baseParse(template)
10 | transform(ast, {
11 | nodeTransforms: [transformExpression, transformElement, transformText],
12 | })
13 | return generate(ast)
14 | }
15 |
--------------------------------------------------------------------------------
/examples/componentEmit/Foo.js:
--------------------------------------------------------------------------------
1 | import { h } from "../../packages/vue3/dist/index.mjs"
2 | export const Foo = {
3 | name: "Foo",
4 | setup(props, { emit }) {
5 | const emitAdd = () => {
6 | console.log("Foo emitAdd")
7 | emit("add", 1, 2)
8 | emit("add-foo")
9 | }
10 |
11 | return {
12 | emitAdd,
13 | }
14 | },
15 | render() {
16 | const btn = h(
17 | "button",
18 | {
19 | onClick: this.emitAdd,
20 | },
21 | "emitAdd",
22 | )
23 |
24 | const foo = h("p", {}, "foo")
25 | return h("div", {}, [foo, btn])
26 | },
27 | }
28 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/componentSlots.ts:
--------------------------------------------------------------------------------
1 | import { ShapeFlags } from "@mini-vue3/shared"
2 |
3 | export function initSlots(instance, children) {
4 | const { vnode } = instance
5 | if (vnode.shapeFlag & ShapeFlags.SLOT_CHILDREN) {
6 | normalizeObjectSlots(children, instance.slots)
7 | }
8 | }
9 |
10 | function normalizeObjectSlots(children, slots) {
11 | for (const key in children) {
12 | const value = children[key]
13 | slots[key] = (props) => normalizeSlotValue(value(props))
14 | }
15 | }
16 |
17 | function normalizeSlotValue(value) {
18 | return Array.isArray(value) ? value : [value]
19 | }
20 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/scheduler.ts:
--------------------------------------------------------------------------------
1 | const queue: any[] = []
2 | let isFlushPending = false
3 |
4 | const p = Promise.resolve()
5 |
6 | export function nextTick(fn) {
7 | return fn ? p.then(fn) : p
8 | }
9 |
10 | export function queueJobs(job) {
11 | if (!queue.includes(job)) {
12 | queue.push(job)
13 | }
14 |
15 | queueFlush()
16 | }
17 |
18 | function queueFlush() {
19 | if (isFlushPending) return
20 | isFlushPending = true
21 | nextTick(flushJobs)
22 | }
23 |
24 | function flushJobs() {
25 | isFlushPending = false
26 | let job
27 | while ((job = queue.shift())) {
28 | job && job()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/reactivity/__test__/shallowReadonly.test.ts:
--------------------------------------------------------------------------------
1 | import { isReadonly, shallowReadonly } from "../src/reactive"
2 |
3 | describe("shallowReadonly", () => {
4 | it("should not make non-reactive properties reactive", () => {
5 | const props = shallowReadonly({ n: { foo: 1 } })
6 |
7 | expect(isReadonly(props)).toBe(true)
8 | expect(isReadonly(props.n)).toBe(false)
9 | })
10 |
11 | it("should warn after calling set", () => {
12 | console.warn = vi.fn()
13 |
14 | const user = shallowReadonly({
15 | age: 10,
16 | })
17 |
18 | user.age = 11
19 |
20 | expect(console.warn).toBeCalled()
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/examples/helloword/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | helloword
8 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/packages/compiler-core/__test__/__snapshots__/codegen.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[`codegen > element 1`] = `
4 | "const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode } = Vue
5 | return function render(_ctx, _cache) {return _createElementVNode(\\"div\\", null, \\"hi,\\" + _toDisplayString(_ctx.message))}"
6 | `;
7 |
8 | exports[`codegen > interpolation 1`] = `
9 | "const { toDisplayString: _toDisplayString } = Vue
10 | return function render(_ctx, _cache) {return _toDisplayString(_ctx.message)}"
11 | `;
12 |
13 | exports[`codegen > string 1`] = `"return function render(_ctx, _cache) {return \\"hi\\"}"`;
14 |
--------------------------------------------------------------------------------
/packages/compiler-core/__test__/transform.test.ts:
--------------------------------------------------------------------------------
1 | import { NodeTypes } from "../src/ast"
2 | import { baseParse } from "../src/parse"
3 | import { transform } from "../src/transform"
4 |
5 | describe("transform", () => {
6 | it("happy path", () => {
7 | const ast = baseParse("hi,{{message}}
")
8 |
9 | const plugin = (node) => {
10 | if (node.type === NodeTypes.TEXT)
11 | node.content = node.content + "mini-vue3"
12 | }
13 | transform(ast, {
14 | nodeTransforms: [plugin],
15 | })
16 |
17 | const nodeText = ast.children[0].children[0]
18 | expect(nodeText.content).toBe("hi,mini-vue3")
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/componentPublicInstance.ts:
--------------------------------------------------------------------------------
1 | import { hasOwn } from "@mini-vue3/shared"
2 |
3 | const publicPropertiesMap = {
4 | $el: (i) => i.vnode.el,
5 | $slots: (i) => i.slots,
6 | $props: (i) => i.props,
7 | }
8 |
9 | export const PublicInstanceProxyHandlers = {
10 | get({ _: instance }, key) {
11 | // setupState
12 | const { setupState, props } = instance
13 |
14 | if (hasOwn(setupState, key)) return setupState[key]
15 | else if (hasOwn(props, key)) return props[key]
16 |
17 | const publicGetter = publicPropertiesMap[key]
18 | if (publicGetter) {
19 | return publicGetter(instance)
20 | }
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/transforms/transformElement.ts:
--------------------------------------------------------------------------------
1 | import { createVNodeCall, NodeTypes } from "../ast"
2 | import { CREATE_ELEMENT_VNODE } from "../runtimeHelpers"
3 |
4 | export function transformElement(node, context) {
5 | if (node.type === NodeTypes.ELEMENT) {
6 | return () => {
7 | // 中间处理
8 | // tag
9 | const vnodeTag = `"${node.tag}"`
10 | // props
11 | let vnodeProps
12 | // children
13 | const children = node.children
14 | let vnodeChildren = children[0]
15 |
16 | node.codegenNode = createVNodeCall(
17 | context,
18 | vnodeTag,
19 | vnodeProps,
20 | vnodeChildren,
21 | )
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/patchChildren/App.js:
--------------------------------------------------------------------------------
1 | import { h } from "../../packages/vue3/dist/index.mjs"
2 |
3 | import ArrayToText from "./ArrayToText.js"
4 | import TextToText from "./TextToText.js"
5 | import TextToArray from "./TextToArray.js"
6 | import ArrayToArray from "./ArrayToArray.js"
7 |
8 | export const App = {
9 | name: "App",
10 | setup() {},
11 | render() {
12 | return h("div", { tId: 1 }, [
13 | h("p", {}, "主页"),
14 |
15 | // old: array, new: text
16 | // h(ArrayToText),
17 |
18 | // old: text, new: text
19 | // h(TextToText),
20 |
21 | // old: text, new: array
22 | // h(TextToArray),
23 |
24 | // old: array, new: array
25 | h(ArrayToArray),
26 | ])
27 | },
28 | }
29 |
--------------------------------------------------------------------------------
/examples/customRender/main.js:
--------------------------------------------------------------------------------
1 | import { createRenderer } from "../../packages/vue3/dist/index.mjs"
2 | import { App } from "./App.js"
3 |
4 | const game = new PIXI.Application({
5 | width: 500,
6 | height: 500,
7 | })
8 |
9 | document.body.append(game.view)
10 |
11 | const render = createRenderer({
12 | createElement(type) {
13 | if (type === "rect") {
14 | const rect = new PIXI.Graphics()
15 | rect.beginFill(0xff0000)
16 | rect.drawRect(0, 0, 100, 100)
17 | rect.endFill()
18 |
19 | return rect
20 | }
21 | },
22 | patchProp(el, key, val) {
23 | el[key] = val
24 | },
25 | insert(el, parent) {
26 | parent.addChild(el)
27 | },
28 | })
29 |
30 | render.createApp(App).mount(game.stage)
31 |
--------------------------------------------------------------------------------
/packages/shared/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mini-vue3/shared",
3 | "version": "0.0.0",
4 | "description": "shared",
5 | "main": "index.js",
6 | "module": "./dist/index.mjs",
7 | "types": "./dist/index.d.ts",
8 | "scripts": {
9 | "build": "tsup --dts"
10 | },
11 | "files": [
12 | "index.js",
13 | "dist"
14 | ],
15 | "keywords": [
16 | "shared"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/LoTwT/mini-vue3.git",
21 | "directory": "packages/shared"
22 | },
23 | "author": "LoTwT",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/LoTwT/mini-vue3/issues"
27 | },
28 | "homepage": "https://github.com/LoTwT/mini-vue3#readme",
29 | "devDependencies": {
30 | "tsup": "^5.12.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/reactivity/src/computed.ts:
--------------------------------------------------------------------------------
1 | import { ReactiveEffect } from "./effect"
2 |
3 | class ComputedRefImpl {
4 | // 判断是否需要重新计算
5 | private _dirty = true
6 | // computed 的计算值
7 | private _value
8 | // 借用 ReactiveEffect 收集 getter
9 | private _effect: ReactiveEffect
10 |
11 | constructor(_getter) {
12 | // 利用 scheduler 比 run 优先级高的特性,当要计算的值改变后
13 | // 关闭缓存开关,重新计算一次
14 | this._effect = new ReactiveEffect(_getter, () => {
15 | if (!this._dirty) this._dirty = true
16 | })
17 | }
18 |
19 | get value() {
20 | // (没有缓存 / 不使用缓存) 计算一次
21 | if (this._dirty) {
22 | this._dirty = false
23 | this._value = this._effect.run()
24 | }
25 |
26 | return this._value
27 | }
28 | }
29 |
30 | export function computed(getter) {
31 | return new ComputedRefImpl(getter)
32 | }
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mini-vue3",
3 | "version": "0.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "pnpm -r build",
8 | "test": "vitest",
9 | "prettier": "prettier --write ."
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/LoTwT/mini-vue3.git"
14 | },
15 | "keywords": [
16 | "vue",
17 | "vue3",
18 | "mini-vue",
19 | "mini-vue3"
20 | ],
21 | "author": "LoTwT",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/LoTwT/mini-vue3/issues"
25 | },
26 | "homepage": "https://github.com/LoTwT/mini-vue3#readme",
27 | "devDependencies": {
28 | "pnpm": "^6.32.4",
29 | "prettier": "^2.6.1",
30 | "typescript": "^4.6.3",
31 | "unplugin-auto-import": "^0.6.9",
32 | "vitest": "^0.8.1"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/compiler-core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mini-vue3/compiler-core",
3 | "version": "0.0.0",
4 | "description": "compiler-core",
5 | "main": "index.js",
6 | "module": "./dist/index.mjs",
7 | "types": "./dist/index.d.ts",
8 | "scripts": {
9 | "build": "tsup --dts"
10 | },
11 | "files": [
12 | "index.js",
13 | "dist"
14 | ],
15 | "keywords": [
16 | "compiler-core"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/LoTwT/mini-vue3.git",
21 | "directory": "packages/compiler-core"
22 | },
23 | "author": "LoTwT",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/LoTwT/mini-vue3/issues"
27 | },
28 | "homepage": "https://github.com/LoTwT/mini-vue3#readme",
29 | "devDependencies": {
30 | "@mini-vue3/shared": "workspace:*",
31 | "tsup": "^5.12.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/reactivity/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mini-vue3/reactivity",
3 | "version": "0.0.0",
4 | "description": "reactivity",
5 | "main": "index.js",
6 | "module": "./dist/index.mjs",
7 | "types": "./dist/index.d.ts",
8 | "scripts": {
9 | "build": "tsup --dts"
10 | },
11 | "files": [
12 | "index.js",
13 | "dist"
14 | ],
15 | "keywords": [
16 | "reactivity"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/LoTwT/mini-vue3.git",
21 | "directory": "packages/reactivity"
22 | },
23 | "author": "LoTwT",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/LoTwT/mini-vue3/issues"
27 | },
28 | "homepage": "https://github.com/LoTwT/mini-vue3#readme",
29 | "dependencies": {
30 | "@mini-vue3/shared": "workspace:^0.0.0"
31 | },
32 | "devDependencies": {
33 | "tsup": "^5.12.1"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/componentUpdate/App.js:
--------------------------------------------------------------------------------
1 | import { h, ref } from "../../packages/vue3/dist/index.mjs"
2 | import { Child } from "./Child.js"
3 |
4 | export const App = {
5 | name: "App",
6 | setup() {
7 | const msg = ref("123")
8 | const count = ref(1)
9 |
10 | window.msg = msg
11 |
12 | const changeChildProps = () => {
13 | msg.value = "456"
14 | }
15 |
16 | const changeCount = () => {
17 | count.value++
18 | }
19 |
20 | return {
21 | msg,
22 | changeChildProps,
23 | count,
24 | changeCount,
25 | }
26 | },
27 | render() {
28 | return h("div", {}, [
29 | h("div", {}, "componentUpdate"),
30 | h("button", { onClick: this.changeChildProps }, "change child props"),
31 | h(Child, { msg: this.msg }),
32 | h("button", { onClick: this.changeCount }, "change count"),
33 | h("div", {}, "count: " + this.count),
34 | ])
35 | },
36 | }
37 |
--------------------------------------------------------------------------------
/examples/nextTick/App.js:
--------------------------------------------------------------------------------
1 | import {
2 | h,
3 | ref,
4 | getCurrentInstance,
5 | nextTick,
6 | } from "../../packages/vue3/dist/index.mjs"
7 |
8 | export const App = {
9 | name: "App",
10 | setup() {
11 | const instance = getCurrentInstance()
12 | const count = ref(1)
13 |
14 | const onClick = () => {
15 | for (let i = 0; i < 100; i++) {
16 | console.log("update")
17 | count.value = i
18 | }
19 |
20 | console.log("outer instance => ", instance.vnode.el.innerHTML)
21 | nextTick(() => {
22 | console.log("nextTick instance => ", instance.vnode.el.innerHTML)
23 | })
24 | }
25 |
26 | return {
27 | count,
28 | onClick,
29 | }
30 | },
31 | render() {
32 | const button = h("button", { onClick: this.onClick }, "update")
33 | const p = h("p", {}, "count: " + this.count)
34 |
35 | return h("div", {}, [button, p])
36 | },
37 | }
38 |
--------------------------------------------------------------------------------
/packages/reactivity/__test__/reactive.test.ts:
--------------------------------------------------------------------------------
1 | import { isProxy, isReactive, reactive } from "../src/reactive"
2 |
3 | describe("reactive", () => {
4 | it("happy path", () => {
5 | const origin = { foo: 1 }
6 | const observed = reactive(origin)
7 |
8 | // origin 被代理
9 | expect(observed).not.toBe(origin)
10 | // 代理的 observed 能正常访问 foo
11 | expect(observed.foo).toBe(1)
12 |
13 | expect(isReactive(observed)).toBe(true)
14 | expect(isReactive(origin)).toBe(false)
15 |
16 | expect(isProxy(observed)).toBe(true)
17 | })
18 |
19 | it("nested reactive", () => {
20 | const origin = {
21 | nested: {
22 | foo: 1,
23 | },
24 | array: [{ bar: 2 }],
25 | }
26 |
27 | const observed = reactive(origin)
28 | expect(isReactive(observed.nested)).toBe(true)
29 | expect(isReactive(observed.array)).toBe(true)
30 | expect(isReactive(observed.array[0])).toBe(true)
31 | })
32 | })
33 |
--------------------------------------------------------------------------------
/packages/runtime-core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mini-vue3/runtime-core",
3 | "version": "0.0.0",
4 | "description": "runtime-core",
5 | "main": "index.js",
6 | "module": "./dist/index.mjs",
7 | "types": "./dist/index.d.ts",
8 | "scripts": {
9 | "build": "tsup --dts"
10 | },
11 | "files": [
12 | "index.js",
13 | "dist"
14 | ],
15 | "keywords": [
16 | "runtime-core"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/LoTwT/mini-vue3.git",
21 | "directory": "packages/runtime-core"
22 | },
23 | "author": "LoTwT",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/LoTwT/mini-vue3/issues"
27 | },
28 | "homepage": "https://github.com/LoTwT/mini-vue3#readme",
29 | "dependencies": {
30 | "@mini-vue3/shared": "workspace:*",
31 | "@mini-vue3/reactivity": "workspace:*"
32 | },
33 | "devDependencies": {
34 | "tsup": "^5.12.1"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/runtime-dom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mini-vue3/runtime-dom",
3 | "version": "0.0.0",
4 | "description": "runtime-dom",
5 | "main": "index.js",
6 | "module": "./dist/index.mjs",
7 | "types": "./dist/index.d.ts",
8 | "scripts": {
9 | "build": "tsup --dts"
10 | },
11 | "files": [
12 | "index.js",
13 | "dist"
14 | ],
15 | "keywords": [
16 | "runtime-dom"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/LoTwT/mini-vue3.git",
21 | "directory": "packages/runtime-dom"
22 | },
23 | "author": "LoTwT",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/LoTwT/mini-vue3/issues"
27 | },
28 | "homepage": "https://github.com/LoTwT/mini-vue3#readme",
29 | "dependencies": {
30 | "@mini-vue3/runtime-core": "workspace:*",
31 | "@mini-vue3/shared": "workspace:*"
32 | },
33 | "devDependencies": {
34 | "tsup": "^5.12.1"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/shared/src/index.ts:
--------------------------------------------------------------------------------
1 | export const extend = Object.assign
2 |
3 | export const isObject = (val) => val != null && typeof val === "object"
4 |
5 | export const hasChanged = (v1, v2) => !Object.is(v1, v2)
6 |
7 | export const hasOwn = (val, key) =>
8 | Object.prototype.hasOwnProperty.call(val, key)
9 |
10 | // camelCase: add => Add
11 | export const capitalize = (str: string) => {
12 | return str.charAt(0).toUpperCase() + str.slice(1)
13 | }
14 |
15 | export const toHandlerKey = (str: string) => {
16 | return str ? `on${capitalize(str)}` : ""
17 | }
18 |
19 | // kebabCase: add-foo => addFoo
20 | export const camelize = (str: string) => {
21 | return str.replace(/-(\w)/g, (_, c: string) => {
22 | return c ? c.toUpperCase() : ""
23 | })
24 | }
25 |
26 | export { ShapeFlags } from "./shapeFlags"
27 |
28 | export const EMPTY_OBJ = {}
29 |
30 | export const isString = (value) => typeof value === "string"
31 |
32 | export { toDisplayString } from "./toDisplayString"
33 |
--------------------------------------------------------------------------------
/packages/vue3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mini-vue3",
3 | "version": "0.0.0",
4 | "description": "mini-vue3",
5 | "main": "index.js",
6 | "module": "./dist/index.mjs",
7 | "types": "./dist/index.d.ts",
8 | "scripts": {
9 | "build": "tsup --dts"
10 | },
11 | "files": [
12 | "index.js",
13 | "dist"
14 | ],
15 | "keywords": [
16 | "mini-vue3"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/LoTwT/mini-vue3.git",
21 | "directory": "packages/mini-vue3"
22 | },
23 | "author": "LoTwT",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/LoTwT/mini-vue3/issues"
27 | },
28 | "homepage": "https://github.com/LoTwT/mini-vue3#readme",
29 | "dependencies": {
30 | "@mini-vue3/runtime-dom": "workspace:*",
31 | "@mini-vue3/compiler-core": "workspace:*",
32 | "@mini-vue3/reactivity": "workspace:*"
33 | },
34 | "devDependencies": {
35 | "tsup": "^5.12.1"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/reactivity/__test__/readonly.test.ts:
--------------------------------------------------------------------------------
1 | import { isProxy, isReadonly, readonly } from "../src/reactive"
2 |
3 | describe("readonly", () => {
4 | it("happy path", () => {
5 | const origin = { foo: 1, bar: { baz: 2 } }
6 | const wrapped = readonly(origin)
7 |
8 | expect(wrapped).not.toBe(origin)
9 | expect(wrapped.foo).toBe(1)
10 |
11 | expect(isReadonly(wrapped)).toBe(true)
12 | expect(isReadonly(origin)).toBe(false)
13 |
14 | expect(isProxy(wrapped)).toBe(true)
15 | })
16 |
17 | it("nested readonly", () => {
18 | const origin = { foo: 1, bar: { baz: 2 } }
19 | const wrapped = readonly(origin)
20 |
21 | expect(isReadonly(wrapped.bar)).toBe(true)
22 | expect(isReadonly(origin.bar)).toBe(false)
23 | })
24 |
25 | it("should warn after calling set", () => {
26 | console.warn = vi.fn()
27 |
28 | const user = readonly({
29 | age: 10,
30 | })
31 |
32 | user.age = 11
33 |
34 | expect(console.warn).toBeCalled()
35 | })
36 | })
37 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/apiInject.ts:
--------------------------------------------------------------------------------
1 | import { getCurrentInstance } from "./component"
2 |
3 | export function provide(key, value) {
4 | // 存
5 | const currentInstance = getCurrentInstance() as any
6 |
7 | if (currentInstance) {
8 | let { provides } = currentInstance
9 | const parentProvides = currentInstance.parent.provides
10 |
11 | // 初始化时调用一次
12 | if (provides === parentProvides) {
13 | // 原型链
14 | provides = currentInstance.provides = Object.create(parentProvides)
15 | }
16 |
17 | provides[key] = value
18 | }
19 | }
20 |
21 | export function inject(key, defaultValue) {
22 | // 取
23 | const currentInstance = getCurrentInstance() as any
24 |
25 | if (currentInstance) {
26 | const parentProvides = currentInstance.parent.provides
27 |
28 | if (key in parentProvides) return parentProvides[key]
29 | else if (defaultValue) {
30 | if (typeof defaultValue === "function") return defaultValue()
31 | return defaultValue
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/examples/helloword/App.js:
--------------------------------------------------------------------------------
1 | import { h } from "../../packages/vue3/dist/index.mjs"
2 | import { Foo } from "./Foo.js"
3 |
4 | window.self = null
5 | export const App = {
6 | name: "App",
7 | setup() {
8 | // composition api
9 | return {
10 | msg: "Here is setup msg!",
11 | }
12 | },
13 | render() {
14 | window.self = this
15 |
16 | // 渲染函数 ( )
17 | return h(
18 | "div",
19 | {
20 | id: "root",
21 | class: ["red", "bold"],
22 | onClick() {
23 | console.log("click")
24 | },
25 | onMousedown() {
26 | console.log("mousedown")
27 | },
28 | },
29 | // string
30 | // "hi, mini-vue3!",
31 |
32 | // setup varibles
33 | // "hi, mini-vue3!" + this.msg,
34 |
35 | // array
36 | // [
37 | // h("p", { class: "red" }, "hi"),
38 | // h("p", { class: "lightblue" }, "mini-vue3!"),
39 | // ],
40 |
41 | // component
42 | [h("div", {}, "hi, " + this.msg), h(Foo, { count: 1 })],
43 | )
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/vnode.ts:
--------------------------------------------------------------------------------
1 | import { ShapeFlags } from "@mini-vue3/shared"
2 |
3 | export const Fragment = Symbol("Fragment")
4 | export const Text = Symbol("Text")
5 |
6 | export function createVNode(type, props?, children?) {
7 | const vnode = {
8 | type,
9 | props,
10 | children,
11 | component: null,
12 | el: null,
13 | key: props && props.key,
14 | shapeFlag: getShapeFlags(type),
15 | }
16 |
17 | if (typeof children === "string") {
18 | vnode.shapeFlag |= ShapeFlags.TEXT_CHILDREN
19 | } else if (Array.isArray(children)) {
20 | vnode.shapeFlag |= ShapeFlags.ARRAY_CHILDREN
21 | }
22 |
23 | // 插槽
24 | // 组件 + children object
25 | if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
26 | if (typeof children === "object") {
27 | vnode.shapeFlag |= ShapeFlags.SLOT_CHILDREN
28 | }
29 | }
30 |
31 | return vnode
32 | }
33 |
34 | function getShapeFlags(type) {
35 | return typeof type === "string"
36 | ? ShapeFlags.ELEMENT
37 | : ShapeFlags.STATEFUL_COMPONENT
38 | }
39 |
40 | export function createTextVNode(text: string) {
41 | return createVNode(Text, {}, text)
42 | }
43 |
--------------------------------------------------------------------------------
/packages/compiler-core/__test__/codegen.test.ts:
--------------------------------------------------------------------------------
1 | import { generate } from "../src/codegen"
2 | import { baseParse } from "../src/parse"
3 | import { transform } from "../src/transform"
4 | import { transformElement } from "../src/transforms/transformElement"
5 | import { transformExpression } from "../src/transforms/transformExpression"
6 | import { transformText } from "../src/transforms/transformText"
7 |
8 | describe("codegen", () => {
9 | it("string", () => {
10 | const ast = baseParse("hi")
11 | transform(ast)
12 | const { code } = generate(ast)
13 | expect(code).toMatchSnapshot()
14 | })
15 |
16 | it("interpolation", () => {
17 | const ast = baseParse("{{message}}")
18 | transform(ast, {
19 | nodeTransforms: [transformExpression],
20 | })
21 | const { code } = generate(ast)
22 | expect(code).toMatchSnapshot()
23 | })
24 |
25 | it("element", () => {
26 | const ast = baseParse("hi,{{message}}
")
27 | transform(ast, {
28 | nodeTransforms: [transformExpression, transformElement, transformText],
29 | })
30 | const { code } = generate(ast)
31 | expect(code).toMatchSnapshot()
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/transforms/transformText.ts:
--------------------------------------------------------------------------------
1 | import { NodeTypes } from "../ast"
2 | import { isText } from "../utils"
3 |
4 | export function transformText(node) {
5 | if (node.type === NodeTypes.ELEMENT) {
6 | return () => {
7 | const { children } = node
8 | let currentContainer
9 |
10 | for (let i = 0; i < children.length; i++) {
11 | const child = children[i]
12 |
13 | if (isText(child)) {
14 | for (let j = i + 1; j < children.length; j++) {
15 | const next = children[j]
16 |
17 | if (isText(next)) {
18 | if (!currentContainer) {
19 | currentContainer = children[i] = {
20 | type: NodeTypes.COMPOUND_EXPRESSION,
21 | children: [child],
22 | }
23 | }
24 |
25 | currentContainer.children.push(" + ")
26 | currentContainer.children.push(next)
27 | children.splice(j, 1)
28 | j--
29 | } else {
30 | currentContainer = undefined
31 | break
32 | }
33 | }
34 | }
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/reactivity/__test__/computed.test.ts:
--------------------------------------------------------------------------------
1 | import { computed } from "../src/computed"
2 | import { reactive } from "../src/reactive"
3 |
4 | describe("computed", () => {
5 | it("happy path", () => {
6 | const user = reactive({
7 | age: 1,
8 | })
9 |
10 | const age = computed(() => user.age)
11 |
12 | expect(age.value).toBe(1)
13 | })
14 |
15 | it("should compute lazily", () => {
16 | const value = reactive({
17 | foo: 1,
18 | })
19 | const getter = vi.fn(() => value.foo)
20 | const cValue = computed(getter)
21 |
22 | // lazy
23 | expect(getter).not.toHaveBeenCalled()
24 |
25 | expect(cValue.value).toBe(1)
26 | expect(getter).toHaveBeenCalledTimes(1)
27 |
28 | // should not compute again
29 | cValue.value
30 | expect(getter).toHaveBeenCalledTimes(1)
31 |
32 | // should not compute until needed
33 | value.foo = 2
34 | expect(getter).toHaveBeenCalledTimes(1)
35 |
36 | // should compute
37 | expect(cValue.value).toBe(2)
38 | expect(getter).toHaveBeenCalledTimes(2)
39 |
40 | // should not compute again
41 | cValue.value
42 | expect(getter).toHaveBeenCalledTimes(2)
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/packages/runtime-dom/src/index.ts:
--------------------------------------------------------------------------------
1 | import { createRenderer } from "@mini-vue3/runtime-core"
2 |
3 | function createElement(type) {
4 | return document.createElement(type)
5 | }
6 |
7 | function patchProp(el, key, prevVal, nextVal) {
8 | // 判断 key 是不是以 on 开头
9 | const isOn = (key: string) => /^on[A-Z]/.test(key)
10 |
11 | if (isOn(key)) {
12 | // 事件
13 | const event = key.slice(2).toLowerCase()
14 | el.addEventListener(event, nextVal)
15 | } else {
16 | // 属性
17 | if (nextVal === undefined || nextVal === null) el.removeAttribute(key)
18 | else el.setAttribute(key, nextVal)
19 | }
20 | }
21 |
22 | function insert(child, parent, anchor) {
23 | // parent.append(child)
24 | parent.insertBefore(child, anchor || null)
25 | }
26 |
27 | function remove(child) {
28 | const parent = child.parentNode
29 |
30 | if (parent) {
31 | parent.removeChild(child)
32 | }
33 | }
34 |
35 | function setElementText(el, text) {
36 | el.textContent = text
37 | }
38 |
39 | const renderer = createRenderer({
40 | createElement,
41 | patchProp,
42 | insert,
43 | remove,
44 | setElementText,
45 | }) as any
46 |
47 | export function createApp(...args) {
48 | return renderer.createApp(...args)
49 | }
50 |
51 | export * from "@mini-vue3/runtime-core"
52 |
--------------------------------------------------------------------------------
/packages/reactivity/src/reactive.ts:
--------------------------------------------------------------------------------
1 | import { isObject } from "@mini-vue3/shared"
2 | import {
3 | mutableHandlers,
4 | readonlyHandlers,
5 | shallowReadonlyHandlers,
6 | } from "./baseHandlers"
7 |
8 | export const enum ReactiveFlags {
9 | IS_REACTIVE = "__v_isReactive",
10 | IS_READONLY = "__v_isReadonly",
11 | }
12 |
13 | export function reactive(raw) {
14 | return createReactiveObject(raw, mutableHandlers)
15 | }
16 |
17 | export function readonly(raw) {
18 | return createReactiveObject(raw, readonlyHandlers)
19 | }
20 |
21 | export function shallowReadonly(raw) {
22 | return createReactiveObject(raw, shallowReadonlyHandlers)
23 | }
24 |
25 | export function isReactive(value) {
26 | return !!value[ReactiveFlags.IS_REACTIVE]
27 | }
28 |
29 | export function isReadonly(value) {
30 | return !!value[ReactiveFlags.IS_READONLY]
31 | }
32 |
33 | export function isProxy(value) {
34 | return isReactive(value) || isReadonly(value)
35 | }
36 |
37 | /**
38 | * 创建 Proxy 的包装方法
39 | * 更便于阅读
40 | * @param raw
41 | * @param baseHandlers
42 | * @returns
43 | */
44 | function createReactiveObject(raw, baseHandlers) {
45 | if (!isObject(raw)) {
46 | console.warn(`target ${raw} is not an object!`)
47 | return raw
48 | }
49 |
50 | return new Proxy(raw, baseHandlers)
51 | }
52 |
--------------------------------------------------------------------------------
/examples/apiInject/App.js:
--------------------------------------------------------------------------------
1 | import { h, provide, inject } from "../../packages/vue3/dist/index.mjs"
2 |
3 | const Provider = {
4 | name: "Provider",
5 | setup() {
6 | provide("foo", "fooVal")
7 | provide("bar", "barVal")
8 | },
9 | render() {
10 | return h("div", {}, [h("p", {}, "Provider"), h(ProviderTwo)])
11 | },
12 | }
13 |
14 | const ProviderTwo = {
15 | name: "ProviderTwo",
16 | setup() {
17 | provide("foo", "fooTwo")
18 | const foo = inject("foo")
19 |
20 | return { foo }
21 | },
22 | render() {
23 | return h("div", {}, [
24 | h("p", {}, `ProviderTwo foo: ${this.foo}`),
25 | h(Consumer),
26 | ])
27 | },
28 | }
29 |
30 | const Consumer = {
31 | name: "Consumer",
32 | setup() {
33 | const foo = inject("foo")
34 | const bar = inject("bar")
35 | const baz = inject("baz", () => "bazDefault")
36 |
37 | return {
38 | foo,
39 | bar,
40 | baz,
41 | }
42 | },
43 | render() {
44 | return h(
45 | "div",
46 | {},
47 | `Consumer: - foo: ${this.foo} - bar: ${this.bar} - baz: ${this.baz}`,
48 | )
49 | },
50 | }
51 |
52 | export const App = {
53 | name: "App",
54 | setup() {},
55 | render() {
56 | return h("div", {}, [h("p", {}, "apiInject"), h(Provider)])
57 | },
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mini-vue3
2 |
3 | pnpm + monorepo + tsup + vitest 实现一个简单的 [vue3](https://github.com/vuejs/core)
4 |
5 | ## scripts
6 |
7 | ```bash
8 | # 使用 pnpm -r,借助 pnpm 自动分析依赖关系,递归打包 packages
9 | pnpm build
10 |
11 | # 运行测试
12 | pnpm test
13 | ```
14 |
15 | ## reactivity
16 |
17 | `@mini-vue3/reactivity`
18 |
19 | - [x] reactive
20 | - [x] ref
21 | - [x] readonly
22 | - [x] computed
23 | - [x] track 依赖收集
24 | - [x] trigger 依赖触发
25 | - [x] isReactive
26 | - [x] 嵌套 reactive
27 | - [x] toRaw
28 | - [x] effect.scheduler
29 | - [x] effect.stop
30 | - [x] isReadonly
31 | - [x] isProxy
32 | - [x] shallowReadonly
33 | - [x] proxyRefs
34 |
35 | ## runtime-core
36 |
37 | `@mini-vue3/runtime-core`
38 |
39 | - [x] 支持组件类型
40 | - [x] 支持 element 类型
41 | - [x] 初始化 props
42 | - [x] setup 可获取 props 和 context
43 | - [x] 支持 component emit
44 | - [x] 支持 proxy
45 | - [x] 可以在 render 函数中获取 setup 返回的对象
46 | - [x] nextTick 的实现
47 | - [x] 支持 getCurrentInstance
48 | - [x] 支持 provide/inject
49 | - [x] 支持最基础的 slots
50 | - [x] 支持 Text 类型节点
51 | - [x] 支持 $el api
52 |
53 | ## shared
54 |
55 | `@mini-vue3/shared`
56 |
57 | - [x] extend
58 | - [x] isObject
59 | - [x] hasChanged
60 |
61 | ## compiler-core
62 |
63 | `@mini-vue3/compiler-core`
64 |
65 | - [x] 解析插值
66 | - [x] 解析 element
67 | - [x] 解析 text
68 |
69 | ## runtime-dom
70 |
71 | `@mini-vue3/runtime-dom`
72 |
73 | - [x] 支持 custom renderer
74 |
--------------------------------------------------------------------------------
/examples/update/App.js:
--------------------------------------------------------------------------------
1 | import { h, ref } from "../../packages/vue3/dist/index.mjs"
2 |
3 | export const App = {
4 | name: "App",
5 | setup() {
6 | const count = ref(0)
7 | const onClick = () => {
8 | count.value++
9 | }
10 |
11 | const props = ref({
12 | foo: "foo",
13 | bar: "bar",
14 | })
15 |
16 | const onChangePropsDemo1 = () => {
17 | props.value.foo = "new-foo"
18 | }
19 |
20 | const onChangePropsDemo2 = () => {
21 | props.value.foo = undefined
22 | }
23 |
24 | const onChangePropsDemo3 = () => {
25 | props.value = {
26 | foo: "foo",
27 | }
28 | }
29 |
30 | return {
31 | count,
32 | onClick,
33 | props,
34 | onChangePropsDemo1,
35 | onChangePropsDemo2,
36 | onChangePropsDemo3,
37 | }
38 | },
39 | render() {
40 | return h("div", { id: "root", ...this.props }, [
41 | h("div", {}, "count: " + this.count),
42 | h("button", { onClick: this.onClick }, "inc"),
43 | h(
44 | "button",
45 | { onClick: this.onChangePropsDemo1 },
46 | "changeProps - 值改变了 - 修改",
47 | ),
48 | h(
49 | "button",
50 | { onClick: this.onChangePropsDemo2 },
51 | "changeProps - 值变成了 undefined - 删除",
52 | ),
53 | h(
54 | "button",
55 | { onClick: this.onChangePropsDemo3 },
56 | "changeProps - key 在新的里面没有了 - 删除",
57 | ),
58 | ])
59 | },
60 | }
61 |
--------------------------------------------------------------------------------
/packages/reactivity/src/baseHandlers.ts:
--------------------------------------------------------------------------------
1 | import { track, trigger } from "./effect"
2 | import { reactive, ReactiveFlags, readonly } from "./reactive"
3 | import { extend, isObject } from "@mini-vue3/shared"
4 |
5 | const get = createGetter()
6 | const set = createSetter()
7 | const readonlyGet = createGetter(true)
8 | const shallowReadonlyGet = createGetter(true, true)
9 |
10 | export const mutableHandlers = {
11 | get,
12 | set,
13 | }
14 |
15 | export const readonlyHandlers = {
16 | get: readonlyGet,
17 | set(target, key, value) {
18 | console.warn(`can't set ${String(key)} to a readonly object.`)
19 | return true
20 | },
21 | }
22 |
23 | export const shallowReadonlyHandlers = extend({}, readonlyHandlers, {
24 | get: shallowReadonlyGet,
25 | })
26 |
27 | function createGetter(isReadonly = false, shallow = false) {
28 | return function get(target, key) {
29 | if (key === ReactiveFlags.IS_REACTIVE) return !isReadonly
30 | else if (key === ReactiveFlags.IS_READONLY) return isReadonly
31 |
32 | const res = Reflect.get(target, key)
33 |
34 | if (shallow) return res
35 |
36 | // 嵌套 reactive
37 | if (isObject(res)) return isReadonly ? readonly(res) : reactive(res)
38 |
39 | if (!isReadonly) {
40 | // 依赖收集
41 | track(target, key)
42 | }
43 |
44 | return res
45 | }
46 | }
47 |
48 | function createSetter() {
49 | return function set(target, key, value) {
50 | const res = Reflect.set(target, key, value)
51 |
52 | // 依赖触发
53 | trigger(target, key)
54 |
55 | return res
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/transform.ts:
--------------------------------------------------------------------------------
1 | import { NodeTypes } from "./ast"
2 | import { TO_DISPLAY_STRING } from "./runtimeHelpers"
3 |
4 | export function transform(root, options = {}) {
5 | const context = createTransformContext(root, options)
6 |
7 | // 遍历 - 深度优先搜索
8 | traverseNode(root, context)
9 |
10 | createRootCodegen(root)
11 |
12 | root.helpers = [...context.helpers.keys()]
13 | }
14 |
15 | function createRootCodegen(root) {
16 | const child = root.children[0]
17 |
18 | if (child.type === NodeTypes.ELEMENT) root.codegenNode = child.codegenNode
19 | else root.codegenNode = root.children[0]
20 | }
21 |
22 | function createTransformContext(root, options) {
23 | const context = {
24 | root,
25 | nodeTransforms: options.nodeTransforms || [],
26 | helpers: new Map(),
27 | helper(key) {
28 | context.helpers.set(key, 1)
29 | },
30 | }
31 |
32 | return context
33 | }
34 |
35 | function traverseNode(node, context) {
36 | const nodeTransforms = context.nodeTransforms
37 | const exitFns: any[] = []
38 | for (let i = 0; i < nodeTransforms.length; i++) {
39 | const transform = nodeTransforms[i]
40 | const onExit = transform(node, context)
41 | if (onExit) exitFns.push(onExit)
42 | }
43 |
44 | switch (node.type) {
45 | case NodeTypes.INTERPOLATION:
46 | context.helper(TO_DISPLAY_STRING)
47 | break
48 |
49 | case NodeTypes.ROOT:
50 | case NodeTypes.ELEMENT:
51 | traverseChildren(node, context)
52 | break
53 |
54 | default:
55 | break
56 | }
57 |
58 | let i = exitFns.length
59 | while (i--) {
60 | exitFns[i]()
61 | }
62 | }
63 |
64 | function traverseChildren(node, context) {
65 | const children = node.children
66 | for (let i = 0; i < children.length; i++) {
67 | const n = children[i]
68 | traverseNode(n, context)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/packages/reactivity/src/ref.ts:
--------------------------------------------------------------------------------
1 | import { hasChanged, isObject } from "@mini-vue3/shared"
2 | import {
3 | isTracking,
4 | ReactiveEffect,
5 | trackEffects,
6 | triggerEffects,
7 | } from "./effect"
8 | import { reactive } from "./reactive"
9 |
10 | class RefImpl {
11 | // ref 的值
12 | private _value
13 | // 原始值
14 | private _rawValue
15 | // ref 的依赖
16 | public dep: Set
17 | public __v_isRef = true
18 |
19 | constructor(value) {
20 | // 存储原始值
21 | this._rawValue = value
22 | // 如果 value 是对象,需要用 reactive 包裹
23 | this._value = convert(value)
24 |
25 | this.dep = new Set()
26 | }
27 |
28 | get value() {
29 | trackRefValue(this)
30 | return this._value
31 | }
32 |
33 | set value(newValue) {
34 | // 用原始值进行比较!
35 | if (!hasChanged(this._rawValue, newValue)) return
36 |
37 | // 一定先修改了 value 的值
38 | this._rawValue = newValue
39 | this._value = convert(newValue)
40 |
41 | triggerEffects(this.dep)
42 | }
43 | }
44 |
45 | /**
46 | * 值转换
47 | * @param value
48 | * @returns
49 | */
50 | function convert(value) {
51 | return isObject(value) ? reactive(value) : value
52 | }
53 |
54 | function trackRefValue(ref: RefImpl) {
55 | if (isTracking()) {
56 | trackEffects(ref.dep)
57 | }
58 | }
59 |
60 | export function ref(value) {
61 | return new RefImpl(value)
62 | }
63 |
64 | export function isRef(ref) {
65 | return !!ref.__v_isRef
66 | }
67 |
68 | export function unRef(ref) {
69 | return isRef(ref) ? ref.value : ref
70 | }
71 |
72 | export function proxyRefs(objectWithRefs) {
73 | return new Proxy(objectWithRefs, {
74 | get(target, key) {
75 | // 使用 unRef 解包
76 | return unRef(Reflect.get(target, key))
77 | },
78 | set(target, key, value) {
79 | if (isRef(target[key]) && !isRef(value)) {
80 | return (target[key].value = value)
81 | } else {
82 | return Reflect.set(target, key, value)
83 | }
84 | },
85 | })
86 | }
87 |
--------------------------------------------------------------------------------
/packages/reactivity/__test__/ref.test.ts:
--------------------------------------------------------------------------------
1 | import { effect } from "../src/effect"
2 | import { reactive } from "../src/reactive"
3 | import { isRef, proxyRefs, ref, unRef } from "../src/ref"
4 |
5 | describe("ref", () => {
6 | it("happy path", () => {
7 | const a = ref(1)
8 | expect(a.value).toBe(1)
9 | })
10 |
11 | it("should be reactive", () => {
12 | const a = ref(1)
13 | let dummy
14 | let calls = 0
15 |
16 | effect(() => {
17 | calls++
18 | dummy = a.value
19 | })
20 |
21 | expect(calls).toBe(1)
22 | expect(dummy).toBe(1)
23 | a.value = 2
24 |
25 | expect(calls).toBe(2)
26 | expect(dummy).toBe(2)
27 |
28 | // same value should not trigger
29 | a.value = 2
30 | expect(calls).toBe(2)
31 | expect(dummy).toBe(2)
32 | })
33 |
34 | it("should make nested properties reactive", () => {
35 | const a = ref({ count: 1 })
36 | let dummy
37 |
38 | effect(() => {
39 | dummy = a.value.count
40 | })
41 |
42 | expect(dummy).toBe(1)
43 | a.value.count = 2
44 | expect(dummy).toBe(2)
45 | })
46 |
47 | it("isRef", () => {
48 | const a = ref(1)
49 | const p = reactive({ age: 10 })
50 |
51 | expect(isRef(a)).toBe(true)
52 | expect(isRef(1)).toBe(false)
53 | expect(isRef(p)).toBe(false)
54 | })
55 |
56 | it("unRef", () => {
57 | const a = ref(1)
58 |
59 | expect(unRef(a)).toBe(1)
60 | expect(unRef(1)).toBe(1)
61 | })
62 |
63 | it("proxyRefs", () => {
64 | // 用于模板自动解包
65 | const user = {
66 | age: ref(10),
67 | name: "xiaohong",
68 | }
69 |
70 | const proxyUser = proxyRefs(user)
71 | expect(user.age.value).toBe(10)
72 | expect(proxyUser.age).toBe(10)
73 | expect(proxyUser.name).toBe("xiaohong")
74 |
75 | proxyUser.age = 20
76 | expect(proxyUser.age).toBe(20)
77 | expect(user.age.value).toBe(20)
78 |
79 | proxyUser.age = ref(30)
80 | expect(proxyUser.age).toBe(30)
81 | expect(user.age.value).toBe(30)
82 | })
83 | })
84 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/component.ts:
--------------------------------------------------------------------------------
1 | import { initProps } from "./componentProps"
2 | import { PublicInstanceProxyHandlers } from "./componentPublicInstance"
3 | import { proxyRefs, shallowReadonly } from "@mini-vue3/reactivity"
4 | import { emit } from "./componentEmit"
5 | import { initSlots } from "./componentSlots"
6 |
7 | export function createComponentInstance(vnode, parent) {
8 | const component = {
9 | vnode,
10 | type: vnode.type,
11 | next: null,
12 | setupState: {},
13 | props: {},
14 | slots: {},
15 | provides: parent ? parent.provides : {},
16 | parent,
17 | isMounted: false,
18 | subTree: {},
19 | emit: (...args) => {},
20 | }
21 |
22 | component.emit = emit.bind(null, component)
23 |
24 | return component
25 | }
26 |
27 | export function setupComponent(instance) {
28 | // 初始化 props
29 | initProps(instance, instance.vnode.props)
30 |
31 | // 初始化 slots
32 | initSlots(instance, instance.vnode.children)
33 |
34 | // 初始化有状态组件
35 | setupStatefulComponent(instance)
36 | }
37 |
38 | function setupStatefulComponent(instance) {
39 | // 获取组件实例 (用户写的组件对象)
40 | const Component = instance.type
41 |
42 | // ctx
43 | instance.proxy = new Proxy({ _: instance }, PublicInstanceProxyHandlers)
44 |
45 | const { setup } = Component
46 |
47 | if (setup) {
48 | setCurrentInstance(instance)
49 | const setupResult = setup(shallowReadonly(instance.props), {
50 | emit: instance.emit,
51 | })
52 | setCurrentInstance(null)
53 | handleSetupResult(instance, setupResult)
54 | }
55 | }
56 |
57 | function handleSetupResult(instance, setupResult: object | Function) {
58 | // object => 注入组件实例
59 | if (typeof setupResult === "object") {
60 | instance.setupState = proxyRefs(setupResult)
61 | }
62 |
63 | // Function => 渲染函数
64 |
65 | finishComponentSetup(instance)
66 | }
67 |
68 | function finishComponentSetup(instance) {
69 | const Component = instance.type
70 |
71 | if (compiler && !Component.render && Component.template) {
72 | Component.render = compiler(Component.template)
73 | }
74 |
75 | instance.render = Component.render
76 | }
77 |
78 | let currentInstance = null
79 | export function getCurrentInstance() {
80 | return currentInstance
81 | }
82 |
83 | export function setCurrentInstance(instance) {
84 | currentInstance = instance
85 | }
86 |
87 | let compiler
88 | export function registerRuntimeCompiler(_compiler) {
89 | compiler = _compiler
90 | }
91 |
--------------------------------------------------------------------------------
/packages/reactivity/__test__/effect.test.ts:
--------------------------------------------------------------------------------
1 | import { effect, stop } from "../src/effect"
2 | import { reactive } from "../src/reactive"
3 |
4 | describe("effect", () => {
5 | it("happy path", () => {
6 | const user = reactive({
7 | age: 10,
8 | })
9 |
10 | let nextAge = 0
11 |
12 | effect(() => {
13 | nextAge = user.age + 1
14 | })
15 |
16 | // 初始化
17 | expect(nextAge).toBe(11)
18 |
19 | // 更新
20 | user.age++
21 | expect(nextAge).toBe(12)
22 | })
23 |
24 | it("should return runner after calling effect", () => {
25 | // effect(fn) => runner() => fn() => fn return
26 | let foo = 10
27 | const runner = effect(() => {
28 | foo++
29 | return "foo"
30 | })
31 |
32 | expect(foo).toBe(11)
33 | const r = runner()
34 | expect(foo).toBe(12)
35 | expect(r).toBe("foo")
36 | })
37 |
38 | it("scheduler", () => {
39 | // 1. 通过 effect 的第二个参数,给定一个 scheduler 函数
40 | // 2. effect 第一次执行 ( 依赖收集 ),执行 fn
41 | // 3. 当触发响应式对象修改 ( set ) 时,不执行 fn 而是执行 scheduler
42 | // 4. 当执行 runner 时,正常执行 fn
43 | let dummy
44 | let run
45 |
46 | const scheduler = vi.fn(() => {
47 | run = runner
48 | })
49 |
50 | const obj = reactive({ foo: 1 })
51 | const runner = effect(
52 | () => {
53 | dummy = obj.foo
54 | },
55 | {
56 | scheduler,
57 | },
58 | )
59 |
60 | expect(scheduler).not.toHaveBeenCalled()
61 | expect(dummy).toBe(1)
62 | // should be called on first trigger
63 | obj.foo++
64 | expect(scheduler).toHaveBeenCalledTimes(1)
65 | // should not run yet
66 | expect(dummy).toBe(1)
67 | // manually run
68 | run()
69 | // should have run
70 | expect(dummy).toBe(2)
71 | })
72 |
73 | // 失活 effect
74 | it("should stop", () => {
75 | let dummy
76 |
77 | const obj = reactive({ prop: 1 })
78 | const runner = effect(() => {
79 | dummy = obj.prop
80 | })
81 | obj.prop = 2
82 |
83 | expect(dummy).toBe(2)
84 | stop(runner)
85 |
86 | obj.prop = 3
87 | expect(dummy).toBe(2)
88 |
89 | obj.prop++
90 | expect(dummy).toBe(2)
91 |
92 | // stopped effect should be actived after calling runner
93 | runner()
94 | expect(dummy).toBe(4)
95 | })
96 |
97 | // effect 失活后的回调函数
98 | it("onStop", () => {
99 | let dummy
100 |
101 | const obj = reactive({
102 | foo: 1,
103 | })
104 | const onStop = vi.fn()
105 |
106 | const runner = effect(
107 | () => {
108 | dummy = obj.foo
109 | },
110 | { onStop },
111 | )
112 |
113 | stop(runner)
114 | expect(onStop).toBeCalledTimes(1)
115 | })
116 | })
117 |
--------------------------------------------------------------------------------
/packages/compiler-core/__test__/parse.test.ts:
--------------------------------------------------------------------------------
1 | import { NodeTypes } from "../src/ast"
2 | import { baseParse } from "../src/parse"
3 |
4 | describe("parse", () => {
5 | describe("interpolation", () => {
6 | it("simple interpolation", () => {
7 | const ast = baseParse("{{ message }}")
8 |
9 | // root
10 | expect(ast.children[0]).toStrictEqual({
11 | type: NodeTypes.INTERPOLATION,
12 | content: {
13 | type: NodeTypes.SIMPLE_EXPRESSION,
14 | content: "message",
15 | },
16 | })
17 | })
18 | })
19 |
20 | describe("element", () => {
21 | it("simple element div", () => {
22 | const ast = baseParse("")
23 |
24 | expect(ast.children[0]).toStrictEqual({
25 | type: NodeTypes.ELEMENT,
26 | tag: "div",
27 | children: [],
28 | })
29 | })
30 | })
31 |
32 | describe("text", () => {
33 | it("simple text", () => {
34 | const ast = baseParse("some text")
35 |
36 | expect(ast.children[0]).toStrictEqual({
37 | type: NodeTypes.TEXT,
38 | content: "some text",
39 | })
40 | })
41 | })
42 |
43 | describe("comprehensive", () => {
44 | it("should parse three types", () => {
45 | const ast = baseParse("hi,{{message}}
")
46 |
47 | expect(ast.children[0]).toStrictEqual({
48 | type: NodeTypes.ELEMENT,
49 | tag: "div",
50 | children: [
51 | {
52 | type: NodeTypes.TEXT,
53 | content: "hi,",
54 | },
55 | {
56 | type: NodeTypes.INTERPOLATION,
57 | content: {
58 | type: NodeTypes.SIMPLE_EXPRESSION,
59 | content: "message",
60 | },
61 | },
62 | ],
63 | })
64 | })
65 |
66 | it("should parse nested types", () => {
67 | const ast = baseParse("")
68 |
69 | expect(ast.children[0]).toStrictEqual({
70 | type: NodeTypes.ELEMENT,
71 | tag: "div",
72 | children: [
73 | {
74 | type: NodeTypes.ELEMENT,
75 | tag: "p",
76 | children: [
77 | {
78 | type: NodeTypes.TEXT,
79 | content: "hi",
80 | },
81 | ],
82 | },
83 | {
84 | type: NodeTypes.INTERPOLATION,
85 | content: {
86 | type: NodeTypes.SIMPLE_EXPRESSION,
87 | content: "message",
88 | },
89 | },
90 | ],
91 | })
92 | })
93 |
94 | it("should throw an error when lack of end tag", () => {
95 | expect(() => {
96 | baseParse("
")
97 | }).toThrow(new Error("lack of end tag: span"))
98 | })
99 | })
100 | })
101 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/codegen.ts:
--------------------------------------------------------------------------------
1 | import { NodeTypes } from "./ast"
2 | import {
3 | CREATE_ELEMENT_VNODE,
4 | helperMapName,
5 | TO_DISPLAY_STRING,
6 | } from "./runtimeHelpers"
7 | import { isString } from "@mini-vue3/shared"
8 |
9 | export function generate(ast) {
10 | const context = createCodegenContext()
11 | const { push } = context
12 |
13 | genFunctionPreamble(ast, context)
14 |
15 | push("return ")
16 | const functionName = "render"
17 | const args = ["_ctx", "_cache"]
18 | const signature = args.join(", ")
19 |
20 | push(`function ${functionName}(${signature}) {`)
21 | push(`return `)
22 | genNode(ast.codegenNode, context)
23 | push(`}`)
24 |
25 | return {
26 | code: context.code,
27 | }
28 | }
29 | function genFunctionPreamble(ast, context) {
30 | if (ast.helpers.length === 0) return
31 |
32 | const VueBinging = "Vue"
33 | const aliasHelper = (s) => `${helperMapName[s]}: _${helperMapName[s]}`
34 | context.push(
35 | `const { ${ast.helpers.map(aliasHelper).join(", ")} } = ${VueBinging}\n`,
36 | )
37 | }
38 |
39 | function genNode(node, context) {
40 | switch (node.type) {
41 | case NodeTypes.TEXT:
42 | genText(node, context)
43 | break
44 |
45 | case NodeTypes.INTERPOLATION:
46 | genInterpolation(node, context)
47 | break
48 |
49 | case NodeTypes.SIMPLE_EXPRESSION:
50 | genSimpleExpression(node, context)
51 | break
52 |
53 | case NodeTypes.ELEMENT:
54 | genElement(node, context)
55 | break
56 |
57 | case NodeTypes.COMPOUND_EXPRESSION:
58 | genCompoundExpression(node, context)
59 | break
60 |
61 | default:
62 | break
63 | }
64 | }
65 |
66 | function genCompoundExpression(node, context) {
67 | const children = node.children
68 |
69 | for (let i = 0; i < children.length; i++) {
70 | const child = children[i]
71 | if (isString(child)) {
72 | context.push(child)
73 | } else {
74 | genNode(child, context)
75 | }
76 | }
77 | }
78 |
79 | function genElement(node, context) {
80 | const { tag, children, props } = node
81 | context.push(`${context.helper(CREATE_ELEMENT_VNODE)}(`)
82 | genNodeList(genNullable([tag, props, children]), context)
83 | context.push(")")
84 | }
85 |
86 | function genNodeList(nodes, context) {
87 | for (let i = 0; i < nodes.length; i++) {
88 | const node = nodes[i]
89 | if (isString(node)) context.push(node)
90 | else genNode(node, context)
91 |
92 | if (i < nodes.length - 1) context.push(", ")
93 | }
94 | }
95 |
96 | function genNullable(args) {
97 | return args.map((arg) => arg || "null")
98 | }
99 |
100 | function genSimpleExpression(node, context) {
101 | context.push(`${node.content}`)
102 | }
103 |
104 | function genInterpolation(node, context) {
105 | context.push(`${context.helper(TO_DISPLAY_STRING)}(`)
106 | genNode(node.content, context)
107 | context.push(")")
108 | }
109 |
110 | function genText(node, context) {
111 | context.push(`"${node.content}"`)
112 | }
113 |
114 | function createCodegenContext() {
115 | const context = {
116 | code: "",
117 | push(source) {
118 | context.code += source
119 | },
120 | helper(key) {
121 | return `_${helperMapName[key]}`
122 | },
123 | }
124 |
125 | return context
126 | }
127 |
--------------------------------------------------------------------------------
/packages/reactivity/src/effect.ts:
--------------------------------------------------------------------------------
1 | import { extend } from "@mini-vue3/shared"
2 |
3 | /**
4 | * 存放 依赖收集Map 的 Map
5 | * Map>>
6 | */
7 | const targetMap = new Map>>()
8 |
9 | /**
10 | * 存放当前 effect 的 ReactiveEffect 类实例
11 | */
12 | let activeEffect: ReactiveEffect
13 |
14 | /**
15 | * 是否应该进行依赖收集
16 | */
17 | let shouldTrack: boolean
18 |
19 | export class ReactiveEffect {
20 | /**
21 | * 反向收集的所有依赖的 Set 容器
22 | */
23 | public deps: Set[] = []
24 |
25 | /**
26 | * stop 的回调
27 | */
28 | public onStop?: () => void
29 |
30 | /**
31 | * 当前 effect 状态 ( 激活 / 失活 )
32 | */
33 | private active = true
34 |
35 | constructor(private _fn: () => void, public scheduler?: () => void) {}
36 |
37 | run() {
38 | if (!this.active) return this._fn()
39 |
40 | shouldTrack = true
41 | activeEffect = this
42 |
43 | const result = this._fn()
44 |
45 | shouldTrack = false
46 | return result
47 | }
48 |
49 | /**
50 | * 失活 effect
51 | */
52 | stop() {
53 | if (this.active) {
54 | cleanupEffect(this)
55 |
56 | // 调用 onStop 回调
57 | if (this.onStop) this.onStop()
58 |
59 | this.active = false
60 | }
61 | }
62 | }
63 |
64 | /**
65 | * 清除依赖
66 | * @param { ReactiveEffect } effect
67 | */
68 | function cleanupEffect(effect: ReactiveEffect) {
69 | effect.deps.forEach((dep) => dep.delete(effect))
70 | effect.deps.length = 0
71 | }
72 |
73 | interface EffectOptions {
74 | scheduler?: () => void
75 | onStop?: () => void
76 | }
77 |
78 | export interface ReactiveEffectRunner {
79 | (): T
80 | effect: ReactiveEffect
81 | }
82 |
83 | export function effect(fn: () => void, options?: EffectOptions) {
84 | const _effect = new ReactiveEffect(fn, options?.scheduler)
85 |
86 | // 将 options 注入到 _effect 上
87 | extend(_effect, options)
88 | _effect.run()
89 |
90 | const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
91 | runner.effect = _effect
92 |
93 | return runner
94 | }
95 |
96 | export function isTracking() {
97 | return shouldTrack && activeEffect != null
98 | }
99 |
100 | /**
101 | * 依赖收集
102 | */
103 | export function track(target, key) {
104 | // todo 一个优雅的注释
105 | // if (!activeEffect || !shouldTrack) return
106 | if (!isTracking()) return
107 |
108 | // target => key => dep
109 | let depsMap = targetMap.get(target)
110 | if (!depsMap)
111 | targetMap.set(target, (depsMap = new Map>()))
112 |
113 | let dep = depsMap.get(key)
114 | if (!dep) depsMap.set(key, (dep = new Set()))
115 |
116 | trackEffects(dep)
117 | }
118 |
119 | export function trackEffects(dep: Set) {
120 | // 如果已经在 dep 中,不再重复收集
121 | if (dep.has(activeEffect)) return
122 | dep.add(activeEffect)
123 |
124 | // 反向收集所有依赖的 Set 容器
125 | activeEffect.deps.push(dep)
126 | }
127 |
128 | /**
129 | * 依赖触发
130 | */
131 | export function trigger(target, key) {
132 | const depsMap = targetMap.get(target)
133 | const dep = depsMap?.get(key)
134 |
135 | if (dep) {
136 | triggerEffects(dep)
137 | }
138 | }
139 |
140 | export function triggerEffects(dep: Set) {
141 | dep.forEach((effect) => {
142 | if (effect.scheduler) effect.scheduler()
143 | else effect.run()
144 | })
145 | }
146 |
147 | /**
148 | * 使被收集的依赖失活
149 | * deactive effect
150 | */
151 | export function stop(runner: ReactiveEffectRunner) {
152 | runner.effect.stop()
153 | }
154 |
--------------------------------------------------------------------------------
/packages/compiler-core/src/parse.ts:
--------------------------------------------------------------------------------
1 | import { NodeTypes } from "./ast"
2 |
3 | const enum TagType {
4 | Start,
5 | End,
6 | }
7 |
8 | export function baseParse(content: string) {
9 | const context = createParserContext(content)
10 | return createRoot(parseChildren(context, []))
11 | }
12 |
13 | function parseInterpolation(context) {
14 | const openDelimiter = "{{"
15 | const closeDelimiter = "}}"
16 |
17 | const closeIndex = context.source.indexOf(
18 | closeDelimiter,
19 | openDelimiter.length,
20 | )
21 |
22 | advanceBy(context, openDelimiter.length)
23 |
24 | // 内容长度
25 | const rawContentLength = closeIndex - openDelimiter.length
26 | // 截取内容
27 | const rawContent = parseTextData(context, rawContentLength)
28 |
29 | const content = rawContent.trim()
30 |
31 | advanceBy(context, closeDelimiter.length)
32 |
33 | return {
34 | type: NodeTypes.INTERPOLATION,
35 | content: {
36 | type: NodeTypes.SIMPLE_EXPRESSION,
37 | content,
38 | },
39 | }
40 | }
41 |
42 | /**
43 | * 解析推进
44 | * 去除已解析部分
45 | */
46 | function advanceBy(context, length: number) {
47 | context.source = context.source.slice(length)
48 | }
49 |
50 | function parseChildren(context, ancestors) {
51 | const nodes: any[] = []
52 |
53 | while (!isEnd(context, ancestors)) {
54 | const s = context.source
55 | let node
56 |
57 | if (s.startsWith("{{")) {
58 | // 插值
59 | node = parseInterpolation(context)
60 | } else if (s[0] === "<") {
61 | // 标签
62 | if (/[a-z]/i.test(s[1])) {
63 | node = parseElement(context, ancestors)
64 | }
65 | }
66 |
67 | if (!node) {
68 | // 文本
69 | node = parseText(context)
70 | }
71 |
72 | nodes.push(node)
73 | }
74 |
75 | return nodes
76 | }
77 |
78 | function isEnd(context, ancestors) {
79 | const s = context.source
80 |
81 | // 2. 遇到结束标签
82 | if (s.startsWith("")) {
83 | for (let i = ancestors.length - 1; i >= 0; i--) {
84 | const tag = ancestors[i].tag
85 | if (startsWithEndTagOpen(s, tag)) return true
86 | }
87 | }
88 |
89 | // 1. source length 为 0 或不存在
90 | return !s
91 | }
92 |
93 | function parseText(context) {
94 | let endIndex = context.source.length
95 | const endTokens = ["<", "{{"]
96 |
97 | for (let i = 0; i < endTokens.length; i++) {
98 | const index = context.source.indexOf(endTokens[i])
99 | if (index !== -1 && endIndex > index) endIndex = index
100 | }
101 |
102 | const content = parseTextData(context, endIndex)
103 |
104 | return {
105 | type: NodeTypes.TEXT,
106 | content,
107 | }
108 | }
109 |
110 | function parseTextData(context, length: number) {
111 | const content = context.source.slice(0, length)
112 | advanceBy(context, length)
113 |
114 | return content
115 | }
116 |
117 | function parseElement(context, ancestors) {
118 | const element = parseTag(context, TagType.Start) as any
119 |
120 | ancestors.push(element)
121 | element.children = parseChildren(context, ancestors)
122 | ancestors.pop()
123 |
124 | if (startsWithEndTagOpen(context.source, element.tag))
125 | parseTag(context, TagType.End)
126 | else throw new Error(`lack of end tag: ${element.tag}`)
127 |
128 | return element
129 | }
130 |
131 | function startsWithEndTagOpen(source, tag) {
132 | return (
133 | source.startsWith("") &&
134 | source.slice(2, 2 + tag.length).toLowerCase() === tag
135 | )
136 | }
137 |
138 | function parseTag(context, type: TagType) {
139 | // 1. 解析 tag
140 | const match = /^<\/?([a-z]*)/i.exec(context.source)!
141 | const tag = match[1]
142 |
143 | // 2. 推进解析
144 | advanceBy(context, match[0].length + 1)
145 |
146 | if (type === TagType.End) return
147 |
148 | return {
149 | type: NodeTypes.ELEMENT,
150 | tag,
151 | }
152 | }
153 |
154 | function createRoot(children) {
155 | return {
156 | children,
157 | type: NodeTypes.ROOT,
158 | }
159 | }
160 |
161 | function createParserContext(content: string) {
162 | return {
163 | source: content,
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/examples/patchChildren/ArrayToArray.js:
--------------------------------------------------------------------------------
1 | import { h, ref } from "../../packages/vue3/dist/index.mjs"
2 |
3 | // 1. 左侧对比
4 | // (a b) c
5 | // (a b) d e
6 | // const prevChildren = [
7 | // h("div", { key: "A" }, "A"),
8 | // h("div", { key: "B" }, "B"),
9 | // h("div", { key: "C" }, "C"),
10 | // ]
11 | // const nextChildren = [
12 | // h("div", { key: "A" }, "A"),
13 | // h("div", { key: "B" }, "B"),
14 | // h("div", { key: "D" }, "D"),
15 | // h("div", { key: "E" }, "E"),
16 | // ]
17 |
18 | // 2. 右侧对比
19 | // a (b c)
20 | // d e (b c)
21 | // const prevChildren = [
22 | // h("div", { key: "A" }, "A"),
23 | // h("div", { key: "B" }, "B"),
24 | // h("div", { key: "C" }, "C"),
25 | // ]
26 | // const nextChildren = [
27 | // h("div", { key: "D" }, "D"),
28 | // h("div", { key: "E" }, "E"),
29 | // h("div", { key: "B" }, "B"),
30 | // h("div", { key: "C" }, "C"),
31 | // ]
32 |
33 | // 3. 新的比旧的多
34 | // 创建新的
35 | // 左侧
36 | // (a b)
37 | // (a b) c d
38 | // const prevChildren = [h("div", { key: "A" }, "A"), h("div", { key: "B" }, "B")]
39 | // const nextChildren = [
40 | // h("div", { key: "A" }, "A"),
41 | // h("div", { key: "B" }, "B"),
42 | // h("div", { key: "C" }, "C"),
43 | // h("div", { key: "D" }, "D"),
44 | // ]
45 |
46 | // 右侧
47 | // (a b)
48 | // d c (a b)
49 | // const prevChildren = [h("div", { key: "A" }, "A"), h("div", { key: "B" }, "B")]
50 | // const nextChildren = [
51 | // h("div", { key: "D" }, "D"),
52 | // h("div", { key: "C" }, "C"),
53 | // h("div", { key: "A" }, "A"),
54 | // h("div", { key: "B" }, "B"),
55 | // ]
56 |
57 | // 4. 新的比旧的少
58 | // 删除旧的多的
59 | // 左侧
60 | // (a b) c
61 | // (a b)
62 | // const prevChildren = [
63 | // h("div", { key: "A" }, "A"),
64 | // h("div", { key: "B" }, "B"),
65 | // h("div", { key: "C" }, "C"),
66 | // ]
67 | // const nextChildren = [h("div", { key: "A" }, "A"), h("div", { key: "B" }, "B")]
68 |
69 | // 右侧
70 | // a (b c)
71 | // (b c)
72 | // const prevChildren = [
73 | // h("div", { key: "A" }, "A"),
74 | // h("div", { key: "B" }, "B"),
75 | // h("div", { key: "C" }, "C"),
76 | // ]
77 | // const nextChildren = [h("div", { key: "B" }, "B"), h("div", { key: "C" }, "C")]
78 |
79 | // 5. 中间 (乱序)
80 | // 删除旧的 (在旧的里面存在,新的里面不存在)
81 | // 5.1
82 | // a b (c d) f g
83 | // a b (e c) f g
84 | // d 在新的里面没有 => 删除
85 | // c props 变化
86 | // const prevChildren = [
87 | // h("div", { key: "A" }, "A"),
88 | // h("div", { key: "B" }, "B"),
89 | // h("div", { key: "C", id: "c-prev" }, "C"),
90 | // h("div", { key: "D" }, "D"),
91 | // h("div", { key: "F" }, "F"),
92 | // h("div", { key: "G" }, "G"),
93 | // ]
94 | // const nextChildren = [
95 | // h("div", { key: "A" }, "A"),
96 | // h("div", { key: "B" }, "B"),
97 | // h("div", { key: "E" }, "E"),
98 | // h("div", { key: "C", id: "c-next" }, "C"),
99 | // h("div", { key: "F" }, "F"),
100 | // h("div", { key: "G" }, "G"),
101 | // ]
102 |
103 | // 5.1.1
104 | // a b (c e d) f g
105 | // a b (e c ) f g
106 | // 中间,旧的比新的多,多出来的可以直接删除
107 | // const prevChildren = [
108 | // h("div", { key: "A" }, "A"),
109 | // h("div", { key: "B" }, "B"),
110 | // h("div", { key: "C", id: "c-prev" }, "C"),
111 | // h("div", { key: "E" }, "E"),
112 | // h("div", { key: "D" }, "D"),
113 | // h("div", { key: "F" }, "F"),
114 | // h("div", { key: "G" }, "G"),
115 | // ]
116 | // const nextChildren = [
117 | // h("div", { key: "A" }, "A"),
118 | // h("div", { key: "B" }, "B"),
119 | // h("div", { key: "E" }, "E"),
120 | // h("div", { key: "C", id: "c-next" }, "C"),
121 | // h("div", { key: "F" }, "F"),
122 | // h("div", { key: "G" }, "G"),
123 | // ]
124 |
125 | // 5.2 移动
126 | // a b (c d e) f g
127 | // a b (e c d) f g
128 | // const prevChildren = [
129 | // h("div", { key: "A" }, "A"),
130 | // h("div", { key: "B" }, "B"),
131 | // h("div", { key: "C" }, "C"),
132 | // h("div", { key: "D" }, "D"),
133 | // h("div", { key: "E" }, "E"),
134 | // h("div", { key: "F" }, "F"),
135 | // h("div", { key: "G" }, "G"),
136 | // ]
137 | // const nextChildren = [
138 | // h("div", { key: "A" }, "A"),
139 | // h("div", { key: "B" }, "B"),
140 | // h("div", { key: "E" }, "E"),
141 | // h("div", { key: "C" }, "C"),
142 | // h("div", { key: "D" }, "D"),
143 | // h("div", { key: "F" }, "F"),
144 | // h("div", { key: "G" }, "G"),
145 | // ]
146 |
147 | // 5.3 创建
148 | // a b (c e) f g
149 | // a b (e c d) f g
150 | // const prevChildren = [
151 | // h("div", { key: "A" }, "A"),
152 | // h("div", { key: "B" }, "B"),
153 | // h("div", { key: "C" }, "C"),
154 | // h("div", { key: "E" }, "E"),
155 | // h("div", { key: "F" }, "F"),
156 | // h("div", { key: "G" }, "G"),
157 | // ]
158 | // const nextChildren = [
159 | // h("div", { key: "A" }, "A"),
160 | // h("div", { key: "B" }, "B"),
161 | // h("div", { key: "E" }, "E"),
162 | // h("div", { key: "C" }, "C"),
163 | // h("div", { key: "D" }, "D"),
164 | // h("div", { key: "F" }, "F"),
165 | // h("div", { key: "G" }, "G"),
166 | // ]
167 |
168 | // 综合
169 | // a b (c d e z) f g
170 | // a b (d c y e) f g
171 | // const prevChildren = [
172 | // h("div", { key: "A" }, "A"),
173 | // h("div", { key: "B" }, "B"),
174 | // h("div", { key: "C" }, "C"),
175 | // h("div", { key: "D" }, "D"),
176 | // h("div", { key: "E" }, "E"),
177 | // h("div", { key: "Z" }, "Z"),
178 | // h("div", { key: "F" }, "F"),
179 | // h("div", { key: "G" }, "G"),
180 | // ]
181 | // const nextChildren = [
182 | // h("div", { key: "A" }, "A"),
183 | // h("div", { key: "B" }, "B"),
184 | // h("div", { key: "D" }, "D"),
185 | // h("div", { key: "C" }, "C"),
186 | // h("div", { key: "Y" }, "Y"),
187 | // h("div", { key: "E" }, "E"),
188 | // h("div", { key: "F" }, "F"),
189 | // h("div", { key: "G" }, "G"),
190 | // ]
191 |
192 | // fix
193 | // C 应该是移动的,而不是被重新创建的 #4986
194 | const prevChildren = [
195 | h("div", { key: "A" }, "A"),
196 | h("div", {}, "C"),
197 | h("div", { key: "B" }, "B"),
198 | h("div", { key: "D" }, "D"),
199 | ]
200 | const nextChildren = [
201 | h("div", { key: "A" }, "A"),
202 | h("div", { key: "B" }, "B"),
203 | h("div", {}, "C"),
204 | h("div", { key: "D" }, "D"),
205 | ]
206 |
207 | export default {
208 | name: "ArrayToArray",
209 | setup() {
210 | const isChange = ref(false)
211 | window.isChange = isChange
212 |
213 | return {
214 | isChange,
215 | }
216 | },
217 | render() {
218 | const self = this
219 | return self.isChange === true
220 | ? h("div", {}, nextChildren)
221 | : h("div", {}, prevChildren)
222 | },
223 | }
224 |
--------------------------------------------------------------------------------
/packages/runtime-core/src/renderer.ts:
--------------------------------------------------------------------------------
1 | import { createComponentInstance, setupComponent } from "./component"
2 | import { ShapeFlags, EMPTY_OBJ } from "@mini-vue3/shared"
3 | import { Fragment, Text } from "./vnode"
4 | import { createAppApi } from "./createApp"
5 | import { effect } from "@mini-vue3/reactivity"
6 | import { shouldUpdateComponent } from "./componentUpdateUtils"
7 | import { queueJobs } from "./scheduler"
8 |
9 | export function createRenderer(options) {
10 | const {
11 | createElement: hostCreateElement,
12 | patchProp: hostPatchProp,
13 | insert: hostInsert,
14 | remove: hostRemove,
15 | setElementText: hostSetElementText,
16 | } = options
17 |
18 | function render(vnode, container) {
19 | // patch 递归处理
20 | patch(null, vnode, container, null, null)
21 | }
22 |
23 | /**
24 | * @param n1 old VNode
25 | * @param n2 new VNode
26 | */
27 | function patch(n1, n2, container, parentComponent, anchor) {
28 | // 处理组件
29 | const { type, shapeFlag } = n2
30 |
31 | // Fragment => 只渲染 children
32 | switch (type) {
33 | case Fragment:
34 | processFragment(n1, n2, container, parentComponent, anchor)
35 | break
36 | case Text:
37 | processText(n1, n2, container)
38 | break
39 |
40 | default:
41 | if (shapeFlag & ShapeFlags.ELEMENT) {
42 | processElement(n1, n2, container, parentComponent, anchor)
43 | } else if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
44 | processComponent(n1, n2, container, parentComponent, anchor)
45 | }
46 |
47 | break
48 | }
49 | }
50 |
51 | function processFragment(n1, n2, container, parentComponent, anchor) {
52 | mountChildren(n2.children, container, parentComponent, anchor)
53 | }
54 |
55 | function processText(n1, n2, container) {
56 | const { children } = n2
57 | const textNode = (n2.el = document.createTextNode(children))
58 | container.append(textNode)
59 | }
60 |
61 | function processElement(n1, n2, container, parentComponent, anchor) {
62 | // 初始化
63 | if (!n1) mountElement(n2, container, parentComponent, anchor)
64 | // 更新
65 | else patchElement(n1, n2, container, parentComponent, anchor)
66 | }
67 |
68 | function patchElement(n1, n2, container, parentComponent, anchor) {
69 | const oldProps = n1.props || EMPTY_OBJ
70 | const newProps = n2.props || EMPTY_OBJ
71 |
72 | const el = (n2.el = n1.el)
73 |
74 | patchChildren(n1, n2, el, parentComponent, anchor)
75 | patchProps(el, oldProps, newProps)
76 | }
77 |
78 | function patchChildren(n1, n2, container, parentComponent, anchor) {
79 | const prevShapeFlag = n1.shapeFlag
80 | const nextShapeFlag = n2.shapeFlag
81 |
82 | const c1 = n1.children
83 | const c2 = n2.children
84 |
85 | if (nextShapeFlag & ShapeFlags.TEXT_CHILDREN) {
86 | if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
87 | // 把 old children 清空
88 | unmountChildren(c1)
89 | }
90 |
91 | if (c1 !== c2) hostSetElementText(container, c2)
92 | } else {
93 | if (prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
94 | hostSetElementText(container, "")
95 | mountChildren(c2, container, parentComponent, anchor)
96 | } else {
97 | // array diff array
98 | patchKeyedChildren(c1, c2, container, parentComponent, anchor)
99 | }
100 | }
101 | }
102 |
103 | /**
104 | * 双端 diff
105 | */
106 | function patchKeyedChildren(
107 | c1,
108 | c2,
109 | container,
110 | parentComponent,
111 | parentAnchor,
112 | ) {
113 | const l2 = c2.length
114 |
115 | let i = 0
116 | let e1 = c1.length - 1
117 | let e2 = l2 - 1
118 |
119 | function isSameVNodeType(n1, n2) {
120 | // type
121 | // key
122 | return n1.type === n2.type && n1.key === n2.key
123 | }
124 |
125 | // 左侧
126 | while (i <= e1 && i <= e2) {
127 | const n1 = c1[i]
128 | const n2 = c2[i]
129 |
130 | if (isSameVNodeType(n1, n2)) {
131 | patch(n1, n2, container, parentComponent, parentAnchor)
132 | } else {
133 | break
134 | }
135 | i++
136 | }
137 |
138 | // 右侧
139 | while (i <= e1 && i <= e2) {
140 | const n1 = c1[e1]
141 | const n2 = c2[e2]
142 |
143 | if (isSameVNodeType(n1, n2)) {
144 | patch(n1, n2, container, parentComponent, parentAnchor)
145 | } else {
146 | break
147 | }
148 |
149 | e1--
150 | e2--
151 | }
152 |
153 | if (i > e1) {
154 | // 新的比旧的多 (创建)
155 | if (i <= e2) {
156 | const nextPos = e2 + 1
157 | const anchor = nextPos < l2 ? c2[nextPos].el : null
158 | while (i <= e2) {
159 | patch(null, c2[i], container, parentComponent, anchor)
160 | i++
161 | }
162 | }
163 | } else if (i > e2) {
164 | // 新的比旧的少 (删除)
165 | while (i <= e1) {
166 | hostRemove(c1[i].el)
167 | i++
168 | }
169 | } else {
170 | // 中间 (乱序)
171 | let s1 = i
172 | let s2 = i
173 |
174 | const toBePatched = e2 - s2 + 1
175 | let patched = 0
176 |
177 | const newIndexToOldIndexMap = new Array(toBePatched).fill(0)
178 | let moved = false
179 | let maxNewIndexSoFar = 0
180 |
181 | const keyToNewIndexMap = new Map()
182 |
183 | for (let j = s2; j <= e2; j++) {
184 | const nextChild = c2[j]
185 | keyToNewIndexMap.set(nextChild.key, j)
186 | }
187 |
188 | for (let j = s1; j <= e1; j++) {
189 | const prevChild = c1[j]
190 |
191 | if (patched >= toBePatched) {
192 | hostRemove(prevChild.el)
193 | continue
194 | }
195 |
196 | let newIndex
197 | if (prevChild.key != null) {
198 | newIndex = keyToNewIndexMap.get(prevChild.key)
199 | } else {
200 | for (let k = s2; k <= e2; k++) {
201 | if (isSameVNodeType(prevChild, c2[k])) {
202 | newIndex = k
203 | break
204 | }
205 | }
206 | }
207 |
208 | if (newIndex === undefined) hostRemove(prevChild.el)
209 | else {
210 | if (newIndex >= maxNewIndexSoFar) maxNewIndexSoFar = newIndex
211 | else moved = true
212 |
213 | newIndexToOldIndexMap[newIndex - s2] = j + 1
214 | patch(prevChild, c2[newIndex], container, parentComponent, null)
215 | patched++
216 | }
217 | }
218 |
219 | const increasingNewIndexSequence = moved
220 | ? getSequence(newIndexToOldIndexMap)
221 | : []
222 | let cur = increasingNewIndexSequence.length - 1
223 |
224 | for (let m = toBePatched - 1; m >= 0; m--) {
225 | const nextIndex = m + s2
226 | const nextChild = c2[nextIndex]
227 | const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : null
228 |
229 | if (newIndexToOldIndexMap[m] === 0) {
230 | patch(null, nextChild, container, parentComponent, anchor)
231 | } else if (moved) {
232 | if (cur < 0 || m !== increasingNewIndexSequence[cur]) {
233 | hostInsert(nextChild.el, container, anchor)
234 | } else {
235 | cur--
236 | }
237 | }
238 | }
239 | }
240 | }
241 |
242 | function unmountChildren(children) {
243 | for (let i = 0; i < children.length; i++) {
244 | const el = children[i].el
245 | // remove
246 | hostRemove(el)
247 | }
248 | }
249 |
250 | function patchProps(el, oldProps, newProps) {
251 | if (oldProps === newProps) return
252 |
253 | for (const key in newProps) {
254 | const prevProp = oldProps[key]
255 | const nextProp = newProps[key]
256 |
257 | if (prevProp !== nextProp) {
258 | hostPatchProp(el, key, prevProp, nextProp)
259 | }
260 | }
261 |
262 | if (oldProps === EMPTY_OBJ) return
263 |
264 | for (const key in oldProps) {
265 | if (!(key in newProps)) {
266 | hostPatchProp(el, key, oldProps[key], null)
267 | }
268 | }
269 | }
270 |
271 | function processComponent(n1, n2, container, parentComponent, anchor) {
272 | if (!n1) mountComponent(n2, container, parentComponent, anchor)
273 | else updateComponent(n1, n2)
274 | }
275 |
276 | function updateComponent(n1, n2) {
277 | const instance = (n2.component = n1.component)
278 |
279 | if (shouldUpdateComponent(n1, n2)) {
280 | // 把新的 VNode 挂载到实例上
281 | instance.next = n2
282 | instance.update()
283 | } else {
284 | n2.el = n1.el
285 | n2.vnode = n2
286 | }
287 | }
288 |
289 | function mountElement(vnode, container, parentComponent, anchor) {
290 | const { type, props, children, shapeFlag } = vnode
291 |
292 | const el = (vnode.el = hostCreateElement(type))
293 |
294 | if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
295 | el.textContent = children
296 | } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
297 | // vnode
298 | mountChildren(vnode.children, el, parentComponent, anchor)
299 | }
300 |
301 | // DOM
302 | // for (const key in props) {
303 | // const val = props[key]
304 |
305 | // // 判断 key 是不是以 on 开头
306 | // const isOn = (key: string) => /^on[A-Z]/.test(key)
307 |
308 | // if (isOn(key)) {
309 | // // 事件
310 | // const event = key.slice(2).toLowerCase()
311 | // el.addEventListener(event, val)
312 | // } else {
313 | // // 属性
314 | // el.setAttribute(key, val)
315 | // }
316 | // }
317 |
318 | // container.append(el)
319 |
320 | for (const key in props) {
321 | hostPatchProp(el, key, null, props[key])
322 | }
323 |
324 | hostInsert(el, container, anchor)
325 | }
326 |
327 | function mountChildren(children, container, parentComponent, anchor) {
328 | children.forEach((v) => {
329 | patch(null, v, container, parentComponent, anchor)
330 | })
331 | }
332 |
333 | function mountComponent(initialVNode, container, parentComponent, anchor) {
334 | // 创建组件实例
335 | const instance = (initialVNode.component = createComponentInstance(
336 | initialVNode,
337 | parentComponent,
338 | ))
339 |
340 | setupComponent(instance)
341 | setupRenderEffect(instance, initialVNode, container, anchor)
342 | }
343 |
344 | function setupRenderEffect(instance, initialVNode, container, anchor) {
345 | instance.update = effect(
346 | () => {
347 | if (!instance.isMounted) {
348 | const { proxy } = instance
349 | const subTree = (instance.subTree = instance.render.call(
350 | proxy,
351 | proxy,
352 | ))
353 |
354 | // vnode => patch
355 | // vnode => element => mountElement
356 | patch(null, subTree, container, instance, anchor)
357 |
358 | // 初始化完成
359 | initialVNode.el = subTree.el
360 | instance.isMounted = true
361 | } else {
362 | // 更新 props,需要新的 VNode
363 | const { next, vnode } = instance
364 | if (next) {
365 | next.el = vnode.el
366 | updateComponentPreRender(instance, next)
367 | }
368 |
369 | const { proxy } = instance
370 | const subTree = instance.render.call(proxy, proxy)
371 | const prevSubTree = instance.subTree
372 | instance.subTree = subTree
373 |
374 | patch(prevSubTree, subTree, container, instance, anchor)
375 | }
376 | },
377 | {
378 | scheduler() {
379 | queueJobs(instance.update)
380 | },
381 | },
382 | )
383 | }
384 |
385 | return {
386 | createApp: createAppApi(render),
387 | }
388 |
389 | function updateComponentPreRender(instance, nextVNode) {
390 | instance.vnode = nextVNode
391 | instance.next = null
392 | instance.props = nextVNode.props
393 | }
394 | }
395 |
396 | function getSequence(arr: number[]): number[] {
397 | const p = arr.slice()
398 | const result = [0]
399 | let i, j, u, v, c
400 | const len = arr.length
401 | for (i = 0; i < len; i++) {
402 | const arrI = arr[i]
403 | if (arrI !== 0) {
404 | j = result[result.length - 1]
405 | if (arr[j] < arrI) {
406 | p[i] = j
407 | result.push(i)
408 | continue
409 | }
410 | u = 0
411 | v = result.length - 1
412 | while (u < v) {
413 | c = (u + v) >> 1
414 | if (arr[result[c]] < arrI) {
415 | u = c + 1
416 | } else {
417 | v = c
418 | }
419 | }
420 | if (arrI < arr[result[u]]) {
421 | if (u > 0) {
422 | p[i] = result[u - 1]
423 | }
424 | result[u] = i
425 | }
426 | }
427 | }
428 | u = result.length
429 | v = result[u - 1]
430 | while (u-- > 0) {
431 | result[u] = v
432 | v = p[v]
433 | }
434 | return result
435 | }
436 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: 5.3
2 |
3 | importers:
4 |
5 | .:
6 | specifiers:
7 | pnpm: ^6.32.4
8 | prettier: ^2.6.1
9 | typescript: ^4.6.3
10 | unplugin-auto-import: ^0.6.9
11 | vitest: ^0.8.1
12 | devDependencies:
13 | pnpm: 6.32.4
14 | prettier: 2.6.1
15 | typescript: 4.6.3
16 | unplugin-auto-import: 0.6.9
17 | vitest: 0.8.1
18 |
19 | packages/compiler-core:
20 | specifiers:
21 | '@mini-vue3/shared': workspace:*
22 | tsup: ^5.12.1
23 | devDependencies:
24 | '@mini-vue3/shared': link:../shared
25 | tsup: 5.12.1_typescript@4.6.3
26 |
27 | packages/reactivity:
28 | specifiers:
29 | '@mini-vue3/shared': workspace:^0.0.0
30 | tsup: ^5.12.1
31 | dependencies:
32 | '@mini-vue3/shared': link:../shared
33 | devDependencies:
34 | tsup: 5.12.1_typescript@4.6.3
35 |
36 | packages/runtime-core:
37 | specifiers:
38 | '@mini-vue3/reactivity': workspace:*
39 | '@mini-vue3/shared': workspace:*
40 | tsup: ^5.12.1
41 | dependencies:
42 | '@mini-vue3/reactivity': link:../reactivity
43 | '@mini-vue3/shared': link:../shared
44 | devDependencies:
45 | tsup: 5.12.1_typescript@4.6.3
46 |
47 | packages/runtime-dom:
48 | specifiers:
49 | '@mini-vue3/runtime-core': workspace:*
50 | '@mini-vue3/shared': workspace:*
51 | tsup: ^5.12.1
52 | dependencies:
53 | '@mini-vue3/runtime-core': link:../runtime-core
54 | '@mini-vue3/shared': link:../shared
55 | devDependencies:
56 | tsup: 5.12.1_typescript@4.6.3
57 |
58 | packages/shared:
59 | specifiers:
60 | tsup: ^5.12.1
61 | devDependencies:
62 | tsup: 5.12.1_typescript@4.6.3
63 |
64 | packages/vue3:
65 | specifiers:
66 | '@mini-vue3/compiler-core': workspace:*
67 | '@mini-vue3/reactivity': workspace:*
68 | '@mini-vue3/runtime-dom': workspace:*
69 | tsup: ^5.12.1
70 | dependencies:
71 | '@mini-vue3/compiler-core': link:../compiler-core
72 | '@mini-vue3/reactivity': link:../reactivity
73 | '@mini-vue3/runtime-dom': link:../runtime-dom
74 | devDependencies:
75 | tsup: 5.12.1_typescript@4.6.3
76 |
77 | packages:
78 |
79 | /@antfu/utils/0.5.0:
80 | resolution: {integrity: sha512-MrAQ/MrPSxbh1bBrmwJjORfJymw4IqSHFBXqvxaga3ZdDM+/zokYF8DjyJpSjY2QmpmgQrajDUBJOWrYeARfzA==}
81 | dev: true
82 |
83 | /@nodelib/fs.scandir/2.1.5:
84 | resolution: {integrity: sha1-dhnC6yGyVIP20WdUi0z9WnSIw9U=, tarball: '@nodelib/fs.scandir/download/@nodelib/fs.scandir-2.1.5.tgz'}
85 | engines: {node: '>= 8'}
86 | dependencies:
87 | '@nodelib/fs.stat': 2.0.5
88 | run-parallel: 1.2.0
89 | dev: true
90 |
91 | /@nodelib/fs.stat/2.0.5:
92 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
93 | engines: {node: '>= 8'}
94 | dev: true
95 |
96 | /@nodelib/fs.walk/1.2.8:
97 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
98 | engines: {node: '>= 8'}
99 | dependencies:
100 | '@nodelib/fs.scandir': 2.1.5
101 | fastq: 1.13.0
102 | dev: true
103 |
104 | /@rollup/pluginutils/4.2.0:
105 | resolution: {integrity: sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA==}
106 | engines: {node: '>= 8.0.0'}
107 | dependencies:
108 | estree-walker: 2.0.2
109 | picomatch: 2.3.1
110 | dev: true
111 |
112 | /@types/chai-subset/1.3.3:
113 | resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
114 | dependencies:
115 | '@types/chai': 4.3.0
116 | dev: true
117 |
118 | /@types/chai/4.3.0:
119 | resolution: {integrity: sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==}
120 | dev: true
121 |
122 | /any-promise/1.3.0:
123 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
124 | dev: true
125 |
126 | /anymatch/3.1.2:
127 | resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
128 | engines: {node: '>= 8'}
129 | dependencies:
130 | normalize-path: 3.0.0
131 | picomatch: 2.3.1
132 | dev: true
133 |
134 | /array-union/2.1.0:
135 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
136 | engines: {node: '>=8'}
137 | dev: true
138 |
139 | /assertion-error/1.1.0:
140 | resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
141 | dev: true
142 |
143 | /balanced-match/1.0.2:
144 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
145 | dev: true
146 |
147 | /binary-extensions/2.2.0:
148 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
149 | engines: {node: '>=8'}
150 | dev: true
151 |
152 | /brace-expansion/1.1.11:
153 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
154 | dependencies:
155 | balanced-match: 1.0.2
156 | concat-map: 0.0.1
157 | dev: true
158 |
159 | /braces/3.0.2:
160 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
161 | engines: {node: '>=8'}
162 | dependencies:
163 | fill-range: 7.0.1
164 | dev: true
165 |
166 | /bundle-require/3.0.4_esbuild@0.14.29:
167 | resolution: {integrity: sha512-VXG6epB1yrLAvWVQpl92qF347/UXmncQj7J3U8kZEbdVZ1ZkQyr4hYeL/9RvcE8vVVdp53dY78Fd/3pqfRqI1A==}
168 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
169 | peerDependencies:
170 | esbuild: '>=0.13'
171 | dependencies:
172 | esbuild: 0.14.29
173 | load-tsconfig: 0.2.3
174 | dev: true
175 |
176 | /cac/6.7.12:
177 | resolution: {integrity: sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA==}
178 | engines: {node: '>=8'}
179 | dev: true
180 |
181 | /chai/4.3.6:
182 | resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==}
183 | engines: {node: '>=4'}
184 | dependencies:
185 | assertion-error: 1.1.0
186 | check-error: 1.0.2
187 | deep-eql: 3.0.1
188 | get-func-name: 2.0.0
189 | loupe: 2.3.4
190 | pathval: 1.1.1
191 | type-detect: 4.0.8
192 | dev: true
193 |
194 | /check-error/1.0.2:
195 | resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
196 | dev: true
197 |
198 | /chokidar/3.5.3:
199 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
200 | engines: {node: '>= 8.10.0'}
201 | dependencies:
202 | anymatch: 3.1.2
203 | braces: 3.0.2
204 | glob-parent: 5.1.2
205 | is-binary-path: 2.1.0
206 | is-glob: 4.0.3
207 | normalize-path: 3.0.0
208 | readdirp: 3.6.0
209 | optionalDependencies:
210 | fsevents: 2.3.2
211 | dev: true
212 |
213 | /commander/4.1.1:
214 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
215 | engines: {node: '>= 6'}
216 | dev: true
217 |
218 | /concat-map/0.0.1:
219 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=, tarball: concat-map/download/concat-map-0.0.1.tgz}
220 | dev: true
221 |
222 | /cross-spawn/7.0.3:
223 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
224 | engines: {node: '>= 8'}
225 | dependencies:
226 | path-key: 3.1.1
227 | shebang-command: 2.0.0
228 | which: 2.0.2
229 | dev: true
230 |
231 | /debug/4.3.4:
232 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
233 | engines: {node: '>=6.0'}
234 | peerDependencies:
235 | supports-color: '*'
236 | peerDependenciesMeta:
237 | supports-color:
238 | optional: true
239 | dependencies:
240 | ms: 2.1.2
241 | dev: true
242 |
243 | /deep-eql/3.0.1:
244 | resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==}
245 | engines: {node: '>=0.12'}
246 | dependencies:
247 | type-detect: 4.0.8
248 | dev: true
249 |
250 | /dir-glob/3.0.1:
251 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
252 | engines: {node: '>=8'}
253 | dependencies:
254 | path-type: 4.0.0
255 | dev: true
256 |
257 | /esbuild-android-64/0.14.29:
258 | resolution: {integrity: sha512-tJuaN33SVZyiHxRaVTo1pwW+rn3qetJX/SRuc/83rrKYtyZG0XfsQ1ao1nEudIt9w37ZSNXR236xEfm2C43sbw==}
259 | engines: {node: '>=12'}
260 | cpu: [x64]
261 | os: [android]
262 | requiresBuild: true
263 | dev: true
264 | optional: true
265 |
266 | /esbuild-android-arm64/0.14.29:
267 | resolution: {integrity: sha512-D74dCv6yYnMTlofVy1JKiLM5JdVSQd60/rQfJSDP9qvRAI0laPXIG/IXY1RG6jobmFMUfL38PbFnCqyI/6fPXg==}
268 | engines: {node: '>=12'}
269 | cpu: [arm64]
270 | os: [android]
271 | requiresBuild: true
272 | dev: true
273 | optional: true
274 |
275 | /esbuild-darwin-64/0.14.29:
276 | resolution: {integrity: sha512-+CJaRvfTkzs9t+CjGa0Oa28WoXa7EeLutQhxus+fFcu0MHhsBhlmeWHac3Cc/Sf/xPi1b2ccDFfzGYJCfV0RrA==}
277 | engines: {node: '>=12'}
278 | cpu: [x64]
279 | os: [darwin]
280 | requiresBuild: true
281 | dev: true
282 | optional: true
283 |
284 | /esbuild-darwin-arm64/0.14.29:
285 | resolution: {integrity: sha512-5Wgz/+zK+8X2ZW7vIbwoZ613Vfr4A8HmIs1XdzRmdC1kG0n5EG5fvKk/jUxhNlrYPx1gSY7XadQ3l4xAManPSw==}
286 | engines: {node: '>=12'}
287 | cpu: [arm64]
288 | os: [darwin]
289 | requiresBuild: true
290 | dev: true
291 | optional: true
292 |
293 | /esbuild-freebsd-64/0.14.29:
294 | resolution: {integrity: sha512-VTfS7Bm9QA12JK1YXF8+WyYOfvD7WMpbArtDj6bGJ5Sy5xp01c/q70Arkn596aGcGj0TvQRplaaCIrfBG1Wdtg==}
295 | engines: {node: '>=12'}
296 | cpu: [x64]
297 | os: [freebsd]
298 | requiresBuild: true
299 | dev: true
300 | optional: true
301 |
302 | /esbuild-freebsd-arm64/0.14.29:
303 | resolution: {integrity: sha512-WP5L4ejwLWWvd3Fo2J5mlXvG3zQHaw5N1KxFGnUc4+2ZFZknP0ST63i0IQhpJLgEJwnQpXv2uZlU1iWZjFqEIg==}
304 | engines: {node: '>=12'}
305 | cpu: [arm64]
306 | os: [freebsd]
307 | requiresBuild: true
308 | dev: true
309 | optional: true
310 |
311 | /esbuild-linux-32/0.14.29:
312 | resolution: {integrity: sha512-4myeOvFmQBWdI2U1dEBe2DCSpaZyjdQtmjUY11Zu2eQg4ynqLb8Y5mNjNU9UN063aVsCYYfbs8jbken/PjyidA==}
313 | engines: {node: '>=12'}
314 | cpu: [ia32]
315 | os: [linux]
316 | requiresBuild: true
317 | dev: true
318 | optional: true
319 |
320 | /esbuild-linux-64/0.14.29:
321 | resolution: {integrity: sha512-iaEuLhssReAKE7HMwxwFJFn7D/EXEs43fFy5CJeA4DGmU6JHh0qVJD2p/UP46DvUXLRKXsXw0i+kv5TdJ1w5pg==}
322 | engines: {node: '>=12'}
323 | cpu: [x64]
324 | os: [linux]
325 | requiresBuild: true
326 | dev: true
327 | optional: true
328 |
329 | /esbuild-linux-arm/0.14.29:
330 | resolution: {integrity: sha512-OXa9D9QL1hwrAnYYAHt/cXAuSCmoSqYfTW/0CEY0LgJNyTxJKtqc5mlwjAZAvgyjmha0auS/sQ0bXfGf2wAokQ==}
331 | engines: {node: '>=12'}
332 | cpu: [arm]
333 | os: [linux]
334 | requiresBuild: true
335 | dev: true
336 | optional: true
337 |
338 | /esbuild-linux-arm64/0.14.29:
339 | resolution: {integrity: sha512-KYf7s8wDfUy+kjKymW3twyGT14OABjGHRkm9gPJ0z4BuvqljfOOUbq9qT3JYFnZJHOgkr29atT//hcdD0Pi7Mw==}
340 | engines: {node: '>=12'}
341 | cpu: [arm64]
342 | os: [linux]
343 | requiresBuild: true
344 | dev: true
345 | optional: true
346 |
347 | /esbuild-linux-mips64le/0.14.29:
348 | resolution: {integrity: sha512-05jPtWQMsZ1aMGfHOvnR5KrTvigPbU35BtuItSSWLI2sJu5VrM8Pr9Owym4wPvA4153DFcOJ1EPN/2ujcDt54g==}
349 | engines: {node: '>=12'}
350 | cpu: [mips64el]
351 | os: [linux]
352 | requiresBuild: true
353 | dev: true
354 | optional: true
355 |
356 | /esbuild-linux-ppc64le/0.14.29:
357 | resolution: {integrity: sha512-FYhBqn4Ir9xG+f6B5VIQVbRuM4S6qwy29dDNYFPoxLRnwTEKToIYIUESN1qHyUmIbfO0YB4phG2JDV2JDN9Kgw==}
358 | engines: {node: '>=12'}
359 | cpu: [ppc64]
360 | os: [linux]
361 | requiresBuild: true
362 | dev: true
363 | optional: true
364 |
365 | /esbuild-linux-riscv64/0.14.29:
366 | resolution: {integrity: sha512-eqZMqPehkb4nZcffnuOpXJQdGURGd6GXQ4ZsDHSWyIUaA+V4FpMBe+5zMPtXRD2N4BtyzVvnBko6K8IWWr36ew==}
367 | engines: {node: '>=12'}
368 | cpu: [riscv64]
369 | os: [linux]
370 | requiresBuild: true
371 | dev: true
372 | optional: true
373 |
374 | /esbuild-linux-s390x/0.14.29:
375 | resolution: {integrity: sha512-o7EYajF1rC/4ho7kpSG3gENVx0o2SsHm7cJ5fvewWB/TEczWU7teDgusGSujxCYcMottE3zqa423VTglNTYhjg==}
376 | engines: {node: '>=12'}
377 | cpu: [s390x]
378 | os: [linux]
379 | requiresBuild: true
380 | dev: true
381 | optional: true
382 |
383 | /esbuild-netbsd-64/0.14.29:
384 | resolution: {integrity: sha512-/esN6tb6OBSot6+JxgeOZeBk6P8V/WdR3GKBFeFpSqhgw4wx7xWUqPrdx4XNpBVO7X4Ipw9SAqgBrWHlXfddww==}
385 | engines: {node: '>=12'}
386 | cpu: [x64]
387 | os: [netbsd]
388 | requiresBuild: true
389 | dev: true
390 | optional: true
391 |
392 | /esbuild-openbsd-64/0.14.29:
393 | resolution: {integrity: sha512-jUTdDzhEKrD0pLpjmk0UxwlfNJNg/D50vdwhrVcW/D26Vg0hVbthMfb19PJMatzclbK7cmgk1Nu0eNS+abzoHw==}
394 | engines: {node: '>=12'}
395 | cpu: [x64]
396 | os: [openbsd]
397 | requiresBuild: true
398 | dev: true
399 | optional: true
400 |
401 | /esbuild-sunos-64/0.14.29:
402 | resolution: {integrity: sha512-EfhQN/XO+TBHTbkxwsxwA7EfiTHFe+MNDfxcf0nj97moCppD9JHPq48MLtOaDcuvrTYOcrMdJVeqmmeQ7doTcg==}
403 | engines: {node: '>=12'}
404 | cpu: [x64]
405 | os: [sunos]
406 | requiresBuild: true
407 | dev: true
408 | optional: true
409 |
410 | /esbuild-windows-32/0.14.29:
411 | resolution: {integrity: sha512-uoyb0YAJ6uWH4PYuYjfGNjvgLlb5t6b3zIaGmpWPOjgpr1Nb3SJtQiK4YCPGhONgfg2v6DcJgSbOteuKXhwqAw==}
412 | engines: {node: '>=12'}
413 | cpu: [ia32]
414 | os: [win32]
415 | requiresBuild: true
416 | dev: true
417 | optional: true
418 |
419 | /esbuild-windows-64/0.14.29:
420 | resolution: {integrity: sha512-X9cW/Wl95QjsH8WUyr3NqbmfdU72jCp71cH3pwPvI4CgBM2IeOUDdbt6oIGljPu2bf5eGDIo8K3Y3vvXCCTd8A==}
421 | engines: {node: '>=12'}
422 | cpu: [x64]
423 | os: [win32]
424 | requiresBuild: true
425 | dev: true
426 | optional: true
427 |
428 | /esbuild-windows-arm64/0.14.29:
429 | resolution: {integrity: sha512-+O/PI+68fbUZPpl3eXhqGHTGK7DjLcexNnyJqtLZXOFwoAjaXlS5UBCvVcR3o2va+AqZTj8o6URaz8D2K+yfQQ==}
430 | engines: {node: '>=12'}
431 | cpu: [arm64]
432 | os: [win32]
433 | requiresBuild: true
434 | dev: true
435 | optional: true
436 |
437 | /esbuild/0.14.29:
438 | resolution: {integrity: sha512-SQS8cO8xFEqevYlrHt6exIhK853Me4nZ4aMW6ieysInLa0FMAL+AKs87HYNRtR2YWRcEIqoXAHh+Ytt5/66qpg==}
439 | engines: {node: '>=12'}
440 | hasBin: true
441 | requiresBuild: true
442 | optionalDependencies:
443 | esbuild-android-64: 0.14.29
444 | esbuild-android-arm64: 0.14.29
445 | esbuild-darwin-64: 0.14.29
446 | esbuild-darwin-arm64: 0.14.29
447 | esbuild-freebsd-64: 0.14.29
448 | esbuild-freebsd-arm64: 0.14.29
449 | esbuild-linux-32: 0.14.29
450 | esbuild-linux-64: 0.14.29
451 | esbuild-linux-arm: 0.14.29
452 | esbuild-linux-arm64: 0.14.29
453 | esbuild-linux-mips64le: 0.14.29
454 | esbuild-linux-ppc64le: 0.14.29
455 | esbuild-linux-riscv64: 0.14.29
456 | esbuild-linux-s390x: 0.14.29
457 | esbuild-netbsd-64: 0.14.29
458 | esbuild-openbsd-64: 0.14.29
459 | esbuild-sunos-64: 0.14.29
460 | esbuild-windows-32: 0.14.29
461 | esbuild-windows-64: 0.14.29
462 | esbuild-windows-arm64: 0.14.29
463 | dev: true
464 |
465 | /estree-walker/2.0.2:
466 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
467 | dev: true
468 |
469 | /execa/5.1.1:
470 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
471 | engines: {node: '>=10'}
472 | dependencies:
473 | cross-spawn: 7.0.3
474 | get-stream: 6.0.1
475 | human-signals: 2.1.0
476 | is-stream: 2.0.1
477 | merge-stream: 2.0.0
478 | npm-run-path: 4.0.1
479 | onetime: 5.1.2
480 | signal-exit: 3.0.7
481 | strip-final-newline: 2.0.0
482 | dev: true
483 |
484 | /fast-glob/3.2.11:
485 | resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==}
486 | engines: {node: '>=8.6.0'}
487 | dependencies:
488 | '@nodelib/fs.stat': 2.0.5
489 | '@nodelib/fs.walk': 1.2.8
490 | glob-parent: 5.1.2
491 | merge2: 1.4.1
492 | micromatch: 4.0.5
493 | dev: true
494 |
495 | /fastq/1.13.0:
496 | resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==}
497 | dependencies:
498 | reusify: 1.0.4
499 | dev: true
500 |
501 | /fill-range/7.0.1:
502 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
503 | engines: {node: '>=8'}
504 | dependencies:
505 | to-regex-range: 5.0.1
506 | dev: true
507 |
508 | /fs.realpath/1.0.0:
509 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
510 | dev: true
511 |
512 | /fsevents/2.3.2:
513 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
514 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
515 | os: [darwin]
516 | requiresBuild: true
517 | dev: true
518 | optional: true
519 |
520 | /function-bind/1.1.1:
521 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
522 | dev: true
523 |
524 | /get-func-name/2.0.0:
525 | resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
526 | dev: true
527 |
528 | /get-stream/6.0.1:
529 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
530 | engines: {node: '>=10'}
531 | dev: true
532 |
533 | /glob-parent/5.1.2:
534 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
535 | engines: {node: '>= 6'}
536 | dependencies:
537 | is-glob: 4.0.3
538 | dev: true
539 |
540 | /glob/7.1.6:
541 | resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
542 | dependencies:
543 | fs.realpath: 1.0.0
544 | inflight: 1.0.6
545 | inherits: 2.0.4
546 | minimatch: 3.1.2
547 | once: 1.4.0
548 | path-is-absolute: 1.0.1
549 | dev: true
550 |
551 | /globby/11.1.0:
552 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
553 | engines: {node: '>=10'}
554 | dependencies:
555 | array-union: 2.1.0
556 | dir-glob: 3.0.1
557 | fast-glob: 3.2.11
558 | ignore: 5.2.0
559 | merge2: 1.4.1
560 | slash: 3.0.0
561 | dev: true
562 |
563 | /has/1.0.3:
564 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
565 | engines: {node: '>= 0.4.0'}
566 | dependencies:
567 | function-bind: 1.1.1
568 | dev: true
569 |
570 | /human-signals/2.1.0:
571 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
572 | engines: {node: '>=10.17.0'}
573 | dev: true
574 |
575 | /ignore/5.2.0:
576 | resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==}
577 | engines: {node: '>= 4'}
578 | dev: true
579 |
580 | /inflight/1.0.6:
581 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
582 | dependencies:
583 | once: 1.4.0
584 | wrappy: 1.0.2
585 | dev: true
586 |
587 | /inherits/2.0.4:
588 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
589 | dev: true
590 |
591 | /is-binary-path/2.1.0:
592 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
593 | engines: {node: '>=8'}
594 | dependencies:
595 | binary-extensions: 2.2.0
596 | dev: true
597 |
598 | /is-core-module/2.8.1:
599 | resolution: {integrity: sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==}
600 | dependencies:
601 | has: 1.0.3
602 | dev: true
603 |
604 | /is-extglob/2.1.1:
605 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
606 | engines: {node: '>=0.10.0'}
607 | dev: true
608 |
609 | /is-glob/4.0.3:
610 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
611 | engines: {node: '>=0.10.0'}
612 | dependencies:
613 | is-extglob: 2.1.1
614 | dev: true
615 |
616 | /is-number/7.0.0:
617 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
618 | engines: {node: '>=0.12.0'}
619 | dev: true
620 |
621 | /is-stream/2.0.1:
622 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
623 | engines: {node: '>=8'}
624 | dev: true
625 |
626 | /isexe/2.0.0:
627 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
628 | dev: true
629 |
630 | /joycon/3.1.1:
631 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
632 | engines: {node: '>=10'}
633 | dev: true
634 |
635 | /lilconfig/2.0.5:
636 | resolution: {integrity: sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==}
637 | engines: {node: '>=10'}
638 | dev: true
639 |
640 | /lines-and-columns/1.2.4:
641 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
642 | dev: true
643 |
644 | /load-tsconfig/0.2.3:
645 | resolution: {integrity: sha512-iyT2MXws+dc2Wi6o3grCFtGXpeMvHmJqS27sMPGtV2eUu4PeFnG+33I8BlFK1t1NWMjOpcx9bridn5yxLDX2gQ==}
646 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
647 | dev: true
648 |
649 | /local-pkg/0.4.1:
650 | resolution: {integrity: sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==}
651 | engines: {node: '>=14'}
652 | dev: true
653 |
654 | /loupe/2.3.4:
655 | resolution: {integrity: sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==}
656 | dependencies:
657 | get-func-name: 2.0.0
658 | dev: true
659 |
660 | /magic-string/0.26.1:
661 | resolution: {integrity: sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==}
662 | engines: {node: '>=12'}
663 | dependencies:
664 | sourcemap-codec: 1.4.8
665 | dev: true
666 |
667 | /merge-stream/2.0.0:
668 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
669 | dev: true
670 |
671 | /merge2/1.4.1:
672 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
673 | engines: {node: '>= 8'}
674 | dev: true
675 |
676 | /micromatch/4.0.5:
677 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
678 | engines: {node: '>=8.6'}
679 | dependencies:
680 | braces: 3.0.2
681 | picomatch: 2.3.1
682 | dev: true
683 |
684 | /mimic-fn/2.1.0:
685 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
686 | engines: {node: '>=6'}
687 | dev: true
688 |
689 | /minimatch/3.1.2:
690 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
691 | dependencies:
692 | brace-expansion: 1.1.11
693 | dev: true
694 |
695 | /ms/2.1.2:
696 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
697 | dev: true
698 |
699 | /mz/2.7.0:
700 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
701 | dependencies:
702 | any-promise: 1.3.0
703 | object-assign: 4.1.1
704 | thenify-all: 1.6.0
705 | dev: true
706 |
707 | /nanoid/3.3.2:
708 | resolution: {integrity: sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==}
709 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
710 | hasBin: true
711 | dev: true
712 |
713 | /normalize-path/3.0.0:
714 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
715 | engines: {node: '>=0.10.0'}
716 | dev: true
717 |
718 | /npm-run-path/4.0.1:
719 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
720 | engines: {node: '>=8'}
721 | dependencies:
722 | path-key: 3.1.1
723 | dev: true
724 |
725 | /object-assign/4.1.1:
726 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
727 | engines: {node: '>=0.10.0'}
728 | dev: true
729 |
730 | /once/1.4.0:
731 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
732 | dependencies:
733 | wrappy: 1.0.2
734 | dev: true
735 |
736 | /onetime/5.1.2:
737 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
738 | engines: {node: '>=6'}
739 | dependencies:
740 | mimic-fn: 2.1.0
741 | dev: true
742 |
743 | /path-is-absolute/1.0.1:
744 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
745 | engines: {node: '>=0.10.0'}
746 | dev: true
747 |
748 | /path-key/3.1.1:
749 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
750 | engines: {node: '>=8'}
751 | dev: true
752 |
753 | /path-parse/1.0.7:
754 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
755 | dev: true
756 |
757 | /path-type/4.0.0:
758 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
759 | engines: {node: '>=8'}
760 | dev: true
761 |
762 | /pathval/1.1.1:
763 | resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
764 | dev: true
765 |
766 | /picocolors/1.0.0:
767 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
768 | dev: true
769 |
770 | /picomatch/2.3.1:
771 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
772 | engines: {node: '>=8.6'}
773 | dev: true
774 |
775 | /pirates/4.0.5:
776 | resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==}
777 | engines: {node: '>= 6'}
778 | dev: true
779 |
780 | /pnpm/6.32.4:
781 | resolution: {integrity: sha512-rOG+VpOzs6g/MR5HWc8KTlLAx3ljdRJCMQwSg1DE/hzAaqF/Y2zIHH0u6dZw/XnRb9w1U8rOs9MJT9jMt7e+Qw==}
782 | engines: {node: '>=12.17'}
783 | hasBin: true
784 | dev: true
785 |
786 | /postcss-load-config/3.1.4:
787 | resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
788 | engines: {node: '>= 10'}
789 | peerDependencies:
790 | postcss: '>=8.0.9'
791 | ts-node: '>=9.0.0'
792 | peerDependenciesMeta:
793 | postcss:
794 | optional: true
795 | ts-node:
796 | optional: true
797 | dependencies:
798 | lilconfig: 2.0.5
799 | yaml: 1.10.2
800 | dev: true
801 |
802 | /postcss/8.4.12:
803 | resolution: {integrity: sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==}
804 | engines: {node: ^10 || ^12 || >=14}
805 | dependencies:
806 | nanoid: 3.3.2
807 | picocolors: 1.0.0
808 | source-map-js: 1.0.2
809 | dev: true
810 |
811 | /prettier/2.6.1:
812 | resolution: {integrity: sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==}
813 | engines: {node: '>=10.13.0'}
814 | hasBin: true
815 | dev: true
816 |
817 | /queue-microtask/1.2.3:
818 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
819 | dev: true
820 |
821 | /readdirp/3.6.0:
822 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
823 | engines: {node: '>=8.10.0'}
824 | dependencies:
825 | picomatch: 2.3.1
826 | dev: true
827 |
828 | /resolve-from/5.0.0:
829 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
830 | engines: {node: '>=8'}
831 | dev: true
832 |
833 | /resolve/1.22.0:
834 | resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
835 | hasBin: true
836 | dependencies:
837 | is-core-module: 2.8.1
838 | path-parse: 1.0.7
839 | supports-preserve-symlinks-flag: 1.0.0
840 | dev: true
841 |
842 | /reusify/1.0.4:
843 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
844 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
845 | dev: true
846 |
847 | /rollup/2.70.1:
848 | resolution: {integrity: sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==}
849 | engines: {node: '>=10.0.0'}
850 | hasBin: true
851 | optionalDependencies:
852 | fsevents: 2.3.2
853 | dev: true
854 |
855 | /run-parallel/1.2.0:
856 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
857 | dependencies:
858 | queue-microtask: 1.2.3
859 | dev: true
860 |
861 | /shebang-command/2.0.0:
862 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
863 | engines: {node: '>=8'}
864 | dependencies:
865 | shebang-regex: 3.0.0
866 | dev: true
867 |
868 | /shebang-regex/3.0.0:
869 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
870 | engines: {node: '>=8'}
871 | dev: true
872 |
873 | /signal-exit/3.0.7:
874 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
875 | dev: true
876 |
877 | /slash/3.0.0:
878 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
879 | engines: {node: '>=8'}
880 | dev: true
881 |
882 | /source-map-js/1.0.2:
883 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
884 | engines: {node: '>=0.10.0'}
885 | dev: true
886 |
887 | /source-map/0.7.3:
888 | resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==}
889 | engines: {node: '>= 8'}
890 | dev: true
891 |
892 | /sourcemap-codec/1.4.8:
893 | resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
894 | dev: true
895 |
896 | /strip-final-newline/2.0.0:
897 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
898 | engines: {node: '>=6'}
899 | dev: true
900 |
901 | /sucrase/3.20.3:
902 | resolution: {integrity: sha512-azqwq0/Bs6RzLAdb4dXxsCgMtAaD2hzmUr4UhSfsxO46JFPAwMnnb441B/qsudZiS6Ylea3JXZe3Q497lsgXzQ==}
903 | engines: {node: '>=8'}
904 | hasBin: true
905 | dependencies:
906 | commander: 4.1.1
907 | glob: 7.1.6
908 | lines-and-columns: 1.2.4
909 | mz: 2.7.0
910 | pirates: 4.0.5
911 | ts-interface-checker: 0.1.13
912 | dev: true
913 |
914 | /supports-preserve-symlinks-flag/1.0.0:
915 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
916 | engines: {node: '>= 0.4'}
917 | dev: true
918 |
919 | /thenify-all/1.6.0:
920 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
921 | engines: {node: '>=0.8'}
922 | dependencies:
923 | thenify: 3.3.1
924 | dev: true
925 |
926 | /thenify/3.3.1:
927 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
928 | dependencies:
929 | any-promise: 1.3.0
930 | dev: true
931 |
932 | /tinypool/0.1.2:
933 | resolution: {integrity: sha512-fvtYGXoui2RpeMILfkvGIgOVkzJEGediv8UJt7TxdAOY8pnvUkFg/fkvqTfXG9Acc9S17Cnn1S4osDc2164guA==}
934 | engines: {node: '>=14.0.0'}
935 | dev: true
936 |
937 | /tinyspy/0.3.0:
938 | resolution: {integrity: sha512-c5uFHqtUp74R2DJE3/Efg0mH5xicmgziaQXMm/LvuuZn3RdpADH32aEGDRyCzObXT1DNfwDMqRQ/Drh1MlO12g==}
939 | engines: {node: '>=14.0.0'}
940 | dev: true
941 |
942 | /to-regex-range/5.0.1:
943 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
944 | engines: {node: '>=8.0'}
945 | dependencies:
946 | is-number: 7.0.0
947 | dev: true
948 |
949 | /tree-kill/1.2.2:
950 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
951 | hasBin: true
952 | dev: true
953 |
954 | /ts-interface-checker/0.1.13:
955 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
956 | dev: true
957 |
958 | /tsup/5.12.1_typescript@4.6.3:
959 | resolution: {integrity: sha512-vI7E4T6+6n5guQ9UKUOkQmzd1n4V9abGK71lbnzJMLJspbkNby5zlwWvgvHafLdYCb1WXpjFuqqmNLjBA0Wz3g==}
960 | hasBin: true
961 | peerDependencies:
962 | typescript: ^4.1.0
963 | peerDependenciesMeta:
964 | typescript:
965 | optional: true
966 | dependencies:
967 | bundle-require: 3.0.4_esbuild@0.14.29
968 | cac: 6.7.12
969 | chokidar: 3.5.3
970 | debug: 4.3.4
971 | esbuild: 0.14.29
972 | execa: 5.1.1
973 | globby: 11.1.0
974 | joycon: 3.1.1
975 | postcss-load-config: 3.1.4
976 | resolve-from: 5.0.0
977 | rollup: 2.70.1
978 | source-map: 0.7.3
979 | sucrase: 3.20.3
980 | tree-kill: 1.2.2
981 | typescript: 4.6.3
982 | transitivePeerDependencies:
983 | - postcss
984 | - supports-color
985 | - ts-node
986 | dev: true
987 |
988 | /type-detect/4.0.8:
989 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
990 | engines: {node: '>=4'}
991 | dev: true
992 |
993 | /typescript/4.6.3:
994 | resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==}
995 | engines: {node: '>=4.2.0'}
996 | hasBin: true
997 | dev: true
998 |
999 | /unplugin-auto-import/0.6.9:
1000 | resolution: {integrity: sha512-IqgT7AoRrNQwNhiF/wDH3sMEDX8SqCYBEgJzwdg5441b5aiC5VwZz0J0wYqkaKu89YkZE9DG6rQ2JpFfZv1iiQ==}
1001 | engines: {node: '>=14'}
1002 | peerDependencies:
1003 | '@vueuse/core': '*'
1004 | peerDependenciesMeta:
1005 | '@vueuse/core':
1006 | optional: true
1007 | dependencies:
1008 | '@antfu/utils': 0.5.0
1009 | '@rollup/pluginutils': 4.2.0
1010 | local-pkg: 0.4.1
1011 | magic-string: 0.26.1
1012 | resolve: 1.22.0
1013 | unplugin: 0.4.0
1014 | transitivePeerDependencies:
1015 | - esbuild
1016 | - rollup
1017 | - vite
1018 | - webpack
1019 | dev: true
1020 |
1021 | /unplugin/0.4.0:
1022 | resolution: {integrity: sha512-4ScITEmzlz1iZW3tkz+3L1V5k/xMQ6kjgm4lEXKxH0ozd8/OUWfiSA7RMRyrawsvq/t50JIzPpp1UyuSL/AXkA==}
1023 | peerDependencies:
1024 | esbuild: '>=0.13'
1025 | rollup: ^2.50.0
1026 | vite: ^2.3.0
1027 | webpack: 4 || 5
1028 | peerDependenciesMeta:
1029 | esbuild:
1030 | optional: true
1031 | rollup:
1032 | optional: true
1033 | vite:
1034 | optional: true
1035 | webpack:
1036 | optional: true
1037 | dependencies:
1038 | chokidar: 3.5.3
1039 | webpack-virtual-modules: 0.4.3
1040 | dev: true
1041 |
1042 | /vite/2.9.1:
1043 | resolution: {integrity: sha512-vSlsSdOYGcYEJfkQ/NeLXgnRv5zZfpAsdztkIrs7AZHV8RCMZQkwjo4DS5BnrYTqoWqLoUe1Cah4aVO4oNNqCQ==}
1044 | engines: {node: '>=12.2.0'}
1045 | hasBin: true
1046 | peerDependencies:
1047 | less: '*'
1048 | sass: '*'
1049 | stylus: '*'
1050 | peerDependenciesMeta:
1051 | less:
1052 | optional: true
1053 | sass:
1054 | optional: true
1055 | stylus:
1056 | optional: true
1057 | dependencies:
1058 | esbuild: 0.14.29
1059 | postcss: 8.4.12
1060 | resolve: 1.22.0
1061 | rollup: 2.70.1
1062 | optionalDependencies:
1063 | fsevents: 2.3.2
1064 | dev: true
1065 |
1066 | /vitest/0.8.1:
1067 | resolution: {integrity: sha512-8HUyc9io1UInZTUKJWTgPG5fMEqd86IlMCjn2CEjGjli55M7iUJiiMwns9W21wKs6DMrRs2lu89X6rbfVvF53A==}
1068 | engines: {node: '>=v14.16.0'}
1069 | hasBin: true
1070 | peerDependencies:
1071 | '@vitest/ui': '*'
1072 | c8: '*'
1073 | happy-dom: '*'
1074 | jsdom: '*'
1075 | peerDependenciesMeta:
1076 | '@vitest/ui':
1077 | optional: true
1078 | c8:
1079 | optional: true
1080 | happy-dom:
1081 | optional: true
1082 | jsdom:
1083 | optional: true
1084 | dependencies:
1085 | '@types/chai': 4.3.0
1086 | '@types/chai-subset': 1.3.3
1087 | chai: 4.3.6
1088 | local-pkg: 0.4.1
1089 | tinypool: 0.1.2
1090 | tinyspy: 0.3.0
1091 | vite: 2.9.1
1092 | transitivePeerDependencies:
1093 | - less
1094 | - sass
1095 | - stylus
1096 | dev: true
1097 |
1098 | /webpack-virtual-modules/0.4.3:
1099 | resolution: {integrity: sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw==}
1100 | dev: true
1101 |
1102 | /which/2.0.2:
1103 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
1104 | engines: {node: '>= 8'}
1105 | hasBin: true
1106 | dependencies:
1107 | isexe: 2.0.0
1108 | dev: true
1109 |
1110 | /wrappy/1.0.2:
1111 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
1112 | dev: true
1113 |
1114 | /yaml/1.10.2:
1115 | resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
1116 | engines: {node: '>= 6'}
1117 | dev: true
1118 |
--------------------------------------------------------------------------------