├── .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 |
--------------------------------------------------------------------------------