├── 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("

hi

{{message}}
") 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("= 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(" 删除 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 | --------------------------------------------------------------------------------