├── .vscode └── settings.json ├── README.md ├── .gitignore ├── src ├── index.ts ├── runtime-core │ ├── index.ts │ ├── componentProps.ts │ ├── h.ts │ ├── createApp.ts │ ├── componentPublicInstance.ts │ ├── vnode.ts │ ├── component.ts │ └── renderer.ts ├── shared │ ├── shapeFlags.ts │ └── index.ts └── reactivity │ ├── computed.ts │ ├── tests │ ├── shallowReactive.spec.ts │ ├── readonly.spec.ts │ ├── shallowReadonly.spec.ts │ ├── reactive.spec.ts │ ├── computed.spec.ts │ ├── effect.spec.ts │ └── ref.spec.ts │ ├── reactive.ts │ ├── ref.ts │ ├── baseHandlers.ts │ └── index.ts ├── babel.config.js ├── example └── helloword │ ├── main.js │ ├── Foo.js │ ├── index.html │ └── App.js ├── rollup.config.js ├── package.json ├── lib ├── mini_vue.esm.js └── mini_vue.cjs.js └── tsconfig.json /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mini_vue 2 | mini_vue 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | .history/* -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './runtime-core' -------------------------------------------------------------------------------- /src/runtime-core/index.ts: -------------------------------------------------------------------------------- 1 | export { createApp } from './createApp' 2 | export { h } from './h' -------------------------------------------------------------------------------- /src/runtime-core/componentProps.ts: -------------------------------------------------------------------------------- 1 | export function initProps(instance: any, props: any) { 2 | instance.props = props || {}; 3 | } -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@babel/preset-env', {targets: {node: 'current'}}], 4 | '@babel/preset-typescript'], 5 | }; -------------------------------------------------------------------------------- /src/runtime-core/h.ts: -------------------------------------------------------------------------------- 1 | import { createVNode } from "./vnode"; 2 | 3 | export function h(type: any, props?: any, children?: any) { 4 | return createVNode(type, props, children); 5 | } -------------------------------------------------------------------------------- /src/shared/shapeFlags.ts: -------------------------------------------------------------------------------- 1 | export const enum ShapeFlags { 2 | ELEMENT = 1, // 0001 3 | STATEFUL_COMPONENT = 1 << 1, // 0010 4 | TEXT_CHILDREN = 1 << 2, // 0100 5 | ARRAY_CHILDREN = 1 << 3, // 1000 6 | } -------------------------------------------------------------------------------- /example/helloword/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | createApp 3 | } from '../../lib/mini_vue.esm.js'; 4 | import { 5 | App 6 | } from './App.js'; 7 | 8 | const rootContainer = document.querySelector('#app') 9 | createApp(App).mount(rootContainer); -------------------------------------------------------------------------------- /src/runtime-core/createApp.ts: -------------------------------------------------------------------------------- 1 | import { render } from "./renderer"; 2 | import { createVNode } from "./vnode"; 3 | 4 | export function createApp(rootComponent: any) { 5 | return { 6 | mount(rootContainer: any) { 7 | const vnode = createVNode(rootComponent); 8 | render(vnode, rootContainer); 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /example/helloword/Foo.js: -------------------------------------------------------------------------------- 1 | import { 2 | h 3 | } from "../../lib/mini_vue.esm.js" 4 | 5 | export default { 6 | name: 'Foo', 7 | setup(props) { 8 | console.log(props.count) 9 | props.count++ 10 | console.log(props.count) 11 | }, 12 | render() { 13 | return h('div', { 14 | id: 'foo' 15 | }, 'foo:' + this.count) 16 | } 17 | } -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "@rollup/plugin-typescript" 2 | import pkg from './package.json' 3 | 4 | export default { 5 | input: './src/index.ts', 6 | output: [{ 7 | format: 'cjs', 8 | file: pkg.main, 9 | }, 10 | { 11 | format: 'es', 12 | file: pkg.module, 13 | } 14 | ], 15 | plugins: [ 16 | typescript() 17 | ] 18 | } -------------------------------------------------------------------------------- /example/helloword/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/shared/index.ts: -------------------------------------------------------------------------------- 1 | export const extend = Object.assign 2 | 3 | export const isObject = (val: any) => { 4 | return val !== null && typeof val === 'object' 5 | } 6 | 7 | export const hasChanged = (newValue: any, value: any) => !Object.is(newValue, value) 8 | 9 | export const isOn = (key: string) => /^on[A-Z]/.test(key) 10 | 11 | export const hasOwn = (obj: any, key: any) => Object.hasOwnProperty.call(obj, key); -------------------------------------------------------------------------------- /src/runtime-core/componentPublicInstance.ts: -------------------------------------------------------------------------------- 1 | import { hasOwn } from "../shared/index"; 2 | 3 | const publicPropertiesMap: any = { 4 | $el: (i: any) => i.$el, 5 | } 6 | 7 | export const PublicInstanceProxyHandlers = { 8 | get({ _: instance }: any, key: string, receiver: any) { 9 | const { setupState, props } = instance; 10 | if (hasOwn(setupState, key)) { 11 | return setupState[key]; 12 | } else if (hasOwn(props, key)) { 13 | return props[key]; 14 | } 15 | 16 | if (key in publicPropertiesMap) { 17 | return publicPropertiesMap[key](instance) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/reactivity/computed.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveEffect } from "." 2 | 3 | class ComputedRefImpl { 4 | private _dirty: boolean = false 5 | private _effect: ReactiveEffect 6 | private _value: any 7 | constructor(getter: any) { 8 | this._effect = new ReactiveEffect(getter, () => { 9 | if (this._dirty) { 10 | this._dirty = false 11 | } 12 | }) 13 | } 14 | get value() { 15 | if (!this._dirty) { 16 | this._value = this._effect.run() 17 | this._dirty = true 18 | return this._value 19 | } 20 | return this._value 21 | } 22 | } 23 | 24 | 25 | export function computed(getter: any) { 26 | return new ComputedRefImpl(getter) 27 | } -------------------------------------------------------------------------------- /src/runtime-core/vnode.ts: -------------------------------------------------------------------------------- 1 | import { ShapeFlags } from "../shared/shapeFlags" 2 | 3 | 4 | export function createVNode(type: any, props?: any, children?: any) { 5 | // type 可能是 string | Component,例如 'div' | MyComponent 6 | const vnode = { 7 | type, 8 | props, 9 | children, 10 | $el: null, 11 | shapeFlag: getShapeFlag(type) 12 | }; 13 | 14 | if (typeof children === 'string') { 15 | vnode.shapeFlag |= ShapeFlags.TEXT_CHILDREN; 16 | } else if (typeof children === 'object') { 17 | vnode.shapeFlag |= ShapeFlags.ARRAY_CHILDREN; 18 | } 19 | 20 | return vnode; 21 | } 22 | 23 | function getShapeFlag(type: any) { 24 | return typeof type === 'string' ? ShapeFlags.ELEMENT : ShapeFlags.STATEFUL_COMPONENT 25 | } -------------------------------------------------------------------------------- /example/helloword/App.js: -------------------------------------------------------------------------------- 1 | import { 2 | h 3 | } from '../../lib/mini_vue.esm.js'; 4 | 5 | import Foo from './Foo.js'; 6 | // component 7 | window.self = null 8 | export const App = { 9 | render() { 10 | window.self = this 11 | return h('div', { 12 | id: 'main', 13 | class: ['blue'], 14 | onClick() { 15 | console.log('click') 16 | } 17 | }, [ 18 | h('span', { 19 | id: 'text', 20 | class: ['yellow'] 21 | }, this.msg), 22 | h('span', { 23 | id: 'text', 24 | class: ['red'] 25 | }, '我是span!'), 26 | h(Foo, { 27 | count: 1 28 | }) 29 | ]); 30 | }, 31 | setup() { 32 | return { 33 | msg: 'Hello World!' 34 | }; 35 | } 36 | } -------------------------------------------------------------------------------- /src/reactivity/tests/shallowReactive.spec.ts: -------------------------------------------------------------------------------- 1 | import { effect } from ".."; 2 | import { isReactive, isReadonly, readonly, shallowReactive } from "../reactive"; 3 | 4 | describe("shallowReactive", () => { 5 | test("should not make non-reactive properties reactive", () => { 6 | const props = shallowReactive({ m: 2, n: { foo: 1 } }); 7 | expect(isReactive(props.n)).toBe(false); 8 | expect(isReactive(props)).toBe(true); 9 | let total 10 | effect(() => { 11 | total = props.m + 1 12 | }) 13 | expect(total).toBe(3) 14 | props.m++ 15 | expect(total).toBe(4) 16 | 17 | let other 18 | effect(() => { 19 | other = props.n.foo + 1 20 | }) 21 | expect(other).toBe(2) 22 | props.n.foo++ 23 | expect(other).toBe(2) 24 | }); 25 | }); -------------------------------------------------------------------------------- /src/reactivity/tests/readonly.spec.ts: -------------------------------------------------------------------------------- 1 | import { isProxy, isReadonly, readonly } from "../reactive"; 2 | 3 | describe('readonly', () => { 4 | it('readonly', () => { 5 | const original = { foo: 1, bar: { baz: 2 } }; 6 | const warpped = readonly(original); 7 | expect(warpped.foo).toBe(1); 8 | expect(warpped).not.toBe(original); 9 | expect(isReadonly(warpped)).toBe(true); 10 | expect(isReadonly(warpped.bar)).toBe(true); 11 | expect(isReadonly(original)).toBe(false); 12 | expect(isProxy(original)).toBe(false); 13 | expect(isProxy(warpped)).toBe(true); 14 | }); 15 | 16 | it('warn then call set', () => { 17 | console.warn = jest.fn() 18 | const user = readonly({ 19 | age: 10 20 | }) 21 | user.age = 11 22 | expect(console.warn).toBeCalled() 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/reactivity/tests/shallowReadonly.spec.ts: -------------------------------------------------------------------------------- 1 | import { isReactive, isReadonly, readonly, shallowReadonly } from "../reactive"; 2 | 3 | describe("shallowReadonly", () => { 4 | test("should not make non-reactive properties reactive", () => { 5 | const props = shallowReadonly({ n: { foo: 1 } }); 6 | expect(isReactive(props.n)).toBe(false); 7 | expect(isReactive(props)).toBe(false); 8 | }); 9 | test("should differentiate from normal readonly calls", async () => { 10 | const original = { foo: {} }; 11 | const shallowProxy = shallowReadonly(original); 12 | const reactiveProxy = readonly(original); 13 | expect(shallowProxy).not.toBe(reactiveProxy); 14 | expect(isReadonly(shallowProxy)).toBe(true); 15 | expect(isReadonly(shallowProxy.foo)).toBe(false); 16 | expect(isReadonly(reactiveProxy.foo)).toBe(true); 17 | }); 18 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini_vue", 3 | "version": "1.0.0", 4 | "main": "lib/mini_vue.cjs.js", 5 | "module": "lib/mini_vue.esm.js", 6 | "repository": "https://github.com/Enochzzz/mini_vue.git", 7 | "author": "Enoch ", 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "jest", 11 | "build": "rollup -c rollup.config.js" 12 | }, 13 | "devDependencies": { 14 | "@babel/core": "^7.17.9", 15 | "@babel/preset-env": "^7.16.11", 16 | "@babel/preset-typescript": "^7.16.7", 17 | "@rollup/plugin-typescript": "^8.3.2", 18 | "@types/jest": "^27.4.1", 19 | "babel-jest": "^27.5.1", 20 | "cz-conventional-changelog": "^3.3.0", 21 | "jest": "^27.5.1", 22 | "rollup": "^2.71.1", 23 | "typescript": "^4.6.3" 24 | }, 25 | "config": { 26 | "commitizen": { 27 | "path": "./node_modules/cz-conventional-changelog" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/reactivity/tests/reactive.spec.ts: -------------------------------------------------------------------------------- 1 | import { effect } from '..'; 2 | import { isProxy, isReactive, reactive } from '../reactive' 3 | 4 | describe('reactive', () => { 5 | it('reactive', () => { 6 | const obj = { 7 | foo: 'foo', 8 | }; 9 | const objRec = reactive(obj); 10 | expect(objRec.foo).toBe('foo'); 11 | expect(obj).not.toBe(objRec); 12 | expect(isReactive(objRec)).toBe(true); 13 | expect(isReactive(obj)).toBe(false); 14 | expect(isProxy(objRec)).toBe(true); 15 | expect(isProxy(obj)).toBe(false); 16 | }); 17 | 18 | it('nested reactive', () => { 19 | const obj = { 20 | a: { b: 1 }, 21 | c: [{ d: 2 }] 22 | } 23 | const objRec = reactive(obj); 24 | let total 25 | effect(() => { 26 | total = objRec.a 27 | }) 28 | expect(isReactive(objRec.a)).toBe(true); 29 | expect(isReactive(objRec.c)).toBe(true); 30 | expect(isReactive(objRec.c[0])).toBe(true); 31 | 32 | objRec.a = 3 33 | expect(total).toBe(3) 34 | }); 35 | 36 | }); 37 | 38 | -------------------------------------------------------------------------------- /src/reactivity/reactive.ts: -------------------------------------------------------------------------------- 1 | import { isObject } from '../shared/index'; 2 | import { mutableHandlers, readonlyHandlers, shallowReactiveHandlers, shallowReadonlyHandlers } from './baseHandlers'; 3 | 4 | export const enum ReaciveFlags { 5 | IS_REACTIVE = '__v_isReactive', 6 | IS_READONLY = '__v_isReadonly' 7 | } 8 | 9 | export function reactive(obj: any) { 10 | return createActiveObject(obj, mutableHandlers) 11 | } 12 | 13 | export function readonly(obj: any) { 14 | return createActiveObject(obj, readonlyHandlers) 15 | } 16 | 17 | export function shallowReactive(obj: any) { 18 | return createActiveObject(obj, shallowReactiveHandlers) 19 | } 20 | 21 | export function shallowReadonly(obj: any) { 22 | return createActiveObject(obj, shallowReadonlyHandlers) 23 | } 24 | 25 | function createActiveObject(obj: any, baseHandlers: any) { 26 | if (!isObject(obj)) { 27 | return console.warn(`target: ${obj} is not an Object`) 28 | } 29 | return new Proxy(obj, baseHandlers) 30 | } 31 | 32 | export function isReactive(obj: any) { 33 | return !!obj[ReaciveFlags.IS_REACTIVE] 34 | } 35 | 36 | export function isReadonly(obj: any) { 37 | return !!obj[ReaciveFlags.IS_READONLY] 38 | } 39 | 40 | export function isProxy(value: any) { 41 | return isReactive(value) || isReadonly(value) 42 | } -------------------------------------------------------------------------------- /src/runtime-core/component.ts: -------------------------------------------------------------------------------- 1 | import { shallowReadonly } from "../reactivity/reactive"; 2 | import { initProps } from "./componentProps"; 3 | import { PublicInstanceProxyHandlers } from "./componentPublicInstance"; 4 | 5 | 6 | export function createComponentInstance(vnode: any): any { 7 | return { 8 | vnode, 9 | type: vnode.type, 10 | setupState: {}, 11 | $el: null, 12 | props: {} 13 | } 14 | } 15 | 16 | export function setupComponent(instance: any) { 17 | initProps(instance, instance.vnode.props); 18 | // initSlots() 19 | instance.proxy = new Proxy({ _: instance }, PublicInstanceProxyHandlers) 20 | setupStatefulComponent(instance) 21 | } 22 | 23 | function setupStatefulComponent(instance: any) { 24 | const setup = instance.type.setup; 25 | if (setup) { 26 | const setupResult = setup(shallowReadonly(instance.props)); 27 | handleSetupResult(instance, setupResult); 28 | } 29 | } 30 | 31 | function handleSetupResult(instance: any, setupResult: any) { 32 | if (typeof setupResult === 'object') { 33 | instance.setupState = setupResult; 34 | } 35 | 36 | finishComponentSetup(instance); 37 | } 38 | 39 | function finishComponentSetup(instance: any) { 40 | const component = instance.type 41 | if (component.render) { 42 | instance.render = component.render 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/reactivity/tests/computed.spec.ts: -------------------------------------------------------------------------------- 1 | import { computed } from "../computed"; 2 | import { reactive } from "../reactive"; 3 | 4 | describe("computed", () => { 5 | it("happy path", () => { 6 | const value = reactive({ 7 | foo: 1, 8 | }); 9 | 10 | const getter = computed(() => { 11 | return value.foo; 12 | }); 13 | 14 | expect(getter.value).toBe(1); 15 | value.foo = 2; 16 | expect(getter.value).toBe(2); 17 | }); 18 | 19 | it("should compute lazily", () => { 20 | const value = reactive({ 21 | foo: 1, 22 | }); 23 | const getter = jest.fn(() => { 24 | return value.foo; 25 | }); 26 | const cValue = computed(getter); 27 | 28 | // lazy 29 | expect(getter).not.toHaveBeenCalled(); 30 | 31 | expect(cValue.value).toBe(1); 32 | expect(getter).toHaveBeenCalledTimes(1); 33 | 34 | // should not compute again 35 | cValue.value; 36 | expect(getter).toHaveBeenCalledTimes(1); 37 | 38 | // should not compute until needed 39 | value.foo = 2; 40 | expect(getter).toHaveBeenCalledTimes(1); 41 | 42 | // now it should compute 43 | expect(cValue.value).toBe(2); 44 | expect(getter).toHaveBeenCalledTimes(2); 45 | 46 | // should not compute again 47 | cValue.value; 48 | expect(getter).toHaveBeenCalledTimes(2); 49 | }); 50 | }); -------------------------------------------------------------------------------- /src/reactivity/ref.ts: -------------------------------------------------------------------------------- 1 | import { isTracking, trackEffect, trigerEffects } from "."; 2 | import { hasChanged, isObject } from "../shared"; 3 | import { reactive } from "./reactive"; 4 | 5 | 6 | const createRefImpl = (val: any) => ({ 7 | _v_isRef: true, 8 | _rawValue: val, 9 | _value: convert(val), 10 | dep: new Set, 11 | get value() { 12 | if (isTracking()) { 13 | trackEffect(this.dep); 14 | } 15 | return this._value; 16 | }, 17 | set value(newValue) { 18 | if (!hasChanged(newValue, this._rawValue)) return; 19 | this._value = convert(newValue); 20 | this._rawValue = newValue; 21 | trigerEffects(this.dep) 22 | }, 23 | }) 24 | 25 | export function convert(value: any) { 26 | return isObject(value) ? reactive(value) : value; 27 | } 28 | 29 | export function ref(value: any) { 30 | return createRefImpl(value); 31 | } 32 | 33 | export function isRef(ref: any) { 34 | return !!ref._v_isRef 35 | } 36 | 37 | export function unRef(ref: any) { 38 | return isRef(ref) ? ref.value : ref 39 | } 40 | 41 | export function proxyRefs(objectWithRef: any) { 42 | return new Proxy(objectWithRef, { 43 | get(target: any, key: any, receiver: any) { 44 | return unRef(Reflect.get(target, key, receiver)) 45 | }, 46 | set(target: any, key: any, value: any, receiver: any) { 47 | if (isRef(target[key]) && !isRef(value)) { 48 | return (target[key].value = value); 49 | } else { 50 | return Reflect.set(target, key, value, receiver) 51 | } 52 | } 53 | }) 54 | } -------------------------------------------------------------------------------- /src/reactivity/baseHandlers.ts: -------------------------------------------------------------------------------- 1 | import { track, triger } from "."; 2 | import { extend, isObject } from "../shared"; 3 | import { ReaciveFlags, reactive, readonly } from "./reactive"; 4 | 5 | const get = createGetter(); 6 | const set = createSetter(); 7 | const readonlyGet = createGetter(true); 8 | const shallowReactiveGet = createGetter(false, true); 9 | const shallowReadonlyGet = createGetter(true, true); 10 | 11 | function createGetter(isReadonly = false, isShallow = false) { 12 | return function (target: any, key: any, receiver: any) { 13 | const res = Reflect.get(target, key, receiver); 14 | if (key === ReaciveFlags.IS_REACTIVE) { 15 | return !isReadonly 16 | } else if (key === ReaciveFlags.IS_READONLY) { 17 | return isReadonly 18 | } 19 | 20 | if (!isReadonly) { 21 | track(target, key); // 触发订阅 22 | } 23 | 24 | if (isShallow) { 25 | return res 26 | } 27 | 28 | if (isObject(res)) { 29 | return isReadonly ? readonly(res) : reactive(res) 30 | } 31 | 32 | return res 33 | } 34 | } 35 | 36 | function createSetter() { 37 | return function (target: any, key: any, value: any, receiver: any) { 38 | const res = Reflect.set(target, key, value, receiver); 39 | triger(target, key); // 触发订阅 40 | return res 41 | } 42 | } 43 | 44 | export const mutableHandlers = { 45 | get, 46 | set 47 | } 48 | 49 | export const readonlyHandlers = { 50 | get: readonlyGet, 51 | set(target: any, key: any, value: any, receiver: any) { 52 | console.warn(`Set ${key} failed, it's readonly`, target); 53 | return true 54 | } 55 | } 56 | 57 | export const shallowReactiveHandlers = extend({}, mutableHandlers, { 58 | get: shallowReactiveGet 59 | }) 60 | 61 | export const shallowReadonlyHandlers = extend({}, readonlyHandlers, { 62 | get: shallowReadonlyGet 63 | }) -------------------------------------------------------------------------------- /src/runtime-core/renderer.ts: -------------------------------------------------------------------------------- 1 | import { isOn } from '../shared/index'; 2 | import { ShapeFlags } from '../shared/shapeFlags'; 3 | import { createComponentInstance, setupComponent } from './component'; 4 | 5 | export function render(vnode: any, container: any) { 6 | // ... 7 | patch(vnode, container); 8 | } 9 | 10 | function patch(vnode: any, container: any) { 11 | if (vnode.shapeFlag & ShapeFlags.ELEMENT) { 12 | processElement(vnode, container) 13 | } else if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) { 14 | processComponent(vnode, container); 15 | } 16 | } 17 | 18 | function processElement(vnode: any, container: any) { 19 | // type 20 | const el = vnode.$el = document.createElement(vnode.type); 21 | // props 22 | const { props } = vnode 23 | for (let key in props) { 24 | const val = props[key]; 25 | if (isOn(key)) { 26 | const event = key.slice(2).toLocaleLowerCase() 27 | el.addEventListener(event, val) 28 | } else { 29 | el.setAttribute(key, val); 30 | } 31 | } 32 | // children 33 | if (vnode.shapeFlag & ShapeFlags.TEXT_CHILDREN) { 34 | el.innerText = vnode.children; 35 | } else if (vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN) { 36 | mountChildren(vnode, el) 37 | } 38 | // append 39 | container.appendChild(el); 40 | } 41 | 42 | function mountChildren(vnode: any, el: any) { 43 | for (let i = 0; i < vnode.children.length; i++) { 44 | patch(vnode.children[i], el); 45 | } 46 | } 47 | 48 | function processComponent(vnode: any, container: any) { 49 | mountComponent(vnode, container); 50 | } 51 | 52 | function mountComponent(initinalVNode: any, container: any) { 53 | const instance = createComponentInstance(initinalVNode); 54 | setupComponent(instance); 55 | setupRenderEffect(instance, container); 56 | } 57 | 58 | function setupRenderEffect(instance: any, container: any) { 59 | const subTree = instance.render.call(instance.proxy); 60 | patch(subTree, container); 61 | 62 | // 最终是要给rootcomponet实例上添加el 63 | instance.$el = subTree.$el; 64 | } -------------------------------------------------------------------------------- /src/reactivity/tests/effect.spec.ts: -------------------------------------------------------------------------------- 1 | import { effect, stop } from '../index' 2 | import { reactive } from '../reactive'; 3 | 4 | describe('effect', () => { 5 | it('effect', () => { 6 | const record = reactive({ 7 | count: 0 8 | }) 9 | let total; 10 | effect(() => { 11 | total = record.count + 1 12 | }) 13 | expect(total).toBe(1) 14 | record.count = 4 15 | expect(total).toBe(5) 16 | }); 17 | 18 | it('test runner returned by the effect', () => { 19 | let count = 0 20 | const runner = effect(() => { 21 | count++ 22 | return 'hello' 23 | }) 24 | expect(count).toBe(1) 25 | const r = runner() 26 | expect(count).toBe(2) 27 | expect(r).toBe('hello') 28 | }); 29 | 30 | 31 | it('scheduler', () => { 32 | let dummy 33 | let run: any 34 | const scheduler = jest.fn(() => { 35 | run = runner 36 | }) 37 | const obj = reactive({ foo: 1 }) 38 | const runner = effect( 39 | () => { 40 | dummy = obj.foo 41 | }, 42 | { scheduler } 43 | ) 44 | expect(scheduler).not.toHaveBeenCalled() 45 | expect(dummy).toBe(1) 46 | // should be called on first trigger 47 | obj.foo++ 48 | expect(scheduler).toHaveBeenCalledTimes(1) 49 | // should not run yet 50 | expect(dummy).toBe(1) 51 | // manually run 52 | run() 53 | // should have run 54 | expect(dummy).toBe(2) 55 | }) 56 | 57 | it('stop', () => { 58 | let dummy 59 | const obj = reactive({ prop: 1 }) 60 | const runner = effect(() => { 61 | dummy = obj.prop 62 | }) 63 | obj.prop = 2 64 | expect(dummy).toBe(2) 65 | stop(runner) 66 | // obj.prop = 3 67 | // obj.prop = obj.prop + 1 68 | // expect(dummy).toBe(2) 69 | // // stopped effect should still be manually callable 70 | // runner() 71 | // expect(dummy).toBe(3) 72 | }) 73 | 74 | it('events: onStop', () => { 75 | const onStop = jest.fn() 76 | const runner = effect(() => { }, { 77 | onStop 78 | }) 79 | 80 | stop(runner) 81 | expect(onStop).toHaveBeenCalled() 82 | }) 83 | }); -------------------------------------------------------------------------------- /src/reactivity/tests/ref.spec.ts: -------------------------------------------------------------------------------- 1 | import { effect } from ".."; 2 | import { reactive } from "../reactive"; 3 | import { isRef, proxyRefs, ref, unRef } from "../ref"; 4 | 5 | describe("ref", () => { 6 | it('ref', () => { 7 | const count = ref(1) 8 | expect(count.value).toBe(1) 9 | }); 10 | 11 | it("should be reactive", () => { 12 | const a = ref(1); 13 | let dummy; 14 | let calls = 0; 15 | effect(() => { 16 | calls++; 17 | dummy = a.value; 18 | }); 19 | expect(calls).toBe(1); 20 | expect(dummy).toBe(1); 21 | a.value = 2; 22 | expect(calls).toBe(2); 23 | expect(dummy).toBe(2); 24 | // same value should not trigger 25 | a.value = 2; 26 | expect(calls).toBe(2); 27 | expect(dummy).toBe(2); 28 | }); 29 | 30 | it("should make nested properties reactive", () => { 31 | const obj = { 32 | count: 1, 33 | } 34 | const a = ref(obj); 35 | let dummy; 36 | let calls = 0; 37 | effect(() => { 38 | calls++; 39 | dummy = a.value.count; 40 | }); 41 | expect(dummy).toBe(1); 42 | a.value.count = 2; 43 | expect(dummy).toBe(2); 44 | a.value = obj; 45 | expect(dummy).toBe(2); 46 | expect(calls).toBe(2); 47 | }); 48 | 49 | it("proxyRefs", () => { 50 | const user = { 51 | age: ref(10), 52 | name: "xiaohong", 53 | }; 54 | const proxyUser = proxyRefs(user); 55 | expect(user.age.value).toBe(10); 56 | expect(proxyUser.age).toBe(10); 57 | expect(proxyUser.name).toBe("xiaohong"); 58 | 59 | (proxyUser as any).age = 20; 60 | expect(proxyUser.age).toBe(20); 61 | expect(user.age.value).toBe(20); 62 | 63 | proxyUser.age = ref(10); 64 | expect(proxyUser.age).toBe(10); 65 | expect(user.age.value).toBe(10); 66 | }); 67 | 68 | it("isRef", () => { 69 | const a = ref(1); 70 | const user = reactive({ 71 | age: 1, 72 | }); 73 | expect(isRef(a)).toBe(true); 74 | expect(isRef(1)).toBe(false); 75 | expect(isRef(user)).toBe(false); 76 | }); 77 | 78 | it("unRef", () => { 79 | const a = ref(1); 80 | expect(unRef(a)).toBe(1); 81 | expect(unRef(1)).toBe(1); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /src/reactivity/index.ts: -------------------------------------------------------------------------------- 1 | import { extend } from "../shared" 2 | 3 | 4 | const targetMap = new WeakMap 5 | export function track(target: any, key: any) { 6 | if (!isTracking()) return 7 | let depsMap = targetMap.get(target) 8 | if (!depsMap) { 9 | depsMap = new Map 10 | targetMap.set(target, depsMap) 11 | } 12 | let depsSet = depsMap.get(key) 13 | if (!depsSet) { 14 | depsSet = new Set 15 | depsMap.set(key, depsSet) 16 | } 17 | trackEffect(depsSet) 18 | } 19 | 20 | export function isTracking() { 21 | return !!activeEffect 22 | } 23 | 24 | export function trackEffect(depsSet: any) { 25 | if (depsSet.has(activeEffect)) return 26 | depsSet.add(activeEffect) 27 | activeEffect.deps.push(depsSet) 28 | } 29 | 30 | export function triger(target: any, key: any) { 31 | const depsMap = targetMap.get(target) 32 | const depsSet = depsMap.get(key) 33 | trigerEffects(depsSet) 34 | } 35 | 36 | export function trigerEffects(depsSet: any) { 37 | for (const effect of depsSet) { 38 | if (effect.scheduler) { 39 | effect.scheduler() 40 | } else { 41 | effect.run() 42 | } 43 | } 44 | } 45 | 46 | let activeEffect: any; 47 | export function effect(fn: any, options: any = {}) { 48 | const { scheduler } = options 49 | const _effect = new ReactiveEffect(fn, scheduler) 50 | extend(_effect, options) 51 | _effect.run() 52 | const runner: any = _effect.run.bind(_effect) 53 | runner.effect = _effect 54 | return runner 55 | } 56 | 57 | export class ReactiveEffect { 58 | public deps = [] 59 | private active = false 60 | private onStop?: () => void 61 | 62 | constructor(private _fn: any, public scheduler?: any) { 63 | } 64 | 65 | public run() { 66 | if (this.active) { 67 | return this._fn() 68 | } 69 | activeEffect = this 70 | const result = this._fn() 71 | activeEffect = null 72 | return result 73 | } 74 | 75 | public stop() { 76 | if (this.active) return 77 | this.active = true 78 | if (this.onStop) this.onStop() 79 | clearupeffect(this) 80 | } 81 | } 82 | 83 | function clearupeffect(effect: any) { 84 | effect.deps.forEach((depSet: any) => { 85 | depSet.delete(effect) 86 | }); 87 | } 88 | 89 | export function stop(runner: any) { 90 | runner.effect.stop() 91 | } -------------------------------------------------------------------------------- /lib/mini_vue.esm.js: -------------------------------------------------------------------------------- 1 | const extend = Object.assign; 2 | const isObject = (val) => { 3 | return val !== null && typeof val === 'object'; 4 | }; 5 | const isOn = (key) => /^on[A-Z]/.test(key); 6 | const hasOwn = (obj, key) => Object.hasOwnProperty.call(obj, key); 7 | 8 | const targetMap = new WeakMap; 9 | function triger(target, key) { 10 | const depsMap = targetMap.get(target); 11 | const depsSet = depsMap.get(key); 12 | trigerEffects(depsSet); 13 | } 14 | function trigerEffects(depsSet) { 15 | for (const effect of depsSet) { 16 | if (effect.scheduler) { 17 | effect.scheduler(); 18 | } 19 | else { 20 | effect.run(); 21 | } 22 | } 23 | } 24 | 25 | const get = createGetter(); 26 | const set = createSetter(); 27 | const readonlyGet = createGetter(true); 28 | const shallowReactiveGet = createGetter(false, true); 29 | const shallowReadonlyGet = createGetter(true, true); 30 | function createGetter(isReadonly = false, isShallow = false) { 31 | return function (target, key, receiver) { 32 | const res = Reflect.get(target, key, receiver); 33 | if (key === "__v_isReactive" /* IS_REACTIVE */) { 34 | return !isReadonly; 35 | } 36 | else if (key === "__v_isReadonly" /* IS_READONLY */) { 37 | return isReadonly; 38 | } 39 | if (isShallow) { 40 | return res; 41 | } 42 | if (isObject(res)) { 43 | return isReadonly ? readonly(res) : reactive(res); 44 | } 45 | return res; 46 | }; 47 | } 48 | function createSetter() { 49 | return function (target, key, value, receiver) { 50 | const res = Reflect.set(target, key, value, receiver); 51 | triger(target, key); // 触发订阅 52 | return res; 53 | }; 54 | } 55 | const mutableHandlers = { 56 | get, 57 | set 58 | }; 59 | const readonlyHandlers = { 60 | get: readonlyGet, 61 | set(target, key, value, receiver) { 62 | console.warn(`Set ${key} failed, it's readonly`, target); 63 | return true; 64 | } 65 | }; 66 | extend({}, mutableHandlers, { 67 | get: shallowReactiveGet 68 | }); 69 | const shallowReadonlyHandlers = extend({}, readonlyHandlers, { 70 | get: shallowReadonlyGet 71 | }); 72 | 73 | function reactive(obj) { 74 | return createActiveObject(obj, mutableHandlers); 75 | } 76 | function readonly(obj) { 77 | return createActiveObject(obj, readonlyHandlers); 78 | } 79 | function shallowReadonly(obj) { 80 | return createActiveObject(obj, shallowReadonlyHandlers); 81 | } 82 | function createActiveObject(obj, baseHandlers) { 83 | if (!isObject(obj)) { 84 | return console.warn(`target: ${obj} is not an Object`); 85 | } 86 | return new Proxy(obj, baseHandlers); 87 | } 88 | 89 | function initProps(instance, props) { 90 | instance.props = props || {}; 91 | } 92 | 93 | const publicPropertiesMap = { 94 | $el: (i) => i.$el, 95 | }; 96 | const PublicInstanceProxyHandlers = { 97 | get({ _: instance }, key, receiver) { 98 | const { setupState, props } = instance; 99 | if (hasOwn(setupState, key)) { 100 | return setupState[key]; 101 | } 102 | else if (hasOwn(props, key)) { 103 | return props[key]; 104 | } 105 | if (key in publicPropertiesMap) { 106 | return publicPropertiesMap[key](instance); 107 | } 108 | } 109 | }; 110 | 111 | function createComponentInstance(vnode) { 112 | return { 113 | vnode, 114 | type: vnode.type, 115 | setupState: {}, 116 | $el: null, 117 | props: {} 118 | }; 119 | } 120 | function setupComponent(instance) { 121 | initProps(instance, instance.vnode.props); 122 | // initSlots() 123 | instance.proxy = new Proxy({ _: instance }, PublicInstanceProxyHandlers); 124 | setupStatefulComponent(instance); 125 | } 126 | function setupStatefulComponent(instance) { 127 | const setup = instance.type.setup; 128 | if (setup) { 129 | const setupResult = setup(shallowReadonly(instance.props)); 130 | handleSetupResult(instance, setupResult); 131 | } 132 | } 133 | function handleSetupResult(instance, setupResult) { 134 | if (typeof setupResult === 'object') { 135 | instance.setupState = setupResult; 136 | } 137 | finishComponentSetup(instance); 138 | } 139 | function finishComponentSetup(instance) { 140 | const component = instance.type; 141 | if (component.render) { 142 | instance.render = component.render; 143 | } 144 | } 145 | 146 | function render(vnode, container) { 147 | // ... 148 | patch(vnode, container); 149 | } 150 | function patch(vnode, container) { 151 | if (vnode.shapeFlag & 1 /* ELEMENT */) { 152 | processElement(vnode, container); 153 | } 154 | else if (vnode.shapeFlag & 2 /* STATEFUL_COMPONENT */) { 155 | processComponent(vnode, container); 156 | } 157 | } 158 | function processElement(vnode, container) { 159 | // type 160 | const el = vnode.$el = document.createElement(vnode.type); 161 | // props 162 | const { props } = vnode; 163 | for (let key in props) { 164 | const val = props[key]; 165 | if (isOn(key)) { 166 | const event = key.slice(2).toLocaleLowerCase(); 167 | el.addEventListener(event, val); 168 | } 169 | else { 170 | el.setAttribute(key, val); 171 | } 172 | } 173 | // children 174 | if (vnode.shapeFlag & 4 /* TEXT_CHILDREN */) { 175 | el.innerText = vnode.children; 176 | } 177 | else if (vnode.shapeFlag & 8 /* ARRAY_CHILDREN */) { 178 | mountChildren(vnode, el); 179 | } 180 | // append 181 | container.appendChild(el); 182 | } 183 | function mountChildren(vnode, el) { 184 | for (let i = 0; i < vnode.children.length; i++) { 185 | patch(vnode.children[i], el); 186 | } 187 | } 188 | function processComponent(vnode, container) { 189 | mountComponent(vnode, container); 190 | } 191 | function mountComponent(initinalVNode, container) { 192 | const instance = createComponentInstance(initinalVNode); 193 | setupComponent(instance); 194 | setupRenderEffect(instance, container); 195 | } 196 | function setupRenderEffect(instance, container) { 197 | const subTree = instance.render.call(instance.proxy); 198 | patch(subTree, container); 199 | // 最终是要给rootcomponet实例上添加el 200 | instance.$el = subTree.$el; 201 | } 202 | 203 | function createVNode(type, props, children) { 204 | // type 可能是 string | Component,例如 'div' | MyComponent 205 | const vnode = { 206 | type, 207 | props, 208 | children, 209 | $el: null, 210 | shapeFlag: getShapeFlag(type) 211 | }; 212 | if (typeof children === 'string') { 213 | vnode.shapeFlag |= 4 /* TEXT_CHILDREN */; 214 | } 215 | else if (typeof children === 'object') { 216 | vnode.shapeFlag |= 8 /* ARRAY_CHILDREN */; 217 | } 218 | return vnode; 219 | } 220 | function getShapeFlag(type) { 221 | return typeof type === 'string' ? 1 /* ELEMENT */ : 2 /* STATEFUL_COMPONENT */; 222 | } 223 | 224 | function createApp(rootComponent) { 225 | return { 226 | mount(rootContainer) { 227 | const vnode = createVNode(rootComponent); 228 | render(vnode, rootContainer); 229 | } 230 | }; 231 | } 232 | 233 | function h(type, props, children) { 234 | return createVNode(type, props, children); 235 | } 236 | 237 | export { createApp, h }; 238 | -------------------------------------------------------------------------------- /lib/mini_vue.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | const extend = Object.assign; 6 | const isObject = (val) => { 7 | return val !== null && typeof val === 'object'; 8 | }; 9 | const isOn = (key) => /^on[A-Z]/.test(key); 10 | const hasOwn = (obj, key) => Object.hasOwnProperty.call(obj, key); 11 | 12 | const targetMap = new WeakMap; 13 | function triger(target, key) { 14 | const depsMap = targetMap.get(target); 15 | const depsSet = depsMap.get(key); 16 | trigerEffects(depsSet); 17 | } 18 | function trigerEffects(depsSet) { 19 | for (const effect of depsSet) { 20 | if (effect.scheduler) { 21 | effect.scheduler(); 22 | } 23 | else { 24 | effect.run(); 25 | } 26 | } 27 | } 28 | 29 | const get = createGetter(); 30 | const set = createSetter(); 31 | const readonlyGet = createGetter(true); 32 | const shallowReactiveGet = createGetter(false, true); 33 | const shallowReadonlyGet = createGetter(true, true); 34 | function createGetter(isReadonly = false, isShallow = false) { 35 | return function (target, key, receiver) { 36 | const res = Reflect.get(target, key, receiver); 37 | if (key === "__v_isReactive" /* IS_REACTIVE */) { 38 | return !isReadonly; 39 | } 40 | else if (key === "__v_isReadonly" /* IS_READONLY */) { 41 | return isReadonly; 42 | } 43 | if (isShallow) { 44 | return res; 45 | } 46 | if (isObject(res)) { 47 | return isReadonly ? readonly(res) : reactive(res); 48 | } 49 | return res; 50 | }; 51 | } 52 | function createSetter() { 53 | return function (target, key, value, receiver) { 54 | const res = Reflect.set(target, key, value, receiver); 55 | triger(target, key); // 触发订阅 56 | return res; 57 | }; 58 | } 59 | const mutableHandlers = { 60 | get, 61 | set 62 | }; 63 | const readonlyHandlers = { 64 | get: readonlyGet, 65 | set(target, key, value, receiver) { 66 | console.warn(`Set ${key} failed, it's readonly`, target); 67 | return true; 68 | } 69 | }; 70 | extend({}, mutableHandlers, { 71 | get: shallowReactiveGet 72 | }); 73 | const shallowReadonlyHandlers = extend({}, readonlyHandlers, { 74 | get: shallowReadonlyGet 75 | }); 76 | 77 | function reactive(obj) { 78 | return createActiveObject(obj, mutableHandlers); 79 | } 80 | function readonly(obj) { 81 | return createActiveObject(obj, readonlyHandlers); 82 | } 83 | function shallowReadonly(obj) { 84 | return createActiveObject(obj, shallowReadonlyHandlers); 85 | } 86 | function createActiveObject(obj, baseHandlers) { 87 | if (!isObject(obj)) { 88 | return console.warn(`target: ${obj} is not an Object`); 89 | } 90 | return new Proxy(obj, baseHandlers); 91 | } 92 | 93 | function initProps(instance, props) { 94 | instance.props = props || {}; 95 | } 96 | 97 | const publicPropertiesMap = { 98 | $el: (i) => i.$el, 99 | }; 100 | const PublicInstanceProxyHandlers = { 101 | get({ _: instance }, key, receiver) { 102 | const { setupState, props } = instance; 103 | if (hasOwn(setupState, key)) { 104 | return setupState[key]; 105 | } 106 | else if (hasOwn(props, key)) { 107 | return props[key]; 108 | } 109 | if (key in publicPropertiesMap) { 110 | return publicPropertiesMap[key](instance); 111 | } 112 | } 113 | }; 114 | 115 | function createComponentInstance(vnode) { 116 | return { 117 | vnode, 118 | type: vnode.type, 119 | setupState: {}, 120 | $el: null, 121 | props: {} 122 | }; 123 | } 124 | function setupComponent(instance) { 125 | initProps(instance, instance.vnode.props); 126 | // initSlots() 127 | instance.proxy = new Proxy({ _: instance }, PublicInstanceProxyHandlers); 128 | setupStatefulComponent(instance); 129 | } 130 | function setupStatefulComponent(instance) { 131 | const setup = instance.type.setup; 132 | if (setup) { 133 | const setupResult = setup(shallowReadonly(instance.props)); 134 | handleSetupResult(instance, setupResult); 135 | } 136 | } 137 | function handleSetupResult(instance, setupResult) { 138 | if (typeof setupResult === 'object') { 139 | instance.setupState = setupResult; 140 | } 141 | finishComponentSetup(instance); 142 | } 143 | function finishComponentSetup(instance) { 144 | const component = instance.type; 145 | if (component.render) { 146 | instance.render = component.render; 147 | } 148 | } 149 | 150 | function render(vnode, container) { 151 | // ... 152 | patch(vnode, container); 153 | } 154 | function patch(vnode, container) { 155 | if (vnode.shapeFlag & 1 /* ELEMENT */) { 156 | processElement(vnode, container); 157 | } 158 | else if (vnode.shapeFlag & 2 /* STATEFUL_COMPONENT */) { 159 | processComponent(vnode, container); 160 | } 161 | } 162 | function processElement(vnode, container) { 163 | // type 164 | const el = vnode.$el = document.createElement(vnode.type); 165 | // props 166 | const { props } = vnode; 167 | for (let key in props) { 168 | const val = props[key]; 169 | if (isOn(key)) { 170 | const event = key.slice(2).toLocaleLowerCase(); 171 | el.addEventListener(event, val); 172 | } 173 | else { 174 | el.setAttribute(key, val); 175 | } 176 | } 177 | // children 178 | if (vnode.shapeFlag & 4 /* TEXT_CHILDREN */) { 179 | el.innerText = vnode.children; 180 | } 181 | else if (vnode.shapeFlag & 8 /* ARRAY_CHILDREN */) { 182 | mountChildren(vnode, el); 183 | } 184 | // append 185 | container.appendChild(el); 186 | } 187 | function mountChildren(vnode, el) { 188 | for (let i = 0; i < vnode.children.length; i++) { 189 | patch(vnode.children[i], el); 190 | } 191 | } 192 | function processComponent(vnode, container) { 193 | mountComponent(vnode, container); 194 | } 195 | function mountComponent(initinalVNode, container) { 196 | const instance = createComponentInstance(initinalVNode); 197 | setupComponent(instance); 198 | setupRenderEffect(instance, container); 199 | } 200 | function setupRenderEffect(instance, container) { 201 | const subTree = instance.render.call(instance.proxy); 202 | patch(subTree, container); 203 | // 最终是要给rootcomponet实例上添加el 204 | instance.$el = subTree.$el; 205 | } 206 | 207 | function createVNode(type, props, children) { 208 | // type 可能是 string | Component,例如 'div' | MyComponent 209 | const vnode = { 210 | type, 211 | props, 212 | children, 213 | $el: null, 214 | shapeFlag: getShapeFlag(type) 215 | }; 216 | if (typeof children === 'string') { 217 | vnode.shapeFlag |= 4 /* TEXT_CHILDREN */; 218 | } 219 | else if (typeof children === 'object') { 220 | vnode.shapeFlag |= 8 /* ARRAY_CHILDREN */; 221 | } 222 | return vnode; 223 | } 224 | function getShapeFlag(type) { 225 | return typeof type === 'string' ? 1 /* ELEMENT */ : 2 /* STATEFUL_COMPONENT */; 226 | } 227 | 228 | function createApp(rootComponent) { 229 | return { 230 | mount(rootContainer) { 231 | const vnode = createVNode(rootComponent); 232 | render(vnode, rootContainer); 233 | } 234 | }; 235 | } 236 | 237 | function h(type, props, children) { 238 | return createVNode(type, props, children); 239 | } 240 | 241 | exports.createApp = createApp; 242 | exports.h = h; 243 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | "lib": ["DOM", "ES6"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "esnext", /* Specify what module code is generated. */ 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | "types": ["jest"], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | // "resolveJsonModule": true, /* Enable importing .json files */ 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 46 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | 69 | /* Interop Constraints */ 70 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 75 | 76 | /* Type Checking */ 77 | "strict": true, /* Enable all strict type-checking options. */ 78 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 79 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 80 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 81 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 82 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 83 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 84 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 85 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 86 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 87 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 88 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 89 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 90 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 91 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 92 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 93 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 94 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 95 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 96 | 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 100 | } 101 | } 102 | --------------------------------------------------------------------------------