From d27ee983fbe97535738577e743eb50ac55c538e0 Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 15 Jul 2022 21:14:22 +0800 Subject: [PATCH 01/74] Match-id-ee12b2c587d5ebf5b2b49cd40df333ed3386118d --- jest.config.js | 3 +- libs/horizon/src/horizonx/adapters/redux.ts | 35 ++- .../src/horizonx/adapters/reduxReact.ts | 37 +-- .../src/horizonx/adapters/reduxThunk.ts | 3 +- .../src/horizonx/proxy/HooklessObserver.ts | 2 +- libs/horizon/src/horizonx/proxy/Observer.ts | 21 +- .../src/horizonx/proxy/ProxyHandler.ts | 2 +- .../src/horizonx/store/StoreHandler.ts | 163 ++++++---- libs/horizon/src/horizonx/types.d.ts | 81 ----- package.json | 6 +- .../StateManager/StateArray.test.js | 201 ------------- .../StateManager/StateArray.test.tsx | 208 +++++++++++++ .../{StateMap.test.js => StateMap.test.tsx} | 152 +++++----- ...eMixType.test.js => StateMixType.test.tsx} | 37 ++- .../{StateSet.test.js => StateSet.test.tsx} | 151 +++++----- ...eWeakMap.test.js => StateWeakMap.test.tsx} | 80 ++--- ...eWeakSet.test.js => StateWeakSet.test.tsx} | 73 +++-- .../{async.test.js => async.test.tsx} | 27 +- .../StoreFunctionality/basicAccess.test.js | 63 ---- .../StoreFunctionality/basicAccess.test.tsx | 110 +++++++ ...arAccess.test.js => dollarAccess.test.tsx} | 20 +- ...otherCases.test.js => otherCases.test.tsx} | 36 +-- .../{reset.test.js => reset.js} | 42 ++- .../StoreFunctionality/{store.js => store.ts} | 0 ...xAdapter.test.js => ReduxAdapter.test.tsx} | 153 +++++----- .../adapters/ReduxAdapterThunk.test.js | 34 --- .../adapters/ReduxAdapterThunk.test.tsx | 33 +++ ...ter.test.js => ReduxReactAdapter.test.tsx} | 278 +++++++----------- ...eption.test.js => ClassException.test.tsx} | 38 +-- .../class/ClassStateArray.test.js | 220 -------------- .../class/ClassStateArray.test.tsx | 231 +++++++++++++++ ...tateMap.test.js => ClassStateMap.test.tsx} | 165 ++++++----- ...Clear.test.js => ClassVNodeClear.test.tsx} | 63 ++-- ...ar.test.js => FunctionVNodeClear.test.tsx} | 65 ++-- .../HorizonXText/edgeCases/proxy.test.js | 21 -- .../HorizonXText/edgeCases/proxy.test.tsx | 48 +++ scripts/__tests__/jest/logUtils.js | 26 ++ scripts/rollup/rollup.config.js | 2 + 38 files changed, 1538 insertions(+), 1392 deletions(-) delete mode 100644 libs/horizon/src/horizonx/types.d.ts delete mode 100644 scripts/__tests__/HorizonXText/StateManager/StateArray.test.js create mode 100644 scripts/__tests__/HorizonXText/StateManager/StateArray.test.tsx rename scripts/__tests__/HorizonXText/StateManager/{StateMap.test.js => StateMap.test.tsx} (60%) rename scripts/__tests__/HorizonXText/StateManager/{StateMixType.test.js => StateMixType.test.tsx} (77%) rename scripts/__tests__/HorizonXText/StateManager/{StateSet.test.js => StateSet.test.tsx} (58%) rename scripts/__tests__/HorizonXText/StateManager/{StateWeakMap.test.js => StateWeakMap.test.tsx} (61%) rename scripts/__tests__/HorizonXText/StateManager/{StateWeakSet.test.js => StateWeakSet.test.tsx} (59%) rename scripts/__tests__/HorizonXText/StoreFunctionality/{async.test.js => async.test.tsx} (80%) delete mode 100644 scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.js create mode 100644 scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx rename scripts/__tests__/HorizonXText/StoreFunctionality/{dollarAccess.test.js => dollarAccess.test.tsx} (64%) rename scripts/__tests__/HorizonXText/StoreFunctionality/{otherCases.test.js => otherCases.test.tsx} (71%) rename scripts/__tests__/HorizonXText/StoreFunctionality/{reset.test.js => reset.js} (65%) rename scripts/__tests__/HorizonXText/StoreFunctionality/{store.js => store.ts} (100%) rename scripts/__tests__/HorizonXText/adapters/{ReduxAdapter.test.js => ReduxAdapter.test.tsx} (59%) delete mode 100644 scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.js create mode 100644 scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.tsx rename scripts/__tests__/HorizonXText/adapters/{ReduxReactAdapter.test.js => ReduxReactAdapter.test.tsx} (50%) rename scripts/__tests__/HorizonXText/class/{ClassException.test.js => ClassException.test.tsx} (54%) delete mode 100644 scripts/__tests__/HorizonXText/class/ClassStateArray.test.js create mode 100644 scripts/__tests__/HorizonXText/class/ClassStateArray.test.tsx rename scripts/__tests__/HorizonXText/class/{ClassStateMap.test.js => ClassStateMap.test.tsx} (58%) rename scripts/__tests__/HorizonXText/clear/{ClassVNodeClear.test.js => ClassVNodeClear.test.tsx} (62%) rename scripts/__tests__/HorizonXText/clear/{FunctionVNodeClear.test.js => FunctionVNodeClear.test.tsx} (59%) delete mode 100644 scripts/__tests__/HorizonXText/edgeCases/proxy.test.js create mode 100644 scripts/__tests__/HorizonXText/edgeCases/proxy.test.tsx create mode 100644 scripts/__tests__/jest/logUtils.js diff --git a/jest.config.js b/jest.config.js index 665708c9..dde828dd 100644 --- a/jest.config.js +++ b/jest.config.js @@ -138,7 +138,8 @@ module.exports = { // The glob patterns Jest uses to detect test files testMatch: [ - '/scripts/__tests__/**/*.test.js' + '/scripts/__tests__/**/*.test.js', + '/scripts/__tests__/**/*.test.tsx' ], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped diff --git a/libs/horizon/src/horizonx/adapters/redux.ts b/libs/horizon/src/horizonx/adapters/redux.ts index 293700b6..fb19cf9a 100644 --- a/libs/horizon/src/horizonx/adapters/redux.ts +++ b/libs/horizon/src/horizonx/adapters/redux.ts @@ -1,14 +1,38 @@ import { createStore as createStoreX } from '../store/StoreHandler'; -import { ReduxStoreHandler, ReduxAction, ReduxMiddleware } from '../types'; +import { ReduxStoreHandler } from '../store/StoreHandler'; export { thunk } from './reduxThunk'; -export { Provider, useSelector, useStore, useDispatch, connect, createSelectorHook, createDispatchHook } from './reduxReact'; +export { + Provider, + useSelector, + useStore, + useDispatch, + connect, + createSelectorHook, + createDispatchHook, +} from './reduxReact'; + +export type ReduxAction = { + type: string; + [key: string]: any; +}; + +export type ReduxMiddleware = ( + store: ReduxStoreHandler, + extraArgument?: any +) => ( + next: (action: ReduxAction) => any +) => ( + action: + | ReduxAction + | ((dispatch: (action: ReduxAction) => void, store: ReduxStoreHandler, extraArgument?: any) => any) +) => ReduxStoreHandler; type Reducer = (state: any, action: ReduxAction) => any; -export function createStore(reducer: Reducer, preloadedState: any, enhancers): ReduxStoreHandler { +export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): ReduxStoreHandler { const store = createStoreX({ id: 'defaultStore', state: { stateWrapper: preloadedState }, @@ -35,7 +59,7 @@ export function createStore(reducer: Reducer, preloadedState: any, enhancers): R const result = { reducer, getState: function() { - return store.$state.stateWrapper; + return store.$s.stateWrapper; }, subscribe: listener => { store.$subscribe(listener); @@ -48,7 +72,7 @@ export function createStore(reducer: Reducer, preloadedState: any, enhancers): R reducer = newReducer; }, _horizonXstore: store, - dispatch: store.$actions.dispatch, + dispatch: store.$a.dispatch, }; enhancers && enhancers(result); @@ -117,7 +141,6 @@ export function compose(middlewares: ReduxMiddleware[]) { }; } - // HorizonX batches updates by default, this function is only for backwards compatibility export function batch(fn: () => void) { fn(); diff --git a/libs/horizon/src/horizonx/adapters/reduxReact.ts b/libs/horizon/src/horizonx/adapters/reduxReact.ts index da99ab52..8e205eb7 100644 --- a/libs/horizon/src/horizonx/adapters/reduxReact.ts +++ b/libs/horizon/src/horizonx/adapters/reduxReact.ts @@ -3,9 +3,10 @@ import { useState, useContext, useEffect, useRef } from '../../renderer/hooks/Ho import { createContext } from '../../renderer/components/context/CreateContext'; import { createElement } from '../../external/JSXElement'; import { BoundActionCreator } from './redux'; -import { ReduxAction, ReduxStoreHandler } from '../types'; +import { ReduxAction } from './redux'; +import { ReduxStoreHandler } from '../store/StoreHandler' -const DefaultContext = createContext(); +const DefaultContext = createContext(null); type Context = typeof DefaultContext; export function Provider({ @@ -27,8 +28,8 @@ export function createStoreHook(context: Context) { }; } -export function createSelectorHook(context: Context): (selector: (any) => any) => any { - const store = createStoreHook(context)(); +export function createSelectorHook(context: Context): (selector?: (any) => any) => any { + const store = (createStoreHook(context)() as unknown) as ReduxStoreHandler; return function(selector = state => state) { const [b, fr] = useState(false); @@ -37,22 +38,18 @@ export function createSelectorHook(context: Context): (selector: (any) => any) = }; useEffect(() => { - const unsubscribe = store.subscribe(listener); - - return () => { - unsubscribe(listener); - }; + return store.subscribe(listener); }); return selector(store.getState()); }; } -export function createDispatchHook(context: Context): BoundActionCreator { - const store = createStoreHook(context)(); +export function createDispatchHook(context: Context): ()=>BoundActionCreator { + const store = (createStoreHook(context)() as unknown) as ReduxStoreHandler; return function() { return action => { - this.dispatch(action); + store.dispatch(action); }; }.bind(store); } @@ -104,26 +101,32 @@ export function connect( } return Component => { - const useStore = createStoreHook(options.context || DefaultContext); + const useStore = createStoreHook(options?.context || DefaultContext); function Wrapper(props) { const [f, forceReload] = useState(true); - const store = useStore(); + const store = (useStore() as unknown) as ReduxStoreHandler; useEffect(() => { const unsubscribe = store.subscribe(() => forceReload(!f)); () => { - unsubscribe(() => forceReload(!f)); + unsubscribe(); }; }); const previous = useRef({ state: {}, - }); + mappedState: {}, + }) as { + current: { + state: {}; + mappedState: {}; + }; + }; let mappedState; - if (options.areStatesEqual) { + if (options?.areStatesEqual) { if (options.areStatesEqual(previous.current.state, store.getState())) { mappedState = previous.current.mappedState; } else { diff --git a/libs/horizon/src/horizonx/adapters/reduxThunk.ts b/libs/horizon/src/horizonx/adapters/reduxThunk.ts index 850f1b1c..28ce23e0 100644 --- a/libs/horizon/src/horizonx/adapters/reduxThunk.ts +++ b/libs/horizon/src/horizonx/adapters/reduxThunk.ts @@ -1,4 +1,5 @@ -import { ReduxStoreHandler, ReduxAction, ReduxMiddleware } from '../types'; +import { ReduxAction, ReduxMiddleware } from './redux'; +import { ReduxStoreHandler } from '../store/StoreHandler'; function createThunkMiddleware(extraArgument?: any): ReduxMiddleware { return (store: ReduxStoreHandler) => (next: (action: ReduxAction) => any) => ( diff --git a/libs/horizon/src/horizonx/proxy/HooklessObserver.ts b/libs/horizon/src/horizonx/proxy/HooklessObserver.ts index 9ea8937f..1de98c5f 100644 --- a/libs/horizon/src/horizonx/proxy/HooklessObserver.ts +++ b/libs/horizon/src/horizonx/proxy/HooklessObserver.ts @@ -1,6 +1,6 @@ // TODO: implement vNode type -import {IObserver} from '../types'; +import {IObserver} from './Observer'; /** * 一个对象(对象、数组、集合)对应一个Observer diff --git a/libs/horizon/src/horizonx/proxy/Observer.ts b/libs/horizon/src/horizonx/proxy/Observer.ts index be24d1c2..a6348841 100644 --- a/libs/horizon/src/horizonx/proxy/Observer.ts +++ b/libs/horizon/src/horizonx/proxy/Observer.ts @@ -6,7 +6,24 @@ import { launchUpdateFromVNode } from '../../renderer/TreeBuilder'; import { getProcessingVNode } from '../../renderer/GlobalVar'; import { VNode } from '../../renderer/vnode/VNode'; -import { IObserver } from '../types'; +export interface IObserver { + + useProp: (key: string) => void; + + addListener: (listener: () => void) => void; + + removeListener: (listener: () => void) => void; + + setProp: (key: string) => void; + + triggerChangeListeners: () => void; + + triggerUpdate: (vNode: any) => void; + + allChange: () => void; + + clearByVNode: (vNode: any) => void; +} export class Observer implements IObserver { @@ -85,7 +102,7 @@ export class Observer implements IObserver { } } - clearByVNode(vNode: Vnode): void { + clearByVNode(vNode: VNode): void { const keys = this.vNodeKeys.get(vNode); if (keys) { keys.forEach((key: any) => { diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index f2051b9d..b3519729 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -4,7 +4,7 @@ import {HooklessObserver} from './HooklessObserver'; import {isArray, isCollection, isObject} from '../CommonUtils'; import {createArrayProxy} from './handlers/ArrayProxyHandler'; import {createCollectionProxy} from './handlers/CollectionProxyHandler'; -import { IObserver } from '../types'; +import { IObserver } from './Observer'; const OBSERVER_KEY = Symbol('_horizonObserver'); diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index 9b746c96..ffc67aa8 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -3,31 +3,88 @@ import { useEffect, useRef } from '../../renderer/hooks/HookExternal'; import { getProcessingVNode } from '../../renderer/GlobalVar'; import { createProxy } from '../proxy/ProxyHandler'; import readonlyProxy from '../proxy/readonlyProxy'; -import { StoreHandler, StoreConfig, UserActions, UserComputedValues, StoreActions, ComputedValues, ActionFunction, Action, QueuedStoreActions } from '../types'; import { Observer } from '../proxy/Observer'; import { FunctionComponent, ClassComponent } from '../Constants'; +import { VNode } from '../../renderer/Types'; -const storeMap = new Map>(); +const storeMap = new Map>(); function isPromise(obj: any): boolean { return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; } -type PlannedAction>={ - action:string, - payload: any[], - resolve: ReturnType -} +type StoreConfig, C extends UserComputedValues> = { + state?: S; + options?: { suppressHooks?: boolean }; + actions?: A; + id?: string; + computed?: C; +}; -export function createStore,C extends UserComputedValues>(config: StoreConfig): () => StoreHandler { +export type ReduxStoreHandler = { + reducer: (state: any, action: { type: string }) => any; + dispatch: (action: { type: string }) => void; + getState: () => any; + subscribe: (listener: () => void) => () => void; + replaceReducer: (reducer: (state: any, action: { type: string }) => any) => void; + _horizonXstore: StoreHandler; +}; + +type StoreHandler, C extends UserComputedValues> = { + $subscribe: (listener: () => void) => void; + $unsubscribe: (listener: () => void) => void; + $s: S; + $config: StoreConfig; + $queue: QueuedStoreActions; + $a: StoreActions; + $c: UserComputedValues; + reduxHandler?: ReduxStoreHandler; +} & { [K in keyof S]: S[K] } & + { [K in keyof A]: Action } & + { [K in keyof C]: ReturnType }; + +type PlannedAction> = { + action: string; + payload: any[]; + resolve: ReturnType; +}; +type RemoveFirstFromTuple = T['length'] extends 0 + ? [] + : ((...b: T) => void) extends (a, ...b: infer I) => void + ? I + : []; + +type UserActions = { [K: string]: ActionFunction }; +type UserComputedValues = { [K: string]: ComputedFunction }; + +type ActionFunction = (this: StoreHandler, state: S, ...args: any[]) => any; +type ComputedFunction = (state: S) => any; +type Action, S extends object> = ( + this: StoreHandler, + ...args: RemoveFirstFromTuple> +) => ReturnType; +type AsyncAction, S extends object> = ( + this: StoreHandler, + ...args: RemoveFirstFromTuple> +) => Promise>; + +type StoreActions> = { [K in keyof A]: Action }; +type QueuedStoreActions> = { [K in keyof A]: AsyncAction }; +type ComputedValues> = { [K in keyof C]: ReturnType }; +type PostponedAction = (state: object, ...args: any[]) => Promise; +type PostponedActions = { [key: string]: PostponedAction }; + +export function createStore, C extends UserComputedValues>( + config: StoreConfig +): () => StoreHandler { //create a local shalow copy to ensure consistency (if user would change the config object after store creation) config = { - id:config.id, + id: config.id, options: config.options, state: config.state, - actions: config.actions ? {...config.actions}:undefined, - computed: config.computed ? {...config.computed}:undefined - } + actions: config.actions ? { ...config.actions } : undefined, + computed: config.computed ? { ...config.computed } : undefined, + }; // 校验 if (Object.prototype.toString.call(config) !== '[object Object]') { @@ -35,30 +92,30 @@ export function createStore,C extends } const proxyObj = createProxy(config.state, !config.options?.suppressHooks); - + proxyObj.$pending = false; - - const $subscribe = (listener) => { + + const $subscribe = listener => { proxyObj.addListener(listener); }; - - const $unsubscribe = (listener) => { + + const $unsubscribe = listener => { proxyObj.removeListener(listener); }; - const plannedActions:PlannedAction>[] = []; - const $actions:Partial>={} - const $queue:Partial> = {}; - const $computed:Partial>={} - const handler = { + const plannedActions: PlannedAction>[] = []; + const $a: Partial> = {}; + const $queue: Partial> = {}; + const $c: Partial> = {}; + const handler = ({ $subscribe, $unsubscribe, - $actions:$actions as StoreActions, - $state:proxyObj, - $computed: $computed as ComputedValues, - $config:config, - $queue: $queue as QueuedStoreActions, - } as StoreHandler; + $a: $a as StoreActions, + $s: proxyObj, + $c: $c as ComputedValues, + $config: config, + $queue: $queue as QueuedStoreActions, + } as unknown) as StoreHandler; function tryNextAction() { if (!plannedActions.length) { @@ -67,7 +124,9 @@ export function createStore,C extends } const nextAction = plannedActions.shift()!; - const result = config.actions ? config.actions[nextAction.action].bind(self, proxyObj)(...nextAction.payload) : undefined; + const result = config.actions + ? config.actions[nextAction.action].bind(handler, proxyObj)(...nextAction.payload) + : undefined; if (isPromise(result)) { result.then(value => { @@ -81,16 +140,16 @@ export function createStore,C extends } // 包装actions - if(config.actions){ + if (config.actions) { Object.keys(config.actions).forEach(action => { ($queue as any)[action] = (...payload) => { - return new Promise((resolve) => { + return new Promise(resolve => { if (!proxyObj.$pending) { proxyObj.$pending = true; - const result = config.actions![action].bind(self, proxyObj)(...payload); - + const result = config.actions![action].bind(handler, proxyObj)(...payload); + if (isPromise(result)) { - result.then((value) => { + result.then(value => { resolve(value); tryNextAction(); }); @@ -102,40 +161,42 @@ export function createStore,C extends plannedActions.push({ action, payload, - resolve + resolve, }); } }); }; - - ($actions as any)[action] = function Wrapped(...payload) { - return config.actions![action].bind(self, proxyObj)(...payload); + + ($a as any)[action] = function Wrapped(...payload) { + return config.actions![action].bind(handler, proxyObj)(...payload); }; - // direct store access + // direct store access Object.defineProperty(handler, action, { writable: false, - value: $actions[action] + value: (...payload) => { + return config.actions![action].bind(handler, proxyObj)(...payload); + }, }); }); } - if (config.computed) { - Object.keys(config.computed).forEach((key) => { - ($computed as any)[key] = config.computed![key].bind(handler, readonlyProxy(proxyObj)); + if (config.computed) { + Object.keys(config.computed).forEach(key => { + ($c as any)[key] = config.computed![key].bind(handler, readonlyProxy(proxyObj)); - // direct store access + // direct store access Object.defineProperty(handler, key, { - get: $computed[key] as ()=>any + get: $c[key] as () => any, }); }); } // direct state access - if(config.state){ + if (config.state) { Object.keys(config.state).forEach(key => { Object.defineProperty(handler, key, { - get: () => proxyObj[key] + get: () => proxyObj[key], }); }); } @@ -172,7 +233,7 @@ function hookStore() { if (processingVNode.tag === FunctionComponent) { // from FunctionComponent - const vNodeRef = useRef(null); + const vNodeRef = (useRef(null) as unknown) as { current: VNode }; vNodeRef.current = processingVNode; useEffect(() => { @@ -211,9 +272,9 @@ export function useStore, C extends U if (storeObj && !storeObj.$config.options?.suppressHooks) hookStore(); - return storeObj as StoreHandler; + return storeObj as StoreHandler; } -export function clearStore(id:string):void { +export function clearStore(id: string): void { storeMap.delete(id); -} \ No newline at end of file +} diff --git a/libs/horizon/src/horizonx/types.d.ts b/libs/horizon/src/horizonx/types.d.ts deleted file mode 100644 index 3fed3afe..00000000 --- a/libs/horizon/src/horizonx/types.d.ts +++ /dev/null @@ -1,81 +0,0 @@ -export interface IObserver { - - useProp: (key: string) => void; - - addListener: (listener: () => void) => void; - - removeListener: (listener: () => void) => void; - - setProp: (key: string) => void; - - triggerChangeListeners: () => void; - - triggerUpdate: (vNode: any) => void; - - allChange: () => void; - - clearByVNode: (vNode: any) => void; -} - -type RemoveFirstFromTuple = - T['length'] extends 0 ? [] : - (((...b: T) => void) extends (a, ...b: infer I) => void ? I : []) - - -type UserActions = { [K:string]: ActionFunction }; -type UserComputedValues = { [K:string]: ComputedFunction }; - -type ActionFunction = (state: S, ...args: any[]) => any; -type ComputedFunction = (state: S) => any; -type Action> = (...args:RemoveFirstFromTuple>)=>ReturnType -type AsyncAction> = (...args:RemoveFirstFromTuple>)=>Promise> - -type StoreActions> = { [K in keyof A]: Action }; -type QueuedStoreActions> = { [K in keyof A]: AsyncAction }; -type ComputedValues> = { [K in keyof C]: ReturnType }; -type PostponedAction = (state: object, ...args: any[]) => Promise; -type PostponedActions = { [key:string]: PostponedAction } - -export type StoreHandler,C extends UserComputedValues> = - {$subscribe: ((listener: () => void) => void), - $unsubscribe: ((listener: () => void) => void), - $state: S, - $config: StoreConfig, - $queue: QueuedStoreActions, - $actions: StoreActions, - $computed: ComputedValues, - reduxHandler?:ReduxStoreHandler} - & - {[K in keyof S]: S[K]} - & - {[K in keyof A]: Action} - & - {[K in keyof C]: ReturnType} - -export type StoreConfig,C extends UserComputedValues> = { - state?: S, - options?:{suppressHooks?: boolean}, - actions?: A, - id?: string, - computed?: C -} - -type ReduxStoreHandler = { - reducer:(state:any,action:{type:string})=>any, - dispatch:(action:{type:string})=>void, - getState:()=>any, - subscribe:(listener:()=>void)=>((listener:()=>void)=>void) - replaceReducer: (reducer: (state:any,action:{type:string})=>any)=>void - _horizonXstore: StoreHandler -} - -type ReduxAction = { - type:string -} - -type ReduxMiddleware = (store:ReduxStoreHandler, extraArgument?:any) => - (next:((action:ReduxAction)=>any)) => - (action:( - ReduxAction| - ((dispatch:(action:ReduxAction)=>void,store:ReduxStoreHandler,extraArgument?:any)=>any) - )) => ReduxStoreHandler \ No newline at end of file diff --git a/package.json b/package.json index c31b27cf..7f37e8cd 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,12 @@ ], "scripts": { "lint": "eslint . --ext .ts", - "build": " rollup --config ./scripts/rollup/rollup.config.js", - "build:watch": " rollup --watch --config ./scripts/rollup/rollup.config.js", + "build": "rollup --config ./scripts/rollup/rollup.config.js", + "build:watch": "rollup --watch --config ./scripts/rollup/rollup.config.js", "build-3rdLib": "node ./scripts/gen3rdLib.js", "build-3rdLib-dev": "npm run build & node ./scripts/gen3rdLib.js --dev", "build-horizon3rdLib-dev": "npm run build & node ./scripts/gen3rdLib.js --dev --type horizon", + "build-types": "tsc -p ./libs/horizon/index.ts --emitDeclarationOnly --declaration --declarationDir ./build/horizon/@types --skipLibCheck", "debug-test": "yarn test --debug", "test": "jest --config=jest.config.js", "watch-test": "yarn test --watch --dev" @@ -92,6 +93,7 @@ "regenerator-runtime": "^0.13.9", "rimraf": "^3.0.0", "rollup": "^2.75.5", + "rollup-plugin-execute": "^1.1.1", "rollup-plugin-terser": "^7.0.2", "typescript": "^3.9.7" }, diff --git a/scripts/__tests__/HorizonXText/StateManager/StateArray.test.js b/scripts/__tests__/HorizonXText/StateManager/StateArray.test.js deleted file mode 100644 index 826d6dda..00000000 --- a/scripts/__tests__/HorizonXText/StateManager/StateArray.test.js +++ /dev/null @@ -1,201 +0,0 @@ -import * as Horizon from '@cloudsop/horizon/index.ts'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; -import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; - -describe('测试store中的Array', () => { - const { unmountComponentAtNode } = Horizon; - let container = null; - beforeEach(() => { - // 创建一个 DOM 元素作为渲染目标 - container = document.createElement('div'); - document.body.appendChild(container); - - const persons = [ - { name: 'p1', age: 1 }, - { name: 'p2', age: 2 }, - ]; - - createStore({ - id: 'user', - state: { - type: 'bing dun dun', - persons: persons, - }, - actions: { - addOnePerson: (state, person) => { - state.persons.push(person); - }, - delOnePerson: state => { - state.persons.pop(); - }, - clearPersons: state => { - state.persons = null; - }, - }, - }); - }); - - afterEach(() => { - // 退出时进行清理 - unmountComponentAtNode(container); - container.remove(); - container = null; - - clearStore('user'); - }); - - const newPerson = { name: 'p3', age: 3 }; - - function Parent(props) { - const userStore = useStore('user'); - const addOnePerson = function() { - userStore.addOnePerson(newPerson); - }; - const delOnePerson = function() { - userStore.delOnePerson(); - }; - return ( -
- - -
{props.children}
-
- ); - } - - it('测试Array方法: push()、pop()', () => { - function Child(props) { - const userStore = useStore('user'); - - return ( -
- -
- ); - } - - Horizon.render(, container); - - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: 2'); - // 在Array中增加一个对象 - Horizon.act(() => { - triggerClickEvent(container, 'addBtn'); - }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: 3'); - - // 在Array中删除一个对象 - Horizon.act(() => { - triggerClickEvent(container, 'delBtn'); - }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: 2'); - }); - - it('测试Array方法: entries()、push()、shift()、unshift、直接赋值', () => { - let globalStore = null; - - function Child(props) { - const userStore = useStore('user'); - globalStore = userStore; - - const nameList = []; - const entries = userStore.$state.persons?.entries(); - if (entries) { - for (const entry of entries) { - nameList.push(entry[1].name); - } - } - - return ( -
- -
- ); - } - - Horizon.render(, container); - - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); - // push - globalStore.$state.persons.push(newPerson); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); - - // shift - globalStore.$state.persons.shift({ name: 'p0', age: 0 }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3'); - - // 赋值[2] - globalStore.$state.persons[2] = { name: 'p4', age: 4 }; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3 p4'); - - // 重新赋值[2] - globalStore.$state.persons[2] = { name: 'p5', age: 5 }; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3 p5'); - - // unshift - globalStore.$state.persons.unshift({ name: 'p1', age: 1 }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3 p5'); - - // 重新赋值 null - globalStore.$state.persons = null; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); - - // 重新赋值 [{ name: 'p1', age: 1 }] - globalStore.$state.persons = [{ name: 'p1', age: 1 }]; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1'); - }); - - it('测试Array方法: forEach()', () => { - let globalStore = null; - - function Child(props) { - const userStore = useStore('user'); - globalStore = userStore; - - const nameList = []; - userStore.$state.persons?.forEach(per => { - nameList.push(per.name); - }); - - return ( -
- -
- ); - } - - Horizon.render(, container); - - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); - // push - globalStore.$state.persons.push(newPerson); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); - - // shift - globalStore.$state.persons.shift({ name: 'p0', age: 0 }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3'); - - // 赋值[2] - globalStore.$state.persons[2] = { name: 'p4', age: 4 }; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3 p4'); - - // 重新赋值[2] - globalStore.$state.persons[2] = { name: 'p5', age: 5 }; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3 p5'); - - // unshift - globalStore.$state.persons.unshift({ name: 'p1', age: 1 }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3 p5'); - - // 重新赋值 null - globalStore.$state.persons = null; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); - - // 重新赋值 [{ name: 'p1', age: 1 }] - globalStore.$state.persons = [{ name: 'p1', age: 1 }]; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1'); - }); -}); diff --git a/scripts/__tests__/HorizonXText/StateManager/StateArray.test.tsx b/scripts/__tests__/HorizonXText/StateManager/StateArray.test.tsx new file mode 100644 index 00000000..fbe1de0b --- /dev/null +++ b/scripts/__tests__/HorizonXText/StateManager/StateArray.test.tsx @@ -0,0 +1,208 @@ +//@ts-ignore +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../../jest/logUtils'; +import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; + +const useUserStore = createStore({ + id: 'user', + state: { + type: 'bing dun dun', + persons: [ + { name: 'p1', age: 1 }, + { name: 'p2', age: 2 }, + ], + }, + actions: { + addOnePerson: (state, person) => { + state.persons.push(person); + }, + delOnePerson: state => { + state.persons.pop(); + }, + clearPersons: state => { + state.persons = []; + }, + reset: state => { + state.persons = [ + { name: 'p1', age: 1 }, + { name: 'p2', age: 2 }, + ]; + }, + }, +}); + +describe('测试store中的Array', () => { + const { unmountComponentAtNode } = Horizon; + let container: HTMLElement | null = null; + beforeEach(() => { + // 创建一个 DOM 元素作为渲染目标 + container = document.createElement('div'); + document.body.appendChild(container); + + useUserStore().reset(); + }); + + afterEach(() => { + // 退出时进行清理 + unmountComponentAtNode(container); + container?.remove(); + container = null; + LogUtils.clear(); + + clearStore('user'); + }); + + const newPerson = { name: 'p3', age: 3 }; + function Parent(props) { + const userStore = useUserStore(); + const addOnePerson = function() { + userStore.addOnePerson(newPerson); + }; + const delOnePerson = function() { + userStore.delOnePerson(); + }; + return ( +
+ + +
{props.children}
+
+ ); + } + + it('测试Array方法: push()、pop()', () => { + function Child(props) { + const userStore = useUserStore(); + + return ( +
+ +
+ ); + } + + Horizon.render(, container); + + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 2'); + // 在Array中增加一个对象 + Horizon.act(() => { + triggerClickEvent(container, 'addBtn'); + }); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 3'); + + // 在Array中删除一个对象 + Horizon.act(() => { + triggerClickEvent(container, 'delBtn'); + }); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 2'); + }); + + it('测试Array方法: entries()、push()、shift()、unshift、直接赋值', () => { + let globalStore = useUserStore(); + function Child(props) { + const userStore = useUserStore(); + + const nameList: string[] = []; + const entries = userStore.$s.persons?.entries(); + if (entries) { + for (const entry of entries) { + nameList.push(entry[1].name); + } + } + + return ( +
+ +
+ ); + } + + Horizon.render(, container); + + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); + // push + globalStore.$s.persons.push(newPerson); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); + + // shift + //@ts-ignore TODO:why is this argument here? + globalStore.$s.persons.shift({ name: 'p0', age: 0 }); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3'); + + // 赋值[2] + globalStore.$s.persons[2] = { name: 'p4', age: 4 }; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3 p4'); + + // 重新赋值[2] + globalStore.$s.persons[2] = { name: 'p5', age: 5 }; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3 p5'); + + // unshift + globalStore.$s.persons.unshift({ name: 'p1', age: 1 }); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3 p5'); + + // 重新赋值 [] + globalStore.$s.persons = []; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); + + // 重新赋值 [{ name: 'p1', age: 1 }] + globalStore.$s.persons = [{ name: 'p1', age: 1 }]; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1'); + }); + + it('测试Array方法: forEach()', () => { + let globalStore = useUserStore(); + function Child(props) { + const userStore = useUserStore(); + + const nameList: string[] = []; + userStore.$s.persons?.forEach(per => { + nameList.push(per.name); + }); + + return ( +
+ +
+ ); + } + + Horizon.render(, container); + + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); + // push + globalStore.$s.persons.push(newPerson); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); + + // shift + //@ts-ignore TODO: why is this argument here? + globalStore.$s.persons.shift({ name: 'p0', age: 0 }); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3'); + + // 赋值[2] + globalStore.$s.persons[2] = { name: 'p4', age: 4 }; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3 p4'); + + // 重新赋值[2] + globalStore.$s.persons[2] = { name: 'p5', age: 5 }; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3 p5'); + + // unshift + globalStore.$s.persons.unshift({ name: 'p1', age: 1 }); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3 p5'); + + // 重新赋值 [] + globalStore.$s.persons = []; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); + + // 重新赋值 [{ name: 'p1', age: 1 }] + globalStore.$s.persons = [{ name: 'p1', age: 1 }]; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1'); + }); +}); diff --git a/scripts/__tests__/HorizonXText/StateManager/StateMap.test.js b/scripts/__tests__/HorizonXText/StateManager/StateMap.test.tsx similarity index 60% rename from scripts/__tests__/HorizonXText/StateManager/StateMap.test.js rename to scripts/__tests__/HorizonXText/StateManager/StateMap.test.tsx index f7eaed9f..e5d1b141 100644 --- a/scripts/__tests__/HorizonXText/StateManager/StateMap.test.js +++ b/scripts/__tests__/HorizonXText/StateManager/StateMap.test.tsx @@ -1,45 +1,55 @@ +//@ts-ignore import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../../jest/logUtils'; import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; + +const useUserStore = createStore({ + id: 'user', + state: { + type: 'bing dun dun', + persons: new Map([ + ['p1', 1], + ['p2', 2], + ]), + }, + actions: { + addOnePerson: (state, person) => { + state.persons.set(person.name, person.age); + }, + delOnePerson: (state, person) => { + state.persons.delete(person.name); + }, + clearPersons: state => { + state.persons.clear(); + }, + reset: state => { + state.persons = new Map([ + ['p1', 1], + ['p2', 2], + ]); + }, + }, +}); describe('测试store中的Map', () => { const { unmountComponentAtNode } = Horizon; - let container = null; + let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 container = document.createElement('div'); document.body.appendChild(container); - const persons = new Map([ - ['p1', 1], - ['p2', 2], - ]); - - createStore({ - id: 'user', - state: { - type: 'bing dun dun', - persons: persons, - }, - actions: { - addOnePerson: (state, person) => { - state.persons.set(person.name, person.age); - }, - delOnePerson: (state, person) => { - state.persons.delete(person.name); - }, - clearPersons: state => { - state.persons.clear(); - }, - }, - }); + useUserStore().reset(); }); afterEach(() => { // 退出时进行清理 unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; + LogUtils.clear(); clearStore('user'); }); @@ -47,7 +57,7 @@ describe('测试store中的Map', () => { const newPerson = { name: 'p3', age: 3 }; function Parent(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); const addOnePerson = function() { userStore.addOnePerson(newPerson); }; @@ -76,43 +86,43 @@ describe('测试store中的Map', () => { it('测试Map方法: set()、delete()、clear()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); return (
- +
); } Horizon.render(, container); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 2'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 3'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 2'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 0'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 0'); }); it('测试Map方法: keys()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); - const nameList = []; - const keys = userStore.$state.persons.keys(); + const nameList: string[] = []; + const keys = userStore.$s.persons.keys(); for (const key of keys) { nameList.push(key); } @@ -126,32 +136,32 @@ describe('测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Map方法: values()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); - const ageList = []; - const values = userStore.$state.persons.values(); + const ageList: number[] = []; + const values = userStore.$s.persons.values(); for (const val of values) { ageList.push(val); } @@ -165,32 +175,32 @@ describe('测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#ageList').innerHTML).toBe('age list: 1 2'); + expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#ageList').innerHTML).toBe('age list: 1 2 3'); + expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2 3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#ageList').innerHTML).toBe('age list: 1 2'); + expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#ageList').innerHTML).toBe('age list: '); + expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: '); }); it('测试Map方法: entries()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); - const nameList = []; - const entries = userStore.$state.persons.entries(); + const nameList: string[] = []; + const entries = userStore.$s.persons.entries(); for (const entry of entries) { nameList.push(entry[0]); } @@ -204,32 +214,32 @@ describe('测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Map方法: forEach()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); - const nameList = []; - userStore.$state.persons.forEach((val, key) => { + const nameList: string[] = []; + userStore.$s.persons.forEach((val, key) => { nameList.push(key); }); @@ -242,53 +252,53 @@ describe('测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Map方法: has()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); return (
- +
); } Horizon.render(, container); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: false'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: true'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); }); it('测试Map方法: for of()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); - const nameList = []; - for (const per of userStore.$state.persons) { + const nameList: string[] = []; + for (const per of userStore.$s.persons) { nameList.push(per[0]); } @@ -301,23 +311,23 @@ describe('测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); }); diff --git a/scripts/__tests__/HorizonXText/StateManager/StateMixType.test.js b/scripts/__tests__/HorizonXText/StateManager/StateMixType.test.tsx similarity index 77% rename from scripts/__tests__/HorizonXText/StateManager/StateMixType.test.js rename to scripts/__tests__/HorizonXText/StateManager/StateMixType.test.tsx index 870d7d26..bb38773d 100644 --- a/scripts/__tests__/HorizonXText/StateManager/StateMixType.test.js +++ b/scripts/__tests__/HorizonXText/StateManager/StateMixType.test.tsx @@ -1,10 +1,12 @@ +//@ts-ignore import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../../jest/logUtils'; import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; describe('测试store中的混合类型变化', () => { const { unmountComponentAtNode } = Horizon; - let container = null; + let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 container = document.createElement('div'); @@ -42,8 +44,9 @@ describe('测试store中的混合类型变化', () => { afterEach(() => { // 退出时进行清理 unmountComponentAtNode(container); - container.remove(); + (container as HTMLElement).remove(); container = null; + LogUtils.clear(); clearStore('user'); }); @@ -68,7 +71,7 @@ describe('测试store中的混合类型变化', () => { function Child(props) { const userStore = useStore('user'); - const days = userStore.$state.persons + const days = userStore.persons .values() .next() .value.love.get('lanqiu').days; @@ -82,11 +85,11 @@ describe('测试store中的混合类型变化', () => { Horizon.render(, container); - expect(container.querySelector('#dayList').innerHTML).toBe('love: 1 3 5'); + expect(container?.querySelector('#dayList')?.innerHTML).toBe('love: 1 3 5'); Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#dayList').innerHTML).toBe('love: 1 3 5 7'); + expect(container?.querySelector('#dayList')?.innerHTML).toBe('love: 1 3 5 7'); }); it('属性是个class实例', () => { @@ -103,7 +106,6 @@ describe('测试store中的混合类型变化', () => { setName(name) { this.name = name; } - getName() { return this.name; } @@ -111,7 +113,6 @@ describe('测试store中的混合类型变化', () => { setAge(age) { this.age = age; } - getAge() { return this.age; } @@ -119,7 +120,6 @@ describe('测试store中的混合类型变化', () => { addLove(lv) { this.loves.add(lv); } - getLoves() { return this.loves; } @@ -127,14 +127,19 @@ describe('测试store中的混合类型变化', () => { let globalPerson; let globalStore; - function Child(props) { const userStore = useStore('user'); globalStore = userStore; - const nameList = []; - const valIterator = userStore.$state.persons.values(); - let per = valIterator.next(); + const nameList: string[] = []; + const valIterator = userStore.persons.values(); + let per = valIterator.next() as { + value: { + name: string; + getName: () => string; + }; + done: boolean; + }; while (!per.done) { nameList.push(per.value.name ?? per.value.getName()); globalPerson = per.value; @@ -150,15 +155,15 @@ describe('测试store中的混合类型变化', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('p1 p2'); // 动态增加一个Person实例 - globalStore.$state.persons.add(new Person('ClassPerson', 5)); + globalStore.$s.persons.add(new Person('ClassPerson', 5)); - expect(container.querySelector('#nameList').innerHTML).toBe('p1 p2 ClassPerson'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('p1 p2 ClassPerson'); globalPerson.setName('ClassPerson1'); - expect(container.querySelector('#nameList').innerHTML).toBe('p1 p2 ClassPerson1'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('p1 p2 ClassPerson1'); }); }); diff --git a/scripts/__tests__/HorizonXText/StateManager/StateSet.test.js b/scripts/__tests__/HorizonXText/StateManager/StateSet.test.tsx similarity index 58% rename from scripts/__tests__/HorizonXText/StateManager/StateSet.test.js rename to scripts/__tests__/HorizonXText/StateManager/StateSet.test.tsx index 4b6bdc7b..a1a245f0 100644 --- a/scripts/__tests__/HorizonXText/StateManager/StateSet.test.js +++ b/scripts/__tests__/HorizonXText/StateManager/StateSet.test.tsx @@ -1,53 +1,62 @@ +//@ts-ignore import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../../jest/logUtils'; import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; + +const useUserStore = createStore({ + id: 'user', + state: { + type: 'bing dun dun', + persons: new Set([ + { name: 'p1', age: 1 }, + { name: 'p2', age: 2 }, + ]), + }, + actions: { + addOnePerson: (state, person) => { + state.persons.add(person); + }, + delOnePerson: (state, person) => { + state.persons.delete(person); + }, + clearPersons: state => { + state.persons.clear(); + }, + reset: state => { + state.persons = new Set([ + { name: 'p1', age: 1 }, + { name: 'p2', age: 2 }, + ]); + }, + }, +}); describe('测试store中的Set', () => { const { unmountComponentAtNode } = Horizon; - let container = null; + let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 container = document.createElement('div'); document.body.appendChild(container); - const persons = new Set([ - { name: 'p1', age: 1 }, - { name: 'p2', age: 2 }, - ]); - - createStore({ - id: 'user', - state: { - type: 'bing dun dun', - persons: persons, - }, - actions: { - addOnePerson: (state, person) => { - state.persons.add(person); - }, - delOnePerson: (state, person) => { - state.persons.delete(person); - }, - clearPersons: state => { - state.persons.clear(); - }, - }, - }); + useUserStore().reset(); }); afterEach(() => { // 退出时进行清理 unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; + LogUtils.clear(); clearStore('user'); }); const newPerson = { name: 'p3', age: 3 }; - function Parent(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); const addOnePerson = function() { userStore.addOnePerson(newPerson); }; @@ -76,17 +85,17 @@ describe('测试store中的Set', () => { it('测试Set方法: add()、delete()、clear()', () => { function Child(props) { - const userStore = useStore('user'); - const personArr = Array.from(userStore.$state.persons); - const nameList = []; - const keys = userStore.$state.persons.keys(); + const userStore = useUserStore(); + const personArr = Array.from(userStore.$s.persons); + const nameList: string[] = []; + const keys = userStore.$s.persons.keys(); for (const key of keys) { nameList.push(key.name); } return (
- +
); @@ -94,35 +103,35 @@ describe('测试store中的Set', () => { Horizon.render(, container); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 2'); - expect(container.querySelector('#lastAge').innerHTML).toBe('last person age: 2'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); + expect(container?.querySelector('#lastAge')?.innerHTML).toBe('last person age: 2'); // 在set中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 3'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 3'); // 在set中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 2'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // clear set Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 0'); - expect(container.querySelector('#lastAge').innerHTML).toBe('last person age: 0'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 0'); + expect(container?.querySelector('#lastAge')?.innerHTML).toBe('last person age: 0'); }); it('测试Set方法: keys()、values()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); - const nameList = []; - const keys = userStore.$state.persons.keys(); - // const keys = userStore.$state.persons.values(); + const nameList: string[] = []; + const keys = userStore.$s.persons.keys(); + // const keys = userStore.$s.persons.values(); for (const key of keys) { nameList.push(key.name); } @@ -136,32 +145,32 @@ describe('测试store中的Set', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在set中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在set中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear set Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Set方法: entries()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); - const nameList = []; - const entries = userStore.$state.persons.entries(); + const nameList: string[] = []; + const entries = userStore.$s.persons.entries(); for (const entry of entries) { nameList.push(entry[0].name); } @@ -175,32 +184,32 @@ describe('测试store中的Set', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在set中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在set中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear set Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Set方法: forEach()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); - const nameList = []; - userStore.$state.persons.forEach(per => { + const nameList: string[] = []; + userStore.$s.persons.forEach(per => { nameList.push(per.name); }); @@ -213,53 +222,53 @@ describe('测试store中的Set', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在set中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在set中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear set Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Set方法: has()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); return (
- +
); } Horizon.render(, container); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: false'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在set中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: true'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); }); it('测试Set方法: for of()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); - const nameList = []; - for (const per of userStore.$state.persons) { + const nameList: string[] = []; + for (const per of userStore.$s.persons) { nameList.push(per.name); } @@ -272,23 +281,23 @@ describe('测试store中的Set', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在set中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在set中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear set Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); }); diff --git a/scripts/__tests__/HorizonXText/StateManager/StateWeakMap.test.js b/scripts/__tests__/HorizonXText/StateManager/StateWeakMap.test.tsx similarity index 61% rename from scripts/__tests__/HorizonXText/StateManager/StateWeakMap.test.js rename to scripts/__tests__/HorizonXText/StateManager/StateWeakMap.test.tsx index 28d4ff98..2d5a441f 100644 --- a/scripts/__tests__/HorizonXText/StateManager/StateWeakMap.test.js +++ b/scripts/__tests__/HorizonXText/StateManager/StateWeakMap.test.tsx @@ -1,45 +1,55 @@ +//@ts-ignore import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../../jest/logUtils'; import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; + +const useUserStore = createStore({ + id: 'user', + state: { + type: 'bing dun dun', + persons: new WeakMap([ + [{ name: 'p1' }, 1], + [{ name: 'p2' }, 2], + ]), + }, + actions: { + addOnePerson: (state, person) => { + state.persons.set(person, 3); + }, + delOnePerson: (state, person) => { + state.persons.delete(person); + }, + clearPersons: state => { + state.persons = new WeakMap([]); + }, + reset: state => { + state.persons = new WeakMap([ + [{ name: 'p1' }, 1], + [{ name: 'p2' }, 2], + ]); + }, + }, +}); describe('测试store中的WeakMap', () => { const { unmountComponentAtNode } = Horizon; - let container = null; + let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 container = document.createElement('div'); document.body.appendChild(container); - const persons = new WeakMap([ - [{ name: 'p1' }, 1], - [{ name: 'p2' }, 2], - ]); - - createStore({ - id: 'user', - state: { - type: 'bing dun dun', - persons: persons, - }, - actions: { - addOnePerson: (state, person) => { - state.persons.set(person, 3); - }, - delOnePerson: (state, person) => { - state.persons.delete(person); - }, - clearPersons: state => { - state.persons.clear(); - }, - }, - }); + useUserStore().reset(); }); afterEach(() => { // 退出时进行清理 unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; + LogUtils.clear(); clearStore('user'); }); @@ -47,7 +57,7 @@ describe('测试store中的WeakMap', () => { const newPerson = { name: 'p3' }; function Parent(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); const addOnePerson = function() { userStore.addOnePerson(newPerson); }; @@ -76,49 +86,49 @@ describe('测试store中的WeakMap', () => { it('测试WeakMap方法: set()、delete()、has()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); return (
- +
); } Horizon.render(, container); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: false'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在WeakMap中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: true'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); // 在WeakMap中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: false'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); }); it('测试WeakMap方法: get()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); return (
- +
); } Horizon.render(, container); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: undefined'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: undefined'); // 在WeakMap中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: 3'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 3'); }); }); diff --git a/scripts/__tests__/HorizonXText/StateManager/StateWeakSet.test.js b/scripts/__tests__/HorizonXText/StateManager/StateWeakSet.test.tsx similarity index 59% rename from scripts/__tests__/HorizonXText/StateManager/StateWeakSet.test.js rename to scripts/__tests__/HorizonXText/StateManager/StateWeakSet.test.tsx index 4c6a8fca..ecbc632d 100644 --- a/scripts/__tests__/HorizonXText/StateManager/StateWeakSet.test.js +++ b/scripts/__tests__/HorizonXText/StateManager/StateWeakSet.test.tsx @@ -1,53 +1,62 @@ +//@ts-ignore import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../../jest/logUtils'; import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; + +const useUserStore = createStore({ + id: 'user', + state: { + type: 'bing dun dun', + persons: new WeakSet([ + { name: 'p1', age: 1 }, + { name: 'p2', age: 2 }, + ]), + }, + actions: { + addOnePerson: (state, person) => { + state.persons.add(person); + }, + delOnePerson: (state, person) => { + state.persons.delete(person); + }, + clearPersons: state => { + state.persons = new WeakSet([]); + }, + reset: state => { + state.persons = new WeakSet([ + { name: 'p1', age: 1 }, + { name: 'p2', age: 2 }, + ]); + }, + }, +}); describe('测试store中的WeakSet', () => { const { unmountComponentAtNode } = Horizon; - let container = null; + let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 container = document.createElement('div'); document.body.appendChild(container); - const persons = new WeakSet([ - { name: 'p1', age: 1 }, - { name: 'p2', age: 2 }, - ]); - - createStore({ - id: 'user', - state: { - type: 'bing dun dun', - persons: persons, - }, - actions: { - addOnePerson: (state, person) => { - state.persons.add(person); - }, - delOnePerson: (state, person) => { - state.persons.delete(person); - }, - clearPersons: state => { - state.persons.clear(); - }, - }, - }); + useUserStore().reset(); }); afterEach(() => { // 退出时进行清理 unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; + LogUtils.clear(); clearStore('user'); }); const newPerson = { name: 'p3', age: 3 }; - function Parent(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); const addOnePerson = function() { userStore.addOnePerson(newPerson); }; @@ -69,28 +78,28 @@ describe('测试store中的WeakSet', () => { it('测试WeakSet方法: add()、delete()、has()', () => { function Child(props) { - const userStore = useStore('user'); + const userStore = useUserStore(); return (
- +
); } Horizon.render(, container); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: false'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在WeakSet中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: true'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); // 在WeakSet中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: false'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); }); }); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/async.test.js b/scripts/__tests__/HorizonXText/StoreFunctionality/async.test.tsx similarity index 80% rename from scripts/__tests__/HorizonXText/StoreFunctionality/async.test.js rename to scripts/__tests__/HorizonXText/StoreFunctionality/async.test.tsx index f45599d2..66c287e9 100644 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/async.test.js +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/async.test.tsx @@ -1,6 +1,8 @@ +//@ts-ignore import * as Horizon from '@cloudsop/horizon/index.ts'; import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; import { triggerClickEvent } from '../../jest/commonComponents'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; const { unmountComponentAtNode } = Horizon; @@ -13,7 +15,7 @@ function postpone(timer, func) { } describe('Asynchronous functions', () => { - let container = null; + let container: HTMLElement | null = null; const COUNTER_ID = 'counter'; const TOGGLE_ID = 'toggle'; @@ -33,7 +35,7 @@ describe('Asynchronous functions', () => { return new Promise(resolve => { setTimeout(() => { state.counter++; - resolve(); + resolve(true); }, 100); }); }, @@ -53,11 +55,12 @@ describe('Asynchronous functions', () => { afterEach(() => { unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; }); it('Should wait for async actions', async () => { + // @ts-ignore jest.useRealTimers(); let globalStore; @@ -84,14 +87,14 @@ describe('Asynchronous functions', () => { Horizon.render(, container); // initial state - expect(document.getElementById(RESULT_ID).innerHTML).toBe('false0'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false0'); // slow toggle has nothing to wait for, it is resolved immediately Horizon.act(() => { triggerClickEvent(container, TOGGLE_ID); }); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('true0'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('true0'); // counter increment is slow. slow toggle waits for result Horizon.act(() => { @@ -101,18 +104,18 @@ describe('Asynchronous functions', () => { triggerClickEvent(container, TOGGLE_ID); }); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('true0'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('true0'); // fast toggle does not wait for counter and it is resolved immediately Horizon.act(() => { triggerClickEvent(container, TOGGLE_FAST_ID); }); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('false0'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false0'); // at 150ms counter increment will be resolved and slow toggle immediately after const t150 = postpone(150, () => { - expect(document.getElementById(RESULT_ID).innerHTML).toBe('true1'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('true1'); }); // before that, two more actions are added to queue - another counter and slow toggle @@ -125,13 +128,14 @@ describe('Asynchronous functions', () => { // at 250ms they should be already resolved const t250 = postpone(250, () => { - expect(document.getElementById(RESULT_ID).innerHTML).toBe('false2'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false2'); }); await Promise.all([t150, t250]); }); it('call async action by then', async () => { + // @ts-ignore jest.useFakeTimers(); let globalStore; @@ -150,12 +154,13 @@ describe('Asynchronous functions', () => { // call async action by then globalStore.$queue.increment().then(() => { - expect(document.getElementById(RESULT_ID).innerHTML).toBe('false1'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false1'); }); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('false0'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false0'); // past 150 ms + // @ts-ignore jest.advanceTimersByTime(150); }); }); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.js b/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.js deleted file mode 100644 index e42feeda..00000000 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.js +++ /dev/null @@ -1,63 +0,0 @@ -import * as Horizon from '@cloudsop/horizon/index.ts'; -import { triggerClickEvent } from '../../jest/commonComponents'; -import { useLogStore } from './store'; - -const { unmountComponentAtNode } = Horizon; - -describe('Basic store manipulation', () => { - let container = null; - - const BUTTON_ID = 'btn'; - const RESULT_ID = 'result'; - - beforeEach(() => { - container = document.createElement('div'); - document.body.appendChild(container); - }); - - afterEach(() => { - unmountComponentAtNode(container); - container.remove(); - container = null; - }); - - it('Should use getters', () => { - function App() { - const logStore = useLogStore(); - - return
{logStore.length}
; - } - - Horizon.render(, container); - - expect(document.getElementById(RESULT_ID).innerHTML).toBe('1'); - }); - - it('Should use actions and update components', () => { - function App() { - const logStore = useLogStore(); - - return ( -
- -

{logStore.length}

-
- ); - } - - Horizon.render(, container); - - Horizon.act(() => { - triggerClickEvent(container, BUTTON_ID); - }); - - expect(document.getElementById(RESULT_ID).innerHTML).toBe('2'); - }); -}); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx b/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx new file mode 100644 index 00000000..0a2c8584 --- /dev/null +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx @@ -0,0 +1,110 @@ +//@ts-ignore +import Horizon from '@cloudsop/horizon/index.ts'; +import { triggerClickEvent } from '../../jest/commonComponents'; +import { useLogStore } from './store'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; +import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; + +const { unmountComponentAtNode } = Horizon; + +describe('Basic store manipulation', () => { + let container: HTMLElement | null = null; + + const BUTTON_ID = 'btn'; + const RESULT_ID = 'result'; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + }); + + afterEach(() => { + unmountComponentAtNode(container); + container?.remove(); + container = null; + }); + + it('Should use getters', () => { + function App() { + const logStore = useLogStore(); + + return
{logStore.length}
; + } + + Horizon.render(, container); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1'); + }); + + it('Should use actions and update components', () => { + function App() { + const logStore = useLogStore(); + + return ( +
+ +

{logStore.length}

+
+ ); + } + + Horizon.render(, container); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + }); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2'); + }); + + it('should call actions from own actions', () => { + const useIncrementStore = createStore({ + id: 'incrementStore', + state: { + count: 2, + }, + actions: { + increment: state => { + state.count++; + }, + doublePlusOne: function(state) { + state.count = state.count * 2; + this.increment(); + }, + }, + }); + + function App() { + const incrementStore = useIncrementStore(); + + return ( +
+ +

{incrementStore.count}

+
+ ); + } + + Horizon.render(, container); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + }); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('5'); + }); +}); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/dollarAccess.test.js b/scripts/__tests__/HorizonXText/StoreFunctionality/dollarAccess.test.tsx similarity index 64% rename from scripts/__tests__/HorizonXText/StoreFunctionality/dollarAccess.test.js rename to scripts/__tests__/HorizonXText/StoreFunctionality/dollarAccess.test.tsx index 032234d0..94d149f0 100644 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/dollarAccess.test.js +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/dollarAccess.test.tsx @@ -1,11 +1,13 @@ +//@ts-ignore import * as Horizon from '@cloudsop/horizon/index.ts'; import { triggerClickEvent } from '../../jest/commonComponents'; import { useLogStore } from './store'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; const { unmountComponentAtNode } = Horizon; describe('Dollar store access', () => { - let container = null; + let container: HTMLElement | null = null; const BUTTON_ID = 'btn'; const RESULT_ID = 'result'; @@ -17,23 +19,23 @@ describe('Dollar store access', () => { afterEach(() => { unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; }); - it('Should use $state and $computed', () => { + it('Should use $s and $c', () => { function App() { const logStore = useLogStore(); - return
{logStore.$computed.length()}
; + return
{logStore.$c.length()}
; } Horizon.render(, container); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('1'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1'); }); - it('Should use $actions and update components', () => { + it('Should use $a and update components', () => { function App() { const logStore = useLogStore(); @@ -42,12 +44,12 @@ describe('Dollar store access', () => { -

{logStore.$computed.length()}

+

{logStore.$c.length()}

); } @@ -58,6 +60,6 @@ describe('Dollar store access', () => { triggerClickEvent(container, BUTTON_ID); }); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('2'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2'); }); }); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/otherCases.test.js b/scripts/__tests__/HorizonXText/StoreFunctionality/otherCases.test.tsx similarity index 71% rename from scripts/__tests__/HorizonXText/StoreFunctionality/otherCases.test.js rename to scripts/__tests__/HorizonXText/StoreFunctionality/otherCases.test.tsx index 115164c0..ad6a4028 100644 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/otherCases.test.js +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/otherCases.test.tsx @@ -1,11 +1,13 @@ +//@ts-ignore import * as Horizon from '@cloudsop/horizon/index.ts'; import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; import { triggerClickEvent } from '../../jest/commonComponents'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; const { unmountComponentAtNode } = Horizon; describe('Self referencing', () => { - let container = null; + let container: HTMLElement | null = null; const BUTTON_ID = 'btn'; const RESULT_ID = 'result'; @@ -15,7 +17,7 @@ describe('Self referencing', () => { val: 2, }, actions: { - magic: function(state) { + increaseVal: function(state) { state.val = state.val * 2 - 1; }, }, @@ -34,7 +36,7 @@ describe('Self referencing', () => { afterEach(() => { unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; }); @@ -45,8 +47,8 @@ describe('Self referencing', () => { return (

{store.double}

-
); @@ -54,29 +56,29 @@ describe('Self referencing', () => { Horizon.render(, container); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('4'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('4'); Horizon.act(() => { triggerClickEvent(container, BUTTON_ID); }); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('6'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('6'); Horizon.act(() => { triggerClickEvent(container, BUTTON_ID); }); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('10'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('10'); }); it('should access other stores', () => { const useOtherStore = createStore({ state: {}, actions: { - doMagic: () => useSelfRefStore().magic(), + doIncreaseVal: () => useSelfRefStore().increaseVal(), }, computed: { - magicConstant: () => useSelfRefStore().value, + selfRefStoreValue: () => useSelfRefStore().value, }, }); @@ -85,9 +87,9 @@ describe('Self referencing', () => { return (
-

{store.magicConstant}

-
); @@ -95,13 +97,13 @@ describe('Self referencing', () => { Horizon.render(, container); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('5'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('5'); Horizon.act(() => { triggerClickEvent(container, BUTTON_ID); }); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('9'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9'); }); it('should use parametric getters', () => { @@ -138,11 +140,11 @@ describe('Self referencing', () => { } Horizon.render(, container); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('abc'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('abc'); Horizon.act(() => { triggerClickEvent(container, BUTTON_ID); }); - expect(document.getElementById(RESULT_ID).innerHTML).toBe('def'); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('def'); }); }); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/reset.test.js b/scripts/__tests__/HorizonXText/StoreFunctionality/reset.js similarity index 65% rename from scripts/__tests__/HorizonXText/StoreFunctionality/reset.test.js rename to scripts/__tests__/HorizonXText/StoreFunctionality/reset.js index f0b349da..0cc4a4d3 100644 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/reset.test.js +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/reset.js @@ -1,14 +1,14 @@ import * as Horizon from '@cloudsop/horizon/index.ts'; -import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; -import { triggerClickEvent } from '../../jest/commonComponents'; +import {createStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {triggerClickEvent} from '../../jest/commonComponents'; -const { unmountComponentAtNode } = Horizon; +const {unmountComponentAtNode} = Horizon; describe('Reset', () => { it('RESET NOT IMPLEMENTED', async () => { // console.log('reset functionality is not yet implemented') expect(true).toBe(true); - }); + }) return; let container = null; @@ -19,14 +19,14 @@ describe('Reset', () => { const useCounter = createStore({ state: { - counter: 0, + counter: 0 }, actions: { - increment: function(state) { + increment: function (state) { state.counter++; - }, + } }, - computed: {}, + computed: {} }); beforeEach(() => { @@ -44,25 +44,17 @@ describe('Reset', () => { function App() { const store = useCounter(); - return ( -
-

{store.$state.counter}

- - -
- ); + return
+

{store.$s.counter}

+ + +
} - Horizon.render(, container); + Horizon.render(, container); Horizon.act(() => { triggerClickEvent(container, BUTTON_ID); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/store.js b/scripts/__tests__/HorizonXText/StoreFunctionality/store.ts similarity index 100% rename from scripts/__tests__/HorizonXText/StoreFunctionality/store.js rename to scripts/__tests__/HorizonXText/StoreFunctionality/store.ts diff --git a/scripts/__tests__/HorizonXText/adapters/ReduxAdapter.test.js b/scripts/__tests__/HorizonXText/adapters/ReduxAdapter.test.tsx similarity index 59% rename from scripts/__tests__/HorizonXText/adapters/ReduxAdapter.test.js rename to scripts/__tests__/HorizonXText/adapters/ReduxAdapter.test.tsx index 2fa93598..e14c5d9e 100644 --- a/scripts/__tests__/HorizonXText/adapters/ReduxAdapter.test.js +++ b/scripts/__tests__/HorizonXText/adapters/ReduxAdapter.test.tsx @@ -1,9 +1,12 @@ +//@ts-ignore +import * as Horizon from '@cloudsop/horizon/index.ts'; import { createStore, applyMiddleware, combineReducers, - bindActionCreators + bindActionCreators, } from '../../../../libs/horizon/src/horizonx/adapters/redux'; +import { describe, it, expect } from '@jest/globals'; describe('Redux adapter', () => { it('should use getState()', async () => { @@ -12,21 +15,21 @@ describe('Redux adapter', () => { }, 0); expect(reduxStore.getState()).toBe(0); - }) + }); it('Should use default state, dispatch action and update state', async () => { const reduxStore = createStore((state, action) => { switch (action.type) { - case('ADD'): - return {counter: state.counter + 1} + case 'ADD': + return { counter: state.counter + 1 }; default: - return {counter: 0}; + return { counter: 0 }; } }); expect(reduxStore.getState().counter).toBe(0); - reduxStore.dispatch({type: 'ADD'}); + reduxStore.dispatch({ type: 'ADD' }); expect(reduxStore.getState().counter).toBe(1); }); @@ -35,37 +38,37 @@ describe('Redux adapter', () => { let counter = 0; const reduxStore = createStore((state = 0, action) => { switch (action.type) { - case('ADD'): - return state + 1 + case 'ADD': + return state + 1; default: return state; } }); - reduxStore.dispatch({type: 'ADD'}); + reduxStore.dispatch({ type: 'ADD' }); expect(counter).toBe(0); expect(reduxStore.getState()).toBe(1); const unsubscribe = reduxStore.subscribe(() => { counter++; }); - reduxStore.dispatch({type: 'ADD'}); - reduxStore.dispatch({type: 'ADD'}); + reduxStore.dispatch({ type: 'ADD' }); + reduxStore.dispatch({ type: 'ADD' }); expect(counter).toBe(2); expect(reduxStore.getState()).toBe(3); unsubscribe(); - reduxStore.dispatch({type: 'ADD'}); - reduxStore.dispatch({type: 'ADD'}); + reduxStore.dispatch({ type: 'ADD' }); + reduxStore.dispatch({ type: 'ADD' }); expect(counter).toBe(2); expect(reduxStore.getState()).toBe(5); }); it('Should bind action creators', async () => { - const addTodo = (text) => { + const addTodo = text => { return { type: 'ADD_TODO', - text - } - } + text, + }; + }; const reduxStore = createStore((state = [], action) => { if (action.type === 'ADD_TODO') { @@ -74,7 +77,7 @@ describe('Redux adapter', () => { return state; }); - const actions = bindActionCreators({addTodo}, reduxStore.dispatch); + const actions = bindActionCreators({ addTodo }, reduxStore.dispatch); actions.addTodo('todo'); @@ -84,57 +87,57 @@ describe('Redux adapter', () => { it('Should replace reducer', async () => { const reduxStore = createStore((state, action) => { switch (action.type) { - case('ADD'): - return {counter: state.counter + 1} + case 'ADD': + return { counter: state.counter + 1 }; default: - return {counter: 0}; + return { counter: 0 }; } }); - reduxStore.dispatch({type: 'ADD'}); + reduxStore.dispatch({ type: 'ADD' }); expect(reduxStore.getState().counter).toBe(1); reduxStore.replaceReducer((state, action) => { switch (action.type) { - case('SUB'): - return {counter: state.counter - 1} + case 'SUB': + return { counter: state.counter - 1 }; default: - return {counter: 0}; + return { counter: 0 }; } }); - reduxStore.dispatch({type: 'SUB'}); + reduxStore.dispatch({ type: 'SUB' }); expect(reduxStore.getState().counter).toBe(0); - }) + }); it('Should combine reducers', async () => { const booleanReducer = (state = false, action) => { switch (action.type) { - case('TOGGLE'): - return !state - default: - return state; - } - } - - const addReducer = (state = 0, action) => { - switch (action.type) { - case('ADD'): - return state + 1 + case 'TOGGLE': + return !state; default: return state; } }; - const reduxStore = createStore(combineReducers({check: booleanReducer, counter: addReducer})); + const addReducer = (state = 0, action) => { + switch (action.type) { + case 'ADD': + return state + 1; + default: + return state; + } + }; + + const reduxStore = createStore(combineReducers({ check: booleanReducer, counter: addReducer })); expect(reduxStore.getState().counter).toBe(0); expect(reduxStore.getState().check).toBe(false); - reduxStore.dispatch({type: 'ADD'}); - reduxStore.dispatch({type: 'TOGGLE'}); + reduxStore.dispatch({ type: 'ADD' }); + reduxStore.dispatch({ type: 'TOGGLE' }); expect(reduxStore.getState().counter).toBe(1); expect(reduxStore.getState().check).toBe(true); @@ -142,28 +145,32 @@ describe('Redux adapter', () => { it('Should apply enhancers', async () => { let counter = 0; - let middlewareCallList = []; + let middlewareCallList: string[] = []; const callCounter = store => next => action => { middlewareCallList.push('callCounter'); counter++; let result = next(action); return result; - } + }; - const reduxStore = createStore((state, action) => { - switch (action.type) { - case('toggle'): - return { - check: !state.check - } - default: - return state; - } - }, {check: false}, applyMiddleware(callCounter)); + const reduxStore = createStore( + (state, action) => { + switch (action.type) { + case 'toggle': + return { + check: !state.check, + }; + default: + return state; + } + }, + { check: false }, + applyMiddleware(callCounter) + ); - reduxStore.dispatch({type: 'toggle'}); - reduxStore.dispatch({type: 'toggle'}); + reduxStore.dispatch({ type: 'toggle' }); + reduxStore.dispatch({ type: 'toggle' }); expect(counter).toBe(3); // NOTE: first action is always store initialization }); @@ -171,38 +178,42 @@ describe('Redux adapter', () => { it('Should apply multiple enhancers', async () => { let counter = 0; let lastAction = ''; - let middlewareCallList = []; + let middlewareCallList: string[] = []; const callCounter = store => next => action => { middlewareCallList.push('callCounter'); counter++; let result = next(action); return result; - } + }; const lastFunctionStorage = store => next => action => { middlewareCallList.push('lastFunctionStorage'); lastAction = action.type; let result = next(action); return result; - } + }; - const reduxStore = createStore((state, action) => { - switch (action.type) { - case('toggle'): - return { - check: !state.check - } - default: - return state; - } - }, {check: false}, applyMiddleware(callCounter, lastFunctionStorage)); + const reduxStore = createStore( + (state, action) => { + switch (action.type) { + case 'toggle': + return { + check: !state.check, + }; + default: + return state; + } + }, + { check: false }, + applyMiddleware(callCounter, lastFunctionStorage) + ); - reduxStore.dispatch({type: 'toggle'}); + reduxStore.dispatch({ type: 'toggle' }); expect(counter).toBe(2); // NOTE: first action is always store initialization expect(lastAction).toBe('toggle'); - expect(middlewareCallList[0]).toBe("callCounter"); - expect(middlewareCallList[1]).toBe("lastFunctionStorage"); + expect(middlewareCallList[0]).toBe('callCounter'); + expect(middlewareCallList[1]).toBe('lastFunctionStorage'); }); }); diff --git a/scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.js b/scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.js deleted file mode 100644 index 3785b92c..00000000 --- a/scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import { createStore, applyMiddleware, thunk } from '../../../../libs/horizon/src/horizonx/adapters/redux'; - -describe('Redux thunk', () => { - it('should use apply thunk middleware', async () => { - const MAX_TODOS = 5; - - function addTodosIfAllowed(todoText) { - return (dispatch, getState) => { - const state = getState(); - - if (state.todos.length < MAX_TODOS) { - dispatch({ type: 'ADD_TODO', text: todoText }); - } - }; - } - - const todoStore = createStore( - (state = { todos: [] }, action) => { - if (action.type === 'ADD_TODO') { - return { todos: state.todos?.concat(action.text) }; - } - return state; - }, - null, - applyMiddleware(thunk) - ); - - for (let i = 0; i < 10; i++) { - todoStore.dispatch(addTodosIfAllowed('todo no.' + i)); - } - - expect(todoStore.getState().todos.length).toBe(5); - }); -}); diff --git a/scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.tsx b/scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.tsx new file mode 100644 index 00000000..39e4fc83 --- /dev/null +++ b/scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.tsx @@ -0,0 +1,33 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import { createStore, applyMiddleware, thunk } from '../../../../libs/horizon/src/horizonx/adapters/redux'; +import {describe, it, expect} from '@jest/globals'; + +describe('Redux thunk', () => { + it('should use apply thunk middleware', async () => { + const MAX_TODOS = 5; + + function addTodosIfAllowed(todoText) { + return (dispatch, getState) => { + const state = getState(); + + if (state.todos.length < MAX_TODOS) { + dispatch({type: 'ADD_TODO', text: todoText}); + } + } + } + + const todoStore = createStore((state = {todos: []}, action) => { + if (action.type === 'ADD_TODO') { + return {todos: state.todos?.concat(action.text)}; + } + return state; + }, null, applyMiddleware(thunk)); + + for (let i = 0; i < 10; i++) { + //TODO: resolve thunk problems + (todoStore.dispatch as unknown as (delayedAction:(dispatch,getState)=>void)=>void)(addTodosIfAllowed('todo no.' + i)); + } + + expect(todoStore.getState().todos.length).toBe(5); + }); +}); diff --git a/scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.js b/scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.tsx similarity index 50% rename from scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.js rename to scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.tsx index 5977671a..4f6feec6 100644 --- a/scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.js +++ b/scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.tsx @@ -1,3 +1,4 @@ +//@ts-ignore import horizon, * as Horizon from '@cloudsop/horizon/index.ts'; import { batch, @@ -8,17 +9,19 @@ import { useSelector, useStore, createSelectorHook, - createDispatchHook, + createDispatchHook } from '../../../../libs/horizon/src/horizonx/adapters/redux'; -import { triggerClickEvent } from '../../jest/commonComponents'; +import {triggerClickEvent} from '../../jest/commonComponents'; +import {describe, it, beforeEach, afterEach, expect} from '@jest/globals'; +import { ReduxStoreHandler } from '@cloudsop/horizon/src/horizonx/types'; const BUTTON = 'button'; const BUTTON2 = 'button2'; const RESULT = 'result'; -const CONTAINER = 'container'; +const CONTAINER = 'container' -function getE(id) { - return document.getElementById(id); +function getE(id):HTMLElement { + return document.getElementById(id)||document.body; } describe('Redux/React binding adapter', () => { @@ -36,19 +39,17 @@ describe('Redux/React binding adapter', () => { const reduxStore = createStore((state = 'state', action) => state); const Child = () => { - const store = useStore(); + const store = useStore() as unknown as ReduxStoreHandler; return
{store.getState()}
; }; const Wrapper = () => { - return ( - - - - ); + return + + ; }; - Horizon.render(, getE(CONTAINER)); + Horizon.render(, getE(CONTAINER)); expect(getE(RESULT).innerHTML).toBe('state'); }); @@ -60,30 +61,23 @@ describe('Redux/React binding adapter', () => { }); const Child = () => { - const store = useStore(); + const store = useStore() as unknown as ReduxStoreHandler; const dispatch = useDispatch(); - return ( -
-

{store.getState()}

- -
- ); + return
+

{store.getState()}

+ +
; }; const Wrapper = () => { - return ( - - - - ); + return + + ; }; - Horizon.render(, getE(CONTAINER)); + Horizon.render(, getE(CONTAINER)); expect(reduxStore.getState()).toBe(0); @@ -101,32 +95,24 @@ describe('Redux/React binding adapter', () => { }); const Child = () => { - const count = useSelector(state => state); + const count = useSelector((state) => state); const dispatch = useDispatch(); - return ( -
-

{count}

- -
- ); + return
+

{count}

+ +
; }; const Wrapper = () => { - return ( - - - - ); + return + + ; }; - Horizon.render(, getE(CONTAINER)); + Horizon.render(, getE(CONTAINER)); expect(getE(RESULT).innerHTML).toBe('0'); @@ -139,80 +125,58 @@ describe('Redux/React binding adapter', () => { }); it('Should use connect', async () => { - const reduxStore = createStore( - (state, action) => { - switch (action.type) { - case 'INCREMENT': - return { - ...state, - value: state.negative ? state.value - action.amount : state.value + action.amount, - }; - case 'TOGGLE': - return { - ...state, - negative: !state.negative, - }; - default: - return state; - } - }, - { negative: false, value: 0 } - ); + const reduxStore = createStore((state, action) => { + switch (action.type) { + case('INCREMENT'): + return { + ...state, + value: state.negative ? state.value - action.amount : state.value + action.amount + }; + case('TOGGLE'): + return { + ...state, + negative: !state.negative + }; + default: + return state; + } + }, {negative: false, value: 0}); - const Child = connect( - (state, ownProps) => { - // map state to props - return { ...state, ...ownProps }; - }, - (dispatch, ownProps) => { - // map dispatch to props - return { - increment: () => dispatch({ type: 'INCREMENT', amount: ownProps.amount }), - }; - }, - (stateProps, dispatchProps, ownProps) => { - //merge props - return { stateProps, dispatchProps, ownProps }; - }, - {} - )(props => { + const Child = connect((state, ownProps) => { + // map state to props + return {...state, ...ownProps}; + }, (dispatch, ownProps) => { + // map dispatch to props + return { + // @ts-ignore + increment: () => dispatch({type: 'INCREMENT', amount: ownProps?.amount}) + }; + }, (stateProps, dispatchProps, ownProps) => { + //merge props + return {stateProps, dispatchProps, ownProps}; + }, {})((props) => { const n = props.stateProps.negative; - return ( -
-
- {n ? '-' : '+'} - {props.stateProps.value} -
- -
- ); + return
+
{n ? '-' : '+'}{props.stateProps.value}
+ +
; }); const Wrapper = () => { + //@ts-ignore const [amount, setAmount] = Horizon.useState(5); - return ( - - - - - ); + return + + + ; }; - Horizon.render(, getE(CONTAINER)); + Horizon.render(, getE(CONTAINER)); expect(getE(RESULT).innerHTML).toBe('+0'); @@ -231,7 +195,7 @@ describe('Redux/React binding adapter', () => { }); expect(getE(RESULT).innerHTML).toBe('+8'); - }); + }) it('Should batch dispatches', async () => { const reduxStore = createStore((state = 0, action) => { @@ -244,32 +208,22 @@ describe('Redux/React binding adapter', () => { function Counter() { renderCounter++; - const value = useSelector(state => state); + const value = useSelector((state) => state); const dispatch = useDispatch(); - return ( -
-

{value}

- -
- ); + return
+

{value}

+ +
; } - Horizon.render( - - - , - getE(CONTAINER) - ); + Horizon.render(, getE(CONTAINER)); expect(getE(RESULT).innerHTML).toBe('0'); expect(renderCounter).toBe(1); @@ -300,49 +254,33 @@ describe('Redux/React binding adapter', () => { const count = createSelectorHook(counterContext)(); const dispatch = createDispatchHook(counterContext)(); - return ( - - ); + return ; } function Toggle() { const check = createSelectorHook(toggleContext)(); const dispatch = createDispatchHook(toggleContext)(); - return ( - - ); + return ; } function Wrapper() { - return ( -
- - - + return
+ + + - - - -
- ); + + + +
; } - Horizon.render(, getE(CONTAINER)); + Horizon.render(, getE(CONTAINER)); expect(getE(BUTTON).innerHTML).toBe('0'); expect(getE(BUTTON2).innerHTML).toBe('false'); diff --git a/scripts/__tests__/HorizonXText/class/ClassException.test.js b/scripts/__tests__/HorizonXText/class/ClassException.test.tsx similarity index 54% rename from scripts/__tests__/HorizonXText/class/ClassException.test.js rename to scripts/__tests__/HorizonXText/class/ClassException.test.tsx index 64cea5aa..7933d376 100644 --- a/scripts/__tests__/HorizonXText/class/ClassException.test.js +++ b/scripts/__tests__/HorizonXText/class/ClassException.test.tsx @@ -1,14 +1,17 @@ import * as Horizon from '@cloudsop/horizon/index.ts'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; -import { Text } from '../../jest/commonComponents'; +import * as LogUtils from '../../jest/logUtils'; +import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {Text, triggerClickEvent} from '../../jest/commonComponents'; +import {getObserver} from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; +import {describe, beforeEach, afterEach, it, expect} from '@jest/globals'; describe('测试 Class VNode 清除时,对引用清除', () => { - const { unmountComponentAtNode } = Horizon; - let container = null; + const {unmountComponentAtNode} = Horizon; + let container:HTMLElement|null = null; let globalState = { name: 'bing dun dun', isWin: true, - isShow: true, + isShow: true }; beforeEach(() => { @@ -23,7 +26,7 @@ describe('测试 Class VNode 清除时,对引用清除', () => { setWin: (state, val) => { state.isWin = val; }, - hide: state => { + hide: (state) => { state.isShow = false; }, updateName: (state, val) => { @@ -36,8 +39,9 @@ describe('测试 Class VNode 清除时,对引用清除', () => { afterEach(() => { // 退出时进行清理 unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; + LogUtils.clear(); clearStore('user'); }); @@ -47,23 +51,21 @@ describe('测试 Class VNode 清除时,对引用清除', () => { userStore = useStore('user'); render() { + if(!this.userStore) return
; // Do not modify the store data in the render method. Otherwise, an infinite loop may occur. this.userStore.updateName(this.userStore.name === 'bing dun dun' ? 'huo dun dun' : 'bing dun dun'); - return ( -
- - -
- ); + return
+ + +
; } } expect(() => { - Horizon.render(, container); - }).toThrow( - 'The number of updates exceeds the upper limit 50.\n' + - ' A component maybe repeatedly invokes setState on componentWillUpdate or componentDidUpdate.' - ); + Horizon.render(, container); + }).toThrow('The number of updates exceeds the upper limit 50.\n' + + ' A component maybe repeatedly invokes setState on componentWillUpdate or componentDidUpdate.'); + }); }); diff --git a/scripts/__tests__/HorizonXText/class/ClassStateArray.test.js b/scripts/__tests__/HorizonXText/class/ClassStateArray.test.js deleted file mode 100644 index c61aed9c..00000000 --- a/scripts/__tests__/HorizonXText/class/ClassStateArray.test.js +++ /dev/null @@ -1,220 +0,0 @@ -import * as Horizon from '@cloudsop/horizon/index.ts'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; -import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; - -describe('在Class组件中,测试store中的Array', () => { - const { unmountComponentAtNode } = Horizon; - let container = null; - beforeEach(() => { - // 创建一个 DOM 元素作为渲染目标 - container = document.createElement('div'); - document.body.appendChild(container); - - const persons = [ - { name: 'p1', age: 1 }, - { name: 'p2', age: 2 }, - ]; - - createStore({ - id: 'user', - state: { - type: 'bing dun dun', - persons: persons, - }, - actions: { - addOnePerson: (state, person) => { - state.persons.push(person); - }, - delOnePerson: state => { - state.persons.pop(); - }, - clearPersons: state => { - state.persons = null; - }, - }, - }); - }); - - afterEach(() => { - // 退出时进行清理 - unmountComponentAtNode(container); - container.remove(); - container = null; - - clearStore('user'); - }); - - const newPerson = { name: 'p3', age: 3 }; - - class Parent extends Horizon.Component { - userStore = useStore('user'); - - addOnePerson = () => { - this.userStore.addOnePerson(newPerson); - }; - - delOnePerson = () => { - this.userStore.delOnePerson(); - }; - - render() { - return ( -
- - -
{this.props.children}
-
- ); - } - } - - it('测试Array方法: push()、pop()', () => { - class Child extends Horizon.Component { - userStore = useStore('user'); - - render() { - return ( -
- -
- ); - } - } - - Horizon.render(, container); - - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: 2'); - // 在Array中增加一个对象 - Horizon.act(() => { - triggerClickEvent(container, 'addBtn'); - }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: 3'); - - // 在Array中删除一个对象 - Horizon.act(() => { - triggerClickEvent(container, 'delBtn'); - }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: 2'); - }); - - it('测试Array方法: entries()、push()、shift()、unshift、直接赋值', () => { - let globalStore = null; - - class Child extends Horizon.Component { - userStore = useStore('user'); - - constructor(props) { - super(props); - globalStore = this.userStore; - } - - render() { - const nameList = []; - const entries = this.userStore.$state.persons?.entries(); - if (entries) { - for (const entry of entries) { - nameList.push(entry[1].name); - } - } - - return ( -
- -
- ); - } - } - - Horizon.render(, container); - - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); - // push - globalStore.$state.persons.push(newPerson); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); - - // shift - globalStore.$state.persons.shift({ name: 'p0', age: 0 }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3'); - - // 赋值[2] - globalStore.$state.persons[2] = { name: 'p4', age: 4 }; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3 p4'); - - // 重新赋值[2] - globalStore.$state.persons[2] = { name: 'p5', age: 5 }; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3 p5'); - - // unshift - globalStore.$state.persons.unshift({ name: 'p1', age: 1 }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3 p5'); - - // 重新赋值 null - globalStore.$state.persons = null; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); - - // 重新赋值 [{ name: 'p1', age: 1 }] - globalStore.$state.persons = [{ name: 'p1', age: 1 }]; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1'); - }); - - it('测试Array方法: forEach()', () => { - let globalStore = null; - - class Child extends Horizon.Component { - userStore = useStore('user'); - - constructor(props) { - super(props); - globalStore = this.userStore; - } - - render() { - const nameList = []; - this.userStore.$state.persons?.forEach(per => { - nameList.push(per.name); - }); - - return ( -
- -
- ); - } - } - - Horizon.render(, container); - - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); - // push - globalStore.$state.persons.push(newPerson); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); - - // shift - globalStore.$state.persons.shift({ name: 'p0', age: 0 }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3'); - - // 赋值[2] - globalStore.$state.persons[2] = { name: 'p4', age: 4 }; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3 p4'); - - // 重新赋值[2] - globalStore.$state.persons[2] = { name: 'p5', age: 5 }; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p2 p3 p5'); - - // unshift - globalStore.$state.persons.unshift({ name: 'p1', age: 1 }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3 p5'); - - // 重新赋值 null - globalStore.$state.persons = null; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); - - // 重新赋值 [{ name: 'p1', age: 1 }] - globalStore.$state.persons = [{ name: 'p1', age: 1 }]; - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1'); - }); -}); diff --git a/scripts/__tests__/HorizonXText/class/ClassStateArray.test.tsx b/scripts/__tests__/HorizonXText/class/ClassStateArray.test.tsx new file mode 100644 index 00000000..2ca08ef5 --- /dev/null +++ b/scripts/__tests__/HorizonXText/class/ClassStateArray.test.tsx @@ -0,0 +1,231 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../../jest/logUtils'; +import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {App, Text, triggerClickEvent} from '../../jest/commonComponents'; +import {describe, beforeEach, afterEach, it, expect} from '@jest/globals'; + +type Person = {name:string,age:number}; + +const persons:Person[] = [{ name: 'p1', age: 1 }, { name: 'p2', age: 2 }]; +let useUserStore = createStore({ + id: 'user', + state: { + type: 'bing dun dun', + persons: persons, + }, + actions: { + addOnePerson: (state, person) => { + state.persons.push(person); + }, + delOnePerson: (state) => { + state.persons.pop(); + }, + clearPersons: (state) => { + state.persons = []; + }, + }, +}); + +describe('在Class组件中,测试store中的Array', () => { + const { unmountComponentAtNode } = Horizon; + let container:HTMLElement|null = null; + beforeEach(() => { + // 创建一个 DOM 元素作为渲染目标 + container = document.createElement('div'); + document.body.appendChild(container); + }); + + afterEach(() => { + // 退出时进行清理 + unmountComponentAtNode(container); + container?.remove(); + container = null; + LogUtils.clear(); + + clearStore('user'); + }); + + const newPerson = { name: 'p3', age: 3 }; + class Parent extends Horizon.Component { + userStore = useUserStore(); + props:{ + children:any[] + }; + + constructor(props){ + super(props); + this.props = props; + } + + addOnePerson = () => { + this.userStore.addOnePerson(newPerson); + } + + delOnePerson = () => { + this.userStore.delOnePerson(); + } + + render() { + return
+ + +
+ {this.props.children} +
+
+ } + } + + it('测试Array方法: push()、pop()', () => { + class Child extends Horizon.Component { + userStore = useUserStore(); + + render() { + return ( +
+ +
+ ); + } + } + + Horizon.render(, container); + + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 2'); + // 在Array中增加一个对象 + Horizon.act(() => { + triggerClickEvent(container, 'addBtn'); + }); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 3'); + + // 在Array中删除一个对象 + Horizon.act(() => { + triggerClickEvent(container, 'delBtn'); + }); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 2'); + }); + + it('测试Array方法: entries()、push()、shift()、unshift、直接赋值', () => { + let globalStore = useUserStore(); + + class Child extends Horizon.Component { + userStore = useUserStore(); + + constructor(props) { + super(props); + globalStore = this.userStore; + } + + render() { + const nameList:string[] = []; + const entries = this.userStore.$s.persons?.entries(); + if (entries) { + for (const entry of entries) { + nameList.push(entry[1].name); + } + } + + return ( +
+ +
+ ); + } + } + + Horizon.render(, container); + + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); + // push + globalStore?.$s.persons.push(newPerson); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); + + // shift + // @ts-ignore TODO:why has this function argument? + globalStore.$s.persons.shift({ name: 'p0', age: 0 }); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3'); + + // 赋值[2] + globalStore.$s.persons[2] = { name: 'p4', age: 4 }; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3 p4'); + + // 重新赋值[2] + globalStore.$s.persons[2] = { name: 'p5', age: 5 }; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3 p5'); + + // unshift + globalStore.$s.persons.unshift({ name: 'p1', age: 1 }); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3 p5'); + + // 重新赋值 [] + globalStore.$s.persons = []; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); + + // 重新赋值 [{ name: 'p1', age: 1 }] + globalStore.$s.persons = [{ name: 'p1', age: 1 }]; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1'); + }); + + it('测试Array方法: forEach()', () => { + let globalStore = useUserStore(); + globalStore.$s.persons.push({ name: 'p2', age: 2 }); + class Child extends Horizon.Component { + userStore = useUserStore(); + + constructor(props) { + super(props); + globalStore = this.userStore; + } + + render() { + const nameList:string[] = []; + this.userStore.$s.persons.forEach((per:Person) => { + nameList.push(per.name); + }); + + return ( +
+ +
+ ); + } + } + + Horizon.render(, container); + + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); + // push + globalStore.$s.persons.push(newPerson); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); + + // shift + // @ts-ignore TODO:why has this function argument? + globalStore.$s.persons.shift({ name: 'p0', age: 0 }); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3'); + + // 赋值[2] + globalStore.$s.persons[2] = { name: 'p4', age: 4 }; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3 p4'); + + // 重新赋值[2] + globalStore.$s.persons[2] = { name: 'p5', age: 5 }; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p2 p3 p5'); + + // unshift + globalStore.$s.persons.unshift({ name: 'p1', age: 1 }); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3 p5'); + + // 重新赋值 [] + globalStore.$s.persons = []; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); + + // 重新赋值 [{ name: 'p1', age: 1 }] + globalStore.$s.persons = [{ name: 'p1', age: 1 }]; + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1'); + }); + +}); diff --git a/scripts/__tests__/HorizonXText/class/ClassStateMap.test.js b/scripts/__tests__/HorizonXText/class/ClassStateMap.test.tsx similarity index 58% rename from scripts/__tests__/HorizonXText/class/ClassStateMap.test.js rename to scripts/__tests__/HorizonXText/class/ClassStateMap.test.tsx index 071ad650..bb790d8a 100644 --- a/scripts/__tests__/HorizonXText/class/ClassStateMap.test.js +++ b/scripts/__tests__/HorizonXText/class/ClassStateMap.test.tsx @@ -1,45 +1,48 @@ import * as Horizon from '@cloudsop/horizon/index.ts'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; -import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; +import * as LogUtils from '../../jest/logUtils'; +import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {App, Text, triggerClickEvent} from '../../jest/commonComponents'; +import {describe, beforeEach, afterEach, it, expect} from '@jest/globals'; + +const useUserStore = createStore({ + id: 'user', + state: { + type: 'bing dun dun', + persons: new Map([['p1', 1], ['p2', 2]]), + }, + actions: { + addOnePerson: (state, person) => { + state.persons.set(person.name, person.age); + }, + delOnePerson: (state, person) => { + state.persons.delete(person.name); + }, + clearPersons: (state) => { + state.persons.clear(); + }, + reset: (state)=>{ + state.persons=new Map([['p1', 1], ['p2', 2]]); + } + }, +}); describe('在Class组件中,测试store中的Map', () => { const { unmountComponentAtNode } = Horizon; - let container = null; + let container:HTMLElement|null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 container = document.createElement('div'); document.body.appendChild(container); - const persons = new Map([ - ['p1', 1], - ['p2', 2], - ]); - - createStore({ - id: 'user', - state: { - type: 'bing dun dun', - persons: persons, - }, - actions: { - addOnePerson: (state, person) => { - state.persons.set(person.name, person.age); - }, - delOnePerson: (state, person) => { - state.persons.delete(person.name); - }, - clearPersons: state => { - state.persons.clear(); - }, - }, - }); + useUserStore().reset(); }); afterEach(() => { // 退出时进行清理 unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; + LogUtils.clear(); clearStore('user'); }); @@ -47,17 +50,23 @@ describe('在Class组件中,测试store中的Map', () => { const newPerson = { name: 'p3', age: 3 }; class Parent extends Horizon.Component { - userStore = useStore('user'); + userStore = useUserStore(); + props = {children:[]} + + constructor(props){ + super(props); + this.props = props; + } addOnePerson = () => { this.userStore.addOnePerson(newPerson); - }; + } delOnePerson = () => { this.userStore.delOnePerson(newPerson); - }; + } clearPersons = () => { this.userStore.clearPersons(); - }; + } render() { return ( @@ -71,7 +80,9 @@ describe('在Class组件中,测试store中的Map', () => { -
{this.props.children}
+
+ {this.props.children} +
); } @@ -79,12 +90,12 @@ describe('在Class组件中,测试store中的Map', () => { it('测试Map方法: set()、delete()、clear()', () => { class Child extends Horizon.Component { - userStore = useStore('user'); + userStore = useUserStore(); render() { return (
- +
); } @@ -92,33 +103,33 @@ describe('在Class组件中,测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 2'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 3'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 2'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#size').innerHTML).toBe('persons number: 0'); + expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 0'); }); it('测试Map方法: keys()', () => { class Child extends Horizon.Component { - userStore = useStore('user'); + userStore = useUserStore(); render() { - const nameList = []; - const keys = this.userStore.$state.persons.keys(); + const nameList:string[] = []; + const keys = this.userStore.$s.persons.keys(); for (const key of keys) { nameList.push(key); } @@ -133,33 +144,33 @@ describe('在Class组件中,测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Map方法: values()', () => { class Child extends Horizon.Component { - userStore = useStore('user'); + userStore = useUserStore(); render() { - const ageList = []; - const values = this.userStore.$state.persons.values(); + const ageList:number[] = []; + const values = this.userStore.$s.persons.values(); for (const val of values) { ageList.push(val); } @@ -174,33 +185,33 @@ describe('在Class组件中,测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#ageList').innerHTML).toBe('age list: 1 2'); + expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#ageList').innerHTML).toBe('age list: 1 2 3'); + expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2 3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#ageList').innerHTML).toBe('age list: 1 2'); + expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#ageList').innerHTML).toBe('age list: '); + expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: '); }); it('测试Map方法: entries()', () => { class Child extends Horizon.Component { - userStore = useStore('user'); + userStore = useUserStore(); render() { - const nameList = []; - const entries = this.userStore.$state.persons.entries(); + const nameList:string[] = []; + const entries = this.userStore.$s.persons.entries(); for (const entry of entries) { nameList.push(entry[0]); } @@ -215,33 +226,33 @@ describe('在Class组件中,测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Map方法: forEach()', () => { class Child extends Horizon.Component { - userStore = useStore('user'); + userStore = useUserStore(); render() { - const nameList = []; - this.userStore.$state.persons.forEach((val, key) => { + const nameList:string[] = []; + this.userStore.$s.persons.forEach((val, key) => { nameList.push(key); }); @@ -255,34 +266,34 @@ describe('在Class组件中,测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Map方法: has()', () => { class Child extends Horizon.Component { - userStore = useStore('user'); + userStore = useUserStore(); render() { return (
- +
); } @@ -290,21 +301,21 @@ describe('在Class组件中,测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: false'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#hasPerson').innerHTML).toBe('has new person: true'); + expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); }); it('测试Map方法: for of()', () => { class Child extends Horizon.Component { - userStore = useStore('user'); + userStore = useUserStore(); render() { - const nameList = []; - for (const per of this.userStore.$state.persons) { + const nameList:string[] = []; + for (const per of this.userStore.$s.persons) { nameList.push(per[0]); } @@ -318,23 +329,23 @@ describe('在Class组件中,测试store中的Map', () => { Horizon.render(, container); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 Horizon.act(() => { triggerClickEvent(container, 'addBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2 p3'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 Horizon.act(() => { triggerClickEvent(container, 'delBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: p1 p2'); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map Horizon.act(() => { triggerClickEvent(container, 'clearBtn'); }); - expect(container.querySelector('#nameList').innerHTML).toBe('name list: '); + expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); }); diff --git a/scripts/__tests__/HorizonXText/clear/ClassVNodeClear.test.js b/scripts/__tests__/HorizonXText/clear/ClassVNodeClear.test.tsx similarity index 62% rename from scripts/__tests__/HorizonXText/clear/ClassVNodeClear.test.js rename to scripts/__tests__/HorizonXText/clear/ClassVNodeClear.test.tsx index a2226b1c..4fe4e719 100644 --- a/scripts/__tests__/HorizonXText/clear/ClassVNodeClear.test.js +++ b/scripts/__tests__/HorizonXText/clear/ClassVNodeClear.test.tsx @@ -1,15 +1,17 @@ import * as Horizon from '@cloudsop/horizon/index.ts'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; -import { Text, triggerClickEvent } from '../../jest/commonComponents'; -import { getObserver } from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; +import * as LogUtils from '../../jest/logUtils'; +import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {Text, triggerClickEvent} from '../../jest/commonComponents'; +import {getObserver} from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; +import {describe, it, beforeEach, afterEach, expect} from '@jest/globals'; describe('测试 Class VNode 清除时,对引用清除', () => { - const { unmountComponentAtNode } = Horizon; - let container = null; + const {unmountComponentAtNode} = Horizon; + let container:HTMLElement|null = null; let globalState = { name: 'bing dun dun', isWin: true, - isShow: true, + isShow: true }; beforeEach(() => { @@ -24,7 +26,7 @@ describe('测试 Class VNode 清除时,对引用清除', () => { setWin: (state, val) => { state.isWin = val; }, - hide: state => { + hide: (state) => { state.isShow = false; }, updateName: (state, val) => { @@ -37,8 +39,9 @@ describe('测试 Class VNode 清除时,对引用清除', () => { afterEach(() => { // 退出时进行清理 unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; + LogUtils.clear(); clearStore('user'); }); @@ -48,14 +51,12 @@ describe('测试 Class VNode 清除时,对引用清除', () => { userStore = useStore('user'); render() { - return ( -
- - {this.userStore.isShow && } -
- ); + return
+ + {this.userStore?.isShow && } +
; } } @@ -63,18 +64,16 @@ describe('测试 Class VNode 清除时,对引用清除', () => { userStore = useStore('user'); setWin = () => { - this.userStore.setWin(!this.userStore.isWin); - }; + this.userStore?.setWin(!this.userStore?.isWin); + } render() { - return ( -
- - {this.userStore.isWin && } -
- ); + return
+ + {this.userStore?.isWin && } +
; } } @@ -84,16 +83,14 @@ describe('测试 Class VNode 清除时,对引用清除', () => { render() { // this.userStore.updateName(this.userStore.name === 'bing dun dun' ? 'huo dun dun' : 'bing dun dun'); - return ( -
- - -
- ); + return
+ + +
; } } - Horizon.render(, container); + Horizon.render(, container); // Parent and Child hold the isWin key expect(getObserver(globalState).keyVNodes.get('isWin').size).toBe(2); diff --git a/scripts/__tests__/HorizonXText/clear/FunctionVNodeClear.test.js b/scripts/__tests__/HorizonXText/clear/FunctionVNodeClear.test.tsx similarity index 59% rename from scripts/__tests__/HorizonXText/clear/FunctionVNodeClear.test.js rename to scripts/__tests__/HorizonXText/clear/FunctionVNodeClear.test.tsx index ead0453a..76633b76 100644 --- a/scripts/__tests__/HorizonXText/clear/FunctionVNodeClear.test.js +++ b/scripts/__tests__/HorizonXText/clear/FunctionVNodeClear.test.tsx @@ -1,15 +1,17 @@ import * as Horizon from '@cloudsop/horizon/index.ts'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; -import { Text, triggerClickEvent } from '../../jest/commonComponents'; -import { getObserver } from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; +import * as LogUtils from '../../jest/logUtils'; +import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {Text, triggerClickEvent} from '../../jest/commonComponents'; +import {getObserver} from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; +import {describe, it, beforeEach, afterEach, expect} from '@jest/globals'; describe('测试VNode清除时,对引用清除', () => { - const { unmountComponentAtNode } = Horizon; - let container = null; + const {unmountComponentAtNode} = Horizon; + let container:HTMLElement|null = null; let globalState = { name: 'bing dun dun', isWin: true, - isShow: true, + isShow: true }; beforeEach(() => { @@ -24,9 +26,9 @@ describe('测试VNode清除时,对引用清除', () => { setWin: (state, val) => { state.isWin = val; }, - hide: state => { + hide: (state) => { state.isShow = false; - }, + } }, }); }); @@ -34,8 +36,9 @@ describe('测试VNode清除时,对引用清除', () => { afterEach(() => { // 退出时进行清理 unmountComponentAtNode(container); - container.remove(); + container?.remove(); container = null; + LogUtils.clear(); clearStore('user'); }); @@ -45,14 +48,12 @@ describe('测试VNode清除时,对引用清除', () => { userStore = useStore('user'); render() { - return ( -
- - {this.userStore.isShow && } -
- ); + return
+ + {this.userStore?.isShow && } +
; } } @@ -60,18 +61,16 @@ describe('测试VNode清除时,对引用清除', () => { userStore = useStore('user'); setWin = () => { - this.userStore.setWin(!this.userStore.isWin); - }; + this.userStore?.setWin(!this.userStore.isWin); + } render() { - return ( -
- - {this.userStore.isWin && } -
- ); + return
+ + {this.userStore?.isWin && } +
; } } @@ -79,16 +78,14 @@ describe('测试VNode清除时,对引用清除', () => { userStore = useStore('user'); render() { - return ( -
- - -
- ); + return
+ + +
; } } - Horizon.render(, container); + Horizon.render(, container); // Parent and Child hold the isWin key expect(getObserver(globalState).keyVNodes.get('isWin').size).toBe(2); diff --git a/scripts/__tests__/HorizonXText/edgeCases/proxy.test.js b/scripts/__tests__/HorizonXText/edgeCases/proxy.test.js deleted file mode 100644 index a643f311..00000000 --- a/scripts/__tests__/HorizonXText/edgeCases/proxy.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import { createProxy } from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; - -describe('Proxy', () => { - const arr = []; - - it('Should not double wrap proxies', async () => { - const proxy1 = createProxy(arr); - - const proxy2 = createProxy(proxy1); - - expect(proxy1 === proxy2).toBe(true); - }); - - it('Should re-use existing proxy of same object', async () => { - const proxy1 = createProxy(arr); - - const proxy2 = createProxy(arr); - - expect(proxy1 === proxy2).toBe(true); - }); -}); diff --git a/scripts/__tests__/HorizonXText/edgeCases/proxy.test.tsx b/scripts/__tests__/HorizonXText/edgeCases/proxy.test.tsx new file mode 100644 index 00000000..d402e35c --- /dev/null +++ b/scripts/__tests__/HorizonXText/edgeCases/proxy.test.tsx @@ -0,0 +1,48 @@ +import {createProxy} from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; +import {readonlyProxy} from '../../../../libs/horizon/src/horizonx/proxy/readonlyProxy'; +import {describe, beforeEach, afterEach, it, expect} from '@jest/globals'; + +describe('Proxy', () => { + const arr = []; + + it('Should not double wrap proxies', async () => { + const proxy1 = createProxy(arr); + + const proxy2 = createProxy(proxy1); + + expect(proxy1 === proxy2).toBe(true); + }); + + it('Should re-use existing proxy of same object', async () => { + const proxy1 = createProxy(arr); + + const proxy2 = createProxy(arr); + + expect(proxy1 === proxy2).toBe(true); + }); + + it('Readonly proxy should prevent changes', async () => { + const proxy1 = readonlyProxy([1]); + + try{ + proxy1.push('a'); + expect(true).toBe(false);//we expect exception above + }catch(e){ + //expected + } + + try{ + proxy1[0]=null; + expect(true).toBe(false);//we expect exception above + }catch(e){ + //expected + } + + try{ + delete proxy1[0]; + expect(true).toBe(false);//we expect exception above + }catch(e){ + //expected + } + }); +}); diff --git a/scripts/__tests__/jest/logUtils.js b/scripts/__tests__/jest/logUtils.js new file mode 100644 index 00000000..ecb32c98 --- /dev/null +++ b/scripts/__tests__/jest/logUtils.js @@ -0,0 +1,26 @@ +let dataArray = null; + +const log = value => { + if (dataArray === null) { + dataArray = [value]; + } else { + dataArray.push(value); + } +}; + +const getAndClear = () => { + if (dataArray === null) { + return []; + } + const values = dataArray; + dataArray = null; + return values; +}; + +const clear = () => { + dataArray = dataArray ? null : dataArray; +}; + +exports.clear = clear; +exports.log = log; +exports.getAndClear = getAndClear; diff --git a/scripts/rollup/rollup.config.js b/scripts/rollup/rollup.config.js index c3c31b49..c58fa290 100644 --- a/scripts/rollup/rollup.config.js +++ b/scripts/rollup/rollup.config.js @@ -4,6 +4,7 @@ import path from 'path'; import fs from 'fs'; import replace from '@rollup/plugin-replace'; import copy from './copy-plugin'; +import execute from 'rollup-plugin-execute'; import { terser } from 'rollup-plugin-terser'; import { version as horizonVersion } from '@cloudsop/horizon/package.json'; @@ -58,6 +59,7 @@ function genConfig(mode) { }, preventAssignment: true, }), + execute('npm run build-types'), mode === 'production' && terser(), copy([ { From 8995659ea50434cf60cacf6c12ba0c0439fc8945 Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 29 Jul 2022 20:38:43 +0800 Subject: [PATCH 02/74] Match-id-204bc47828a04bfb747ed16f9dbd8d1885dddb7d --- .../src/horizonx/adapters/reduxReact.ts | 11 +++-- .../src/horizonx/store/StoreHandler.ts | 12 ++--- .../StoreFunctionality/basicAccess.test.tsx | 45 +++++++++++++++++++ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/libs/horizon/src/horizonx/adapters/reduxReact.ts b/libs/horizon/src/horizonx/adapters/reduxReact.ts index 8e205eb7..0954fbb3 100644 --- a/libs/horizon/src/horizonx/adapters/reduxReact.ts +++ b/libs/horizon/src/horizonx/adapters/reduxReact.ts @@ -33,12 +33,11 @@ export function createSelectorHook(context: Context): (selector?: (any) => any) return function(selector = state => state) { const [b, fr] = useState(false); - const listener = () => { - fr(!b); - }; - useEffect(() => { - return store.subscribe(listener); + const unsubscribe = store.subscribe(() => fr(!b)); + return () => { + unsubscribe(); + }; }); return selector(store.getState()); @@ -110,7 +109,7 @@ export function connect( useEffect(() => { const unsubscribe = store.subscribe(() => forceReload(!f)); - () => { + return () => { unsubscribe(); }; }); diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index ffc67aa8..ecbe22d9 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -15,7 +15,7 @@ function isPromise(obj: any): boolean { type StoreConfig, C extends UserComputedValues> = { state?: S; - options?: { suppressHooks?: boolean }; + // options?: { suppressHooks?: boolean }; actions?: A; id?: string; computed?: C; @@ -27,18 +27,18 @@ export type ReduxStoreHandler = { getState: () => any; subscribe: (listener: () => void) => () => void; replaceReducer: (reducer: (state: any, action: { type: string }) => any) => void; - _horizonXstore: StoreHandler; + // _horizonXstore: StoreHandler; }; type StoreHandler, C extends UserComputedValues> = { $subscribe: (listener: () => void) => void; $unsubscribe: (listener: () => void) => void; $s: S; - $config: StoreConfig; + // $config: StoreConfig; $queue: QueuedStoreActions; $a: StoreActions; $c: UserComputedValues; - reduxHandler?: ReduxStoreHandler; + // reduxHandler?: ReduxStoreHandler; } & { [K in keyof S]: S[K] } & { [K in keyof A]: Action } & { [K in keyof C]: ReturnType }; @@ -256,13 +256,15 @@ function hookStore() { function createStoreHook, C extends UserComputedValues>( storeHandler: StoreHandler ): () => StoreHandler { - return () => { + const storeHook = () => { if (!storeHandler.$config.options?.suppressHooks) { hookStore(); } return storeHandler; }; + + return storeHook; } export function useStore, C extends UserComputedValues>( diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx b/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx index 0a2c8584..9d95c95f 100644 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx @@ -107,4 +107,49 @@ describe('Basic store manipulation', () => { expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('5'); }); + + it('should call computed from own actions', () => { + const useIncrementStore = createStore({ + id: 'incrementStore', + state: { + count: 2, + }, + actions: { + doublePlusOne: function(state) { + state.count = this.double + 1; + }, + }, + computed:{ + double: (state) => { + return state.count*2 + } + } + }); + + function App() { + const incrementStore = useIncrementStore(); + + return ( +
+ +

{incrementStore.count}

+
+ ); + } + + Horizon.render(, container); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + }); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('5'); + }) }); From ccf66f1653c4aee2497db67ca579ab804f1d2593 Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 15 Aug 2022 20:47:47 +0800 Subject: [PATCH 03/74] Match-id-5f3ff7a11a13ae24c88d6483333762208df08e28 --- libs/horizon/src/horizonx/adapters/redux.ts | 44 +++- .../adapters/reduxPromiseMiddleware.ts | 0 .../src/horizonx/proxy/ProxyHandler.ts | 4 +- .../proxy/handlers/ObjectProxyHandler.ts | 8 +- .../src/horizonx/store/StoreHandler.ts | 2 +- .../StoreFunctionality/async.test.tsx | 209 ++++++------------ .../adapters/ReduxAdapterPromiseMiddleware.js | 96 -------- 7 files changed, 121 insertions(+), 242 deletions(-) delete mode 100644 libs/horizon/src/horizonx/adapters/reduxPromiseMiddleware.ts delete mode 100644 scripts/__tests__/HorizonXText/adapters/ReduxAdapterPromiseMiddleware.js diff --git a/libs/horizon/src/horizonx/adapters/redux.ts b/libs/horizon/src/horizonx/adapters/redux.ts index fb19cf9a..4372e0fa 100644 --- a/libs/horizon/src/horizonx/adapters/redux.ts +++ b/libs/horizon/src/horizonx/adapters/redux.ts @@ -32,6 +32,43 @@ export type ReduxMiddleware = ( type Reducer = (state: any, action: ReduxAction) => any; +function mergeData(state,data){ + console.log('merging data',{state,data}); + if(!data){ + console.log('!data'); + state.stateWrapper=data; + return; + } + + if(Array.isArray(data) && Array.isArray(state?.stateWrapper)){ + console.log('data is array'); + state.stateWrapper.length = data.length; + data.forEach((item,idx) => { + if(item!=state.stateWrapper[idx]){ + state.stateWrapper[idx]=item; + } + }); + return; + } + + if(typeof data === 'object' && typeof state?.stateWrapper === 'object'){ + console.log('data is object'); + Object.keys(state.stateWrapper).forEach(key => { + if(!data.hasOwnProperty(key)) delete state.stateWrapper[key]; + }) + + Object.entries(data).forEach(([key,value])=>{ + if(state.stateWrapper[key]!==value){ + state.stateWrapper[key]=value; + } + }); + return; + } + + console.log('data is primitive or type mismatch'); + state.stateWrapper = data; +} + export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): ReduxStoreHandler { const store = createStoreX({ id: 'defaultStore', @@ -48,14 +85,19 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): if (result === undefined) { return; } // NOTE: reducer should never return undefined, in this case, do not change state + // mergeData(state,result); state.stateWrapper = result; }, }, options: { - suppressHooks: true, + reduxAdapter: true, }, })(); + // store.$subscribe(()=>{ + // console.log('changed'); + // }); + const result = { reducer, getState: function() { diff --git a/libs/horizon/src/horizonx/adapters/reduxPromiseMiddleware.ts b/libs/horizon/src/horizonx/adapters/reduxPromiseMiddleware.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index a540e199..f67dcc47 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -38,7 +38,9 @@ export function createProxy(rawObj: any, hookObserver = true): any { // 创建Proxy let proxyObj; - if (isArray(rawObj)) { + if (!hookObserver) { + proxyObj = createObjectProxy(rawObj,true); + } else if (isArray(rawObj)) { // 数组 proxyObj = createArrayProxy(rawObj as []); } else if (isCollection(rawObj)) { diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 28749a57..4dc27a0b 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -2,16 +2,16 @@ import { isSame } from '../../CommonUtils'; import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { OBSERVER_KEY } from '../../Constants'; -export function createObjectProxy(rawObj: T): ProxyHandler { +export function createObjectProxy(rawObj: T, singleLevel = false): ProxyHandler { const proxy = new Proxy(rawObj, { - get, + get: (...args) => get(...args, singleLevel), set, }); return proxy; } -export function get(rawObj: object, key: string | symbol, receiver: any): any { +export function get(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any { // The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. if (key === OBSERVER_KEY) { return undefined; @@ -34,7 +34,7 @@ export function get(rawObj: object, key: string | symbol, receiver: any): any { // 对于prototype不做代理 if (key !== 'prototype') { // 对于value也需要进一步代理 - const valProxy = createProxy(value, hookObserverMap.get(rawObj)); + const valProxy = singleLevel ? value : createProxy(value, hookObserverMap.get(rawObj)); return valProxy; } diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index ecbe22d9..e82a4a96 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -91,7 +91,7 @@ export function createStore, C extend throw new Error('store obj must be pure object'); } - const proxyObj = createProxy(config.state, !config.options?.suppressHooks); + const proxyObj = createProxy(config.state, !config.options?.reduxAdapter); proxyObj.$pending = false; diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/async.test.tsx b/scripts/__tests__/HorizonXText/StoreFunctionality/async.test.tsx index 66c287e9..d7cab296 100644 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/async.test.tsx +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/async.test.tsx @@ -1,5 +1,5 @@ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Horizon from '../../../../libs/horizon'; import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; import { triggerClickEvent } from '../../jest/commonComponents'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; @@ -8,159 +8,90 @@ const { unmountComponentAtNode } = Horizon; function postpone(timer, func) { return new Promise(resolve => { - setTimeout(function() { + window.setTimeout(function () { + console.log('resolving postpone'); resolve(func()); }, timer); }); } -describe('Asynchronous functions', () => { - let container: HTMLElement | null = null; - - const COUNTER_ID = 'counter'; - const TOGGLE_ID = 'toggle'; - const TOGGLE_FAST_ID = 'toggleFast'; - const RESULT_ID = 'result'; - - let useAsyncCounter; +describe('Asynchronous store', () => { + const useAsyncCounter = createStore({ + state: { + counter: 0, + check: false, + }, + actions: { + increment: function (state) { + return new Promise(resolve => { + window.setTimeout(() => { + state.counter++; + resolve(true); + }, 10); + }); + }, + toggle: function (state) { + state.check = !state.check; + }, + reset: function (state) { + state.check = false; + state.counter = 0; + }, + }, + computed: { + value: state => { + return (state.check ? 'true' : 'false') + state.counter; + }, + }, + }); beforeEach(() => { - useAsyncCounter = createStore({ - state: { - counter: 0, - check: false, - }, - actions: { - increment: function(state) { - return new Promise(resolve => { - setTimeout(() => { - state.counter++; - resolve(true); - }, 100); - }); - }, - toggle: function(state) { - state.check = !state.check; - }, - }, - computed: { - value: state => { - return (state.check ? 'true' : 'false') + state.counter; - }, - }, - }); - container = document.createElement('div'); - document.body.appendChild(container); + useAsyncCounter().reset(); }); - afterEach(() => { - unmountComponentAtNode(container); - container?.remove(); - container = null; - }); - - it('Should wait for async actions', async () => { - // @ts-ignore - jest.useRealTimers(); - let globalStore; - - function App() { - const store = useAsyncCounter(); - globalStore = store; - - return ( -
-

{store.value}

- - - -
- ); - } - - Horizon.render(, container); - - // initial state - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false0'); - - // slow toggle has nothing to wait for, it is resolved immediately - Horizon.act(() => { - triggerClickEvent(container, TOGGLE_ID); - }); - - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('true0'); - - // counter increment is slow. slow toggle waits for result - Horizon.act(() => { - triggerClickEvent(container, COUNTER_ID); - }); - Horizon.act(() => { - triggerClickEvent(container, TOGGLE_ID); - }); - - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('true0'); - - // fast toggle does not wait for counter and it is resolved immediately - Horizon.act(() => { - triggerClickEvent(container, TOGGLE_FAST_ID); - }); - - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false0'); - - // at 150ms counter increment will be resolved and slow toggle immediately after - const t150 = postpone(150, () => { - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('true1'); - }); - - // before that, two more actions are added to queue - another counter and slow toggle - Horizon.act(() => { - triggerClickEvent(container, COUNTER_ID); - }); - Horizon.act(() => { - triggerClickEvent(container, TOGGLE_ID); - }); - - // at 250ms they should be already resolved - const t250 = postpone(250, () => { - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false2'); - }); - - await Promise.all([t150, t250]); - }); - - it('call async action by then', async () => { - // @ts-ignore + it('should return promise when queued function is called', () => { jest.useFakeTimers(); - let globalStore; - function App() { - const store = useAsyncCounter(); - globalStore = store; + const store = useAsyncCounter(); - return ( -
-

{store.value}

-
- ); - } + return new Promise(resolve => { + store.$queue.increment().then(() => { + expect(store.counter == 1); + resolve(true); + }); - Horizon.render(, container); - - // call async action by then - globalStore.$queue.increment().then(() => { - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false1'); + jest.advanceTimersByTime(150); }); + }); - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false0'); + it('should queue async functions', () => { + jest.useFakeTimers(); + return new Promise(resolve => { + const store = useAsyncCounter(); - // past 150 ms - // @ts-ignore - jest.advanceTimersByTime(150); + //initial value + expect(store.value).toBe('false0'); + + // no blocking action action + store.$queue.toggle(); + expect(store.value).toBe('true0'); + + // store is not updated before blocking action is resolved + store.$queue.increment(); + const togglePromise = store.$queue.toggle(); + expect(store.value).toBe('true0'); + + // fast action is resolved immediatelly + store.toggle(); + expect(store.value).toBe('false0'); + + // queued action waits for blocking action to resolve + togglePromise.then(() => { + expect(store.value).toBe('true1'); + resolve(); + }); + + jest.advanceTimersByTime(150); + }); }); }); diff --git a/scripts/__tests__/HorizonXText/adapters/ReduxAdapterPromiseMiddleware.js b/scripts/__tests__/HorizonXText/adapters/ReduxAdapterPromiseMiddleware.js deleted file mode 100644 index 509706cf..00000000 --- a/scripts/__tests__/HorizonXText/adapters/ReduxAdapterPromiseMiddleware.js +++ /dev/null @@ -1,96 +0,0 @@ -export const ActionType = { - Pending: 'PENDING', - Fulfilled: 'FULFILLED', - Rejected: 'REJECTED', -}; - -export const promise = store => next => action => { - //let result = next(action); - store._horizonXstore.$queue.dispatch(action); - return result; -}; - -export function createPromise(config = {}) { - const defaultTypes = [ActionType.Pending, ActionType.Fulfilled, ActionType.Rejected]; - const PROMISE_TYPE_SUFFIXES = config.promiseTypeSuffixes || defaultTypes; - const PROMISE_TYPE_DELIMITER = config.promiseTypeDelimiter || '_'; - - return store => { - const { dispatch } = store; - - return next => action => { - /** - * Instantiate variables to hold: - * (1) the promise - * (2) the data for optimistic updates - */ - let promise; - let data; - - /** - * There are multiple ways to dispatch a promise. The first step is to - * determine if the promise is defined: - * (a) explicitly (action.payload.promise is the promise) - * (b) implicitly (action.payload is the promise) - * (c) as an async function (returns a promise when called) - * - * If the promise is not defined in one of these three ways, we don't do - * anything and move on to the next middleware in the middleware chain. - */ - - // Step 1a: Is there a payload? - if (action.payload) { - const PAYLOAD = action.payload; - - // Step 1.1: Is the promise implicitly defined? - if (isPromise(PAYLOAD)) { - promise = PAYLOAD; - } - - // Step 1.2: Is the promise explicitly defined? - else if (isPromise(PAYLOAD.promise)) { - promise = PAYLOAD.promise; - data = PAYLOAD.data; - } - - // Step 1.3: Is the promise returned by an async function? - else if (typeof PAYLOAD === 'function' || typeof PAYLOAD.promise === 'function') { - promise = PAYLOAD.promise ? PAYLOAD.promise() : PAYLOAD(); - data = PAYLOAD.promise ? PAYLOAD.data : undefined; - - // Step 1.3.1: Is the return of action.payload a promise? - if (!isPromise(promise)) { - // If not, move on to the next middleware. - return next({ - ...action, - payload: promise, - }); - } - } - - // Step 1.4: If there's no promise, move on to the next middleware. - else { - return next(action); - } - - // Step 1b: If there's no payload, move on to the next middleware. - } else { - return next(action); - } - - /** - * Instantiate and define constants for: - * (1) the action type - * (2) the action meta - */ - const TYPE = action.type; - const META = action.meta; - - /** - * Instantiate and define constants for the action type suffixes. - * These are appended to the end of the action type. - */ - const [PENDING, FULFILLED, REJECTED] = PROMISE_TYPE_SUFFIXES; - }; - }; -} From 5d8eebf14487af249fdaab1bbf777799fca2901a Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 24 Aug 2022 17:29:09 +0800 Subject: [PATCH 04/74] Match-id-cec2ce18d7d08f5b4f5642c57aaa799038a73223 --- .../src/horizonx/store/StoreHandler.ts | 8 +- .../src/renderer/render/BaseComponent.ts | 3 + .../multipleStores.test.tsx | 186 ++++++++++++++++++ 3 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 scripts/__tests__/HorizonXText/StoreFunctionality/multipleStores.test.tsx diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index bdbf8f77..fc3ed6c9 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -204,7 +204,8 @@ export function createStore, C extend return createStoreHook(handler); } -function clearVNodeObservers(vNode) { +export function clearVNodeObservers(vNode) { + if(!vNode.observers) return; vNode.observers.forEach(observer => { observer.clearByVNode(vNode); }); @@ -220,10 +221,7 @@ function hookStore() { return; } - if (processingVNode.observers) { - // 清除上一次缓存的Observer依赖 - clearVNodeObservers(processingVNode); - } else { + if (!processingVNode.observers) { processingVNode.observers = new Set(); } diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/horizon/src/renderer/render/BaseComponent.ts index 1ea097d3..46fd1e0a 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/horizon/src/renderer/render/BaseComponent.ts @@ -12,6 +12,7 @@ import { FlagUtils } from '../vnode/VNodeFlags'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; import componentRenders from './index'; import {setProcessingVNode} from '../GlobalVar'; +import { clearVNodeObservers } from '../../horizonx/store/StoreHandler'; // 复用vNode时,也需对stack进行处理 function handlerContext(processing: VNode) { @@ -55,6 +56,8 @@ export function captureVNode(processing: VNode): VNode | null { processing.shouldUpdate = false; setProcessingVNode(processing); + + clearVNodeObservers(processing); const child = component.captureRender(processing, shouldUpdate); setProcessingVNode(null); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/multipleStores.test.tsx b/scripts/__tests__/HorizonXText/StoreFunctionality/multipleStores.test.tsx new file mode 100644 index 00000000..a9b933d9 --- /dev/null +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/multipleStores.test.tsx @@ -0,0 +1,186 @@ +//@ts-ignore +import Horizon, { createStore } from '@cloudsop/horizon/index.ts'; +import { triggerClickEvent } from '../../jest/commonComponents'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; + +const { unmountComponentAtNode } = Horizon; + +const useStore1 = createStore({ + state:{ counter:1 }, + actions:{ + add:(state)=>state.counter++, + reset: (state)=>state.counter=1 + } +}) + +const useStore2 = createStore({ + state:{ counter2:1 }, + actions:{ + add2:(state)=>state.counter2++, + reset: (state)=>state.counter2=1 + } +}) + +describe('Using multiple stores', () => { + let container: HTMLElement | null = null; + + const BUTTON_ID = 'btn'; + const BUTTON_ID2 = 'btn2'; + const RESULT_ID = 'result'; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + useStore1().reset(); + useStore2().reset(); + }); + + afterEach(() => { + unmountComponentAtNode(container); + container?.remove(); + container = null; + }); + + it('Should use multiple stores in class component', () => { + class App extends Horizon.Component{ + render(){ + const {counter,add} = useStore1(); + const store2 = useStore2(); + const {counter2, add2} = store2; + + return ( +
+ + +

{counter*counter2}

+
+ ) + } + } + + Horizon.render(, container); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + triggerClickEvent(container, BUTTON_ID); + triggerClickEvent(container, BUTTON_ID2); + triggerClickEvent(container, BUTTON_ID2); + }); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9'); + }); + + it('Should use use stores in cycles and multiple methods', () => { + interface App { + store:any, + store2:any + } + class App extends Horizon.Component{ + constructor(){ + super(); + this.store = useStore1(); + this.store2 = useStore2() + } + + render(){ + const {counter,add} = useStore1(); + const store2 = useStore2(); + const {counter2, add2} = store2; + + for(let i=0; i<100; i++){ + const {counter,add} = useStore1(); + const store2 = useStore2(); + const {counter2, add2} = store2; + } + + return ( +
+ + +

{counter*counter2}

+
+ ) + } + } + + Horizon.render(, container); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + triggerClickEvent(container, BUTTON_ID); + triggerClickEvent(container, BUTTON_ID2); + triggerClickEvent(container, BUTTON_ID2); + }); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9'); + }); + + it('Should use multiple stores in function component', () => { + function App() { + const {counter,add} = useStore1(); + const store2 = useStore2(); + const {counter2, add2} = store2; + + return ( +
+ + +

{counter*counter2}

+
+ ); + } + + Horizon.render(, container); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + triggerClickEvent(container, BUTTON_ID); + triggerClickEvent(container, BUTTON_ID2); + triggerClickEvent(container, BUTTON_ID2); + }); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9'); + }); +}); \ No newline at end of file From b20293076caec6184cab54f64d69f18c33bda9da Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 31 Aug 2022 16:41:25 +0800 Subject: [PATCH 05/74] Match-id-0c7552eff73b3193eb58fd25a2dfe13e09f93e41 --- libs/horizon/index.ts | 3 + libs/horizon/src/horizonx/proxy/Observer.ts | 2 + .../proxy/handlers/ArrayProxyHandler.ts | 20 ++ .../proxy/handlers/CollectionProxyHandler.ts | 18 ++ .../proxy/handlers/ObjectProxyHandler.ts | 17 ++ .../src/horizonx/store/StoreHandler.ts | 2 +- .../src/renderer/render/BaseComponent.ts | 2 +- .../multipleStores.test.tsx | 186 ------------------ 8 files changed, 62 insertions(+), 188 deletions(-) delete mode 100644 scripts/__tests__/HorizonXText/StoreFunctionality/multipleStores.test.tsx diff --git a/libs/horizon/index.ts b/libs/horizon/index.ts index 9e29e71d..f8035375 100644 --- a/libs/horizon/index.ts +++ b/libs/horizon/index.ts @@ -33,6 +33,7 @@ import { runAsyncEffects } from './src/renderer/submit/HookEffectHandler'; import { createStore, useStore, clearStore } from './src/horizonx/store/StoreHandler'; import * as reduxAdapter from './src/horizonx/adapters/redux'; +import { watch } from './src/horizonx/proxy/watch'; // act用于测试,作用是:如果fun触发了刷新(包含了异步刷新),可以保证在act后面的代码是在刷新完成后才执行。 const act = fun => { @@ -85,6 +86,7 @@ const Horizon = { useStore, clearStore, reduxAdapter, + watch }; export const version = __VERSION__; @@ -125,6 +127,7 @@ export { useStore, clearStore, reduxAdapter, + watch }; export default Horizon; diff --git a/libs/horizon/src/horizonx/proxy/Observer.ts b/libs/horizon/src/horizonx/proxy/Observer.ts index d93c7a05..b00e942e 100644 --- a/libs/horizon/src/horizonx/proxy/Observer.ts +++ b/libs/horizon/src/horizonx/proxy/Observer.ts @@ -33,6 +33,8 @@ export class Observer implements IObserver { listeners:(()=>void)[] = []; + watchers={} as {[key:string]:((key:string, oldValue:any, newValue:any)=>void)[]} + useProp(key: string | symbol): void { const processingVNode = getProcessingVNode(); if (processingVNode === null || !processingVNode.observers) { diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index a6812bde..dd3fdb0b 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -12,6 +12,20 @@ export function createArrayProxy(rawObj: any[]): any[] { } function get(rawObj: any[], key: string, receiver: any) { + if (key === 'watch'){ + const observer = getObserver(rawObj); + + return (prop:any, handler:(key:string, oldValue:any, newValue:any)=>void)=>{ + if(!observer.watchers[prop]){ + observer.watchers[prop]=[] as ((key:string, oldValue:any, newValue:any)=>void)[]; + } + observer.watchers[prop].push(handler); + return ()=>{ + observer.watchers[prop]=observer.watchers[prop].filter(cb=>cb!==handler); + } + } + } + if (isValidIntegerKey(key) || key === 'length') { return objectGet(rawObj, key, receiver); } @@ -29,6 +43,12 @@ function set(rawObj: any[], key: string, value: any, receiver: any) { const observer = getObserver(rawObj); if (!isSame(newValue, oldValue)) { + if(observer.watchers?.[key]){ + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue); + }); + } + observer.setProp(key); } diff --git a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts index bd044b44..15aaa682 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts @@ -34,6 +34,18 @@ function get(rawObj: { size: number }, key: any, receiver: any): any { } else if (Object.prototype.hasOwnProperty.call(handler, key)) { const value = Reflect.get(handler, key, receiver); return value.bind(null, rawObj); + } else if (key === 'watch'){ + const observer = getObserver(rawObj); + + return (prop:any, handler:(key:string, oldValue:any, newValue:any)=>void)=>{ + if(!observer.watchers[prop]){ + observer.watchers[prop]=[] as ((key:string, oldValue:any, newValue:any)=>void)[]; + } + observer.watchers[prop].push(handler); + return ()=>{ + observer.watchers[prop]=observer.watchers[prop].filter(cb=>cb!==handler); + } + } } return Reflect.get(rawObj, key, receiver); @@ -67,6 +79,12 @@ function set( } if (valChange) { + if(observer.watchers?.[key]){ + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue); + }); + } + observer.setProp(key); } diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 4dc27a0b..29ab2658 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -19,6 +19,18 @@ export function get(rawObj: object, key: string | symbol, receiver: any, singleL const observer = getObserver(rawObj); + if (key === 'watch'){ + return (prop, handler:(key:string, oldValue:any, newValue:any)=>void)=>{ + if(!observer.watchers[prop]){ + observer.watchers[prop]=[] as ((key:string, oldValue:any, newValue:any)=>void)[]; + } + observer.watchers[prop].push(handler); + return ()=>{ + observer.watchers[prop]=observer.watchers[prop].filter(cb=>cb!==handler); + } + } + } + if (key === 'addListener') { return observer.addListener.bind(observer); } @@ -54,6 +66,11 @@ export function set(rawObj: object, key: string, value: any, receiver: any): boo const ret = Reflect.set(rawObj, key, newValue, receiver); if (!isSame(newValue, oldValue)) { + if(observer.watchers?.[key]){ + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue); + }); + } observer.setProp(key); } diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index fc3ed6c9..00d4695e 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -220,7 +220,7 @@ function hookStore() { if (!processingVNode) { return; } - + if (!processingVNode.observers) { processingVNode.observers = new Set(); } diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/horizon/src/renderer/render/BaseComponent.ts index 46fd1e0a..4192e100 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/horizon/src/renderer/render/BaseComponent.ts @@ -57,7 +57,7 @@ export function captureVNode(processing: VNode): VNode | null { setProcessingVNode(processing); - clearVNodeObservers(processing); + if(processing.observers) clearVNodeObservers(processing); const child = component.captureRender(processing, shouldUpdate); setProcessingVNode(null); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/multipleStores.test.tsx b/scripts/__tests__/HorizonXText/StoreFunctionality/multipleStores.test.tsx deleted file mode 100644 index a9b933d9..00000000 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/multipleStores.test.tsx +++ /dev/null @@ -1,186 +0,0 @@ -//@ts-ignore -import Horizon, { createStore } from '@cloudsop/horizon/index.ts'; -import { triggerClickEvent } from '../../jest/commonComponents'; -import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; - -const { unmountComponentAtNode } = Horizon; - -const useStore1 = createStore({ - state:{ counter:1 }, - actions:{ - add:(state)=>state.counter++, - reset: (state)=>state.counter=1 - } -}) - -const useStore2 = createStore({ - state:{ counter2:1 }, - actions:{ - add2:(state)=>state.counter2++, - reset: (state)=>state.counter2=1 - } -}) - -describe('Using multiple stores', () => { - let container: HTMLElement | null = null; - - const BUTTON_ID = 'btn'; - const BUTTON_ID2 = 'btn2'; - const RESULT_ID = 'result'; - - beforeEach(() => { - container = document.createElement('div'); - document.body.appendChild(container); - useStore1().reset(); - useStore2().reset(); - }); - - afterEach(() => { - unmountComponentAtNode(container); - container?.remove(); - container = null; - }); - - it('Should use multiple stores in class component', () => { - class App extends Horizon.Component{ - render(){ - const {counter,add} = useStore1(); - const store2 = useStore2(); - const {counter2, add2} = store2; - - return ( -
- - -

{counter*counter2}

-
- ) - } - } - - Horizon.render(, container); - - Horizon.act(() => { - triggerClickEvent(container, BUTTON_ID); - triggerClickEvent(container, BUTTON_ID); - triggerClickEvent(container, BUTTON_ID2); - triggerClickEvent(container, BUTTON_ID2); - }); - - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9'); - }); - - it('Should use use stores in cycles and multiple methods', () => { - interface App { - store:any, - store2:any - } - class App extends Horizon.Component{ - constructor(){ - super(); - this.store = useStore1(); - this.store2 = useStore2() - } - - render(){ - const {counter,add} = useStore1(); - const store2 = useStore2(); - const {counter2, add2} = store2; - - for(let i=0; i<100; i++){ - const {counter,add} = useStore1(); - const store2 = useStore2(); - const {counter2, add2} = store2; - } - - return ( -
- - -

{counter*counter2}

-
- ) - } - } - - Horizon.render(, container); - - Horizon.act(() => { - triggerClickEvent(container, BUTTON_ID); - triggerClickEvent(container, BUTTON_ID); - triggerClickEvent(container, BUTTON_ID2); - triggerClickEvent(container, BUTTON_ID2); - }); - - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9'); - }); - - it('Should use multiple stores in function component', () => { - function App() { - const {counter,add} = useStore1(); - const store2 = useStore2(); - const {counter2, add2} = store2; - - return ( -
- - -

{counter*counter2}

-
- ); - } - - Horizon.render(, container); - - Horizon.act(() => { - triggerClickEvent(container, BUTTON_ID); - triggerClickEvent(container, BUTTON_ID); - triggerClickEvent(container, BUTTON_ID2); - triggerClickEvent(container, BUTTON_ID2); - }); - - expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9'); - }); -}); \ No newline at end of file From fbf7e8370ba029b60a210f68256abde29ca7afbe Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 19 Sep 2022 17:19:07 +0800 Subject: [PATCH 06/74] Match-id-0a9b956d94031f8d8f03fb90913325154133063d --- libs/horizon/src/horizonx/adapters/redux.ts | 32 +-- .../src/horizonx/adapters/reduxReact.ts | 77 ++++--- libs/horizon/src/horizonx/proxy/watch.ts | 8 + .../src/horizonx/store/StoreHandler.ts | 21 +- package.json | 5 +- .../StoreFunctionality/basicAccess.test.tsx | 54 +++-- .../StoreFunctionality/watch.test.tsx | 132 ++++++++++++ .../adapters/ReduxReactAdapter.test.tsx | 2 +- .../HorizonXText/adapters/connectTest.tsx | 46 ++++ .../edgeCases/multipleStores.test.tsx | 199 ++++++++++++++++++ 10 files changed, 506 insertions(+), 70 deletions(-) create mode 100644 libs/horizon/src/horizonx/proxy/watch.ts create mode 100644 scripts/__tests__/HorizonXText/StoreFunctionality/watch.test.tsx create mode 100644 scripts/__tests__/HorizonXText/adapters/connectTest.tsx create mode 100644 scripts/__tests__/HorizonXText/edgeCases/multipleStores.test.tsx diff --git a/libs/horizon/src/horizonx/adapters/redux.ts b/libs/horizon/src/horizonx/adapters/redux.ts index 4372e0fa..ed17b5d4 100644 --- a/libs/horizon/src/horizonx/adapters/redux.ts +++ b/libs/horizon/src/horizonx/adapters/redux.ts @@ -32,34 +32,34 @@ export type ReduxMiddleware = ( type Reducer = (state: any, action: ReduxAction) => any; -function mergeData(state,data){ - console.log('merging data',{state,data}); - if(!data){ +function mergeData(state, data) { + console.log('merging data', { state, data }); + if (!data) { console.log('!data'); - state.stateWrapper=data; + state.stateWrapper = data; return; } - if(Array.isArray(data) && Array.isArray(state?.stateWrapper)){ + if (Array.isArray(data) && Array.isArray(state?.stateWrapper)) { console.log('data is array'); state.stateWrapper.length = data.length; - data.forEach((item,idx) => { - if(item!=state.stateWrapper[idx]){ - state.stateWrapper[idx]=item; + data.forEach((item, idx) => { + if (item != state.stateWrapper[idx]) { + state.stateWrapper[idx] = item; } }); return; } - if(typeof data === 'object' && typeof state?.stateWrapper === 'object'){ + if (typeof data === 'object' && typeof state?.stateWrapper === 'object') { console.log('data is object'); Object.keys(state.stateWrapper).forEach(key => { - if(!data.hasOwnProperty(key)) delete state.stateWrapper[key]; - }) + if (!data.hasOwnProperty(key)) delete state.stateWrapper[key]; + }); - Object.entries(data).forEach(([key,value])=>{ - if(state.stateWrapper[key]!==value){ - state.stateWrapper[key]=value; + Object.entries(data).forEach(([key, value]) => { + if (state.stateWrapper[key] !== value) { + state.stateWrapper[key] = value; } }); return; @@ -100,7 +100,7 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): const result = { reducer, - getState: function() { + getState: function () { return store.$s.stateWrapper; }, subscribe: listener => { @@ -169,7 +169,7 @@ export function bindActionCreators(actionCreators: ActionCreators, dispatch: Dis return boundActionCreators; } -export function compose(middlewares: ReduxMiddleware[]) { +export function compose(...middlewares: ReduxMiddleware[]) { return (store: ReduxStoreHandler, extraArgument: any) => { let val; middlewares.reverse().forEach((middleware: ReduxMiddleware, index) => { diff --git a/libs/horizon/src/horizonx/adapters/reduxReact.ts b/libs/horizon/src/horizonx/adapters/reduxReact.ts index 0954fbb3..09e4b081 100644 --- a/libs/horizon/src/horizonx/adapters/reduxReact.ts +++ b/libs/horizon/src/horizonx/adapters/reduxReact.ts @@ -4,7 +4,8 @@ import { createContext } from '../../renderer/components/context/CreateContext'; import { createElement } from '../../external/JSXElement'; import { BoundActionCreator } from './redux'; import { ReduxAction } from './redux'; -import { ReduxStoreHandler } from '../store/StoreHandler' +import { ReduxStoreHandler } from '../store/StoreHandler'; +import { VNode } from '../../renderer/Types'; const DefaultContext = createContext(null); type Context = typeof DefaultContext; @@ -22,15 +23,15 @@ export function Provider({ return createElement(Context.Provider, { value: store }, children); } -export function createStoreHook(context: Context) { +export function createStoreHook(context: Context): () => ReduxStoreHandler { return () => { - return useContext(context); + return useContext(context) as unknown as ReduxStoreHandler; }; } export function createSelectorHook(context: Context): (selector?: (any) => any) => any { - const store = (createStoreHook(context)() as unknown) as ReduxStoreHandler; - return function(selector = state => state) { + const store = createStoreHook(context)() as unknown as ReduxStoreHandler; + return function (selector = state => state) { const [b, fr] = useState(false); useEffect(() => { @@ -44,9 +45,9 @@ export function createSelectorHook(context: Context): (selector?: (any) => any) }; } -export function createDispatchHook(context: Context): ()=>BoundActionCreator { - const store = (createStoreHook(context)() as unknown) as ReduxStoreHandler; - return function() { +export function createDispatchHook(context: Context): () => BoundActionCreator { + const store = createStoreHook(context)() as unknown as ReduxStoreHandler; + return function () { return action => { store.dispatch(action); }; @@ -84,28 +85,46 @@ export const useStore = () => { // areMergedPropsEqual: shallowCompare // }; -export function connect( - mapStateToProps?: (state: any, ownProps: { [key: string]: any }) => Object, - mapDispatchToProps?: - | { [key: string]: (...args: any[]) => ReduxAction } - | ((dispatch: (action: ReduxAction) => any, ownProps?: Object) => Object), - mergeProps?: (stateProps: Object, dispatchProps: Object, ownProps: Object) => Object, +type MapStateToPropsP = (state: any, ownProps: OwnProps) => StateProps; +type MapDispatchToPropsP = + | { [key: string]: (...args: any[]) => ReduxAction } + | ((dispatch: (action: ReduxAction) => any, ownProps: OwnProps) => DispatchProps); +type MergePropsP = ( + stateProps: StateProps, + dispatchProps: DispatchProps, + ownProps: OwnProps +) => MergedProps; + +type WrappedComponent = (props: OwnProps) => ReturnType; +type OriginalComponent = (props: MergedProps) => ReturnType; +type Connector = (Component: OriginalComponent) => WrappedComponent; + +export function connect( + mapStateToProps: MapStateToPropsP = () => ({} as StateProps), + mapDispatchToProps: MapDispatchToPropsP = () => ({} as DispatchProps), + mergeProps: MergePropsP = ( + stateProps, + dispatchProps, + ownProps + ): MergedProps => ({ ...stateProps, ...dispatchProps, ...ownProps } as MergedProps), options?: { areStatesEqual?: (oldState: any, newState: any) => boolean; - context?: any; // TODO: type this + context?: Context; } -) { +): Connector { if (!options) { options = {}; } - return Component => { + //this component should bear the type returned from mapping functions + return (Component: OriginalComponent): WrappedComponent => { const useStore = createStoreHook(options?.context || DefaultContext); - function Wrapper(props) { + //this component should mimic original type of component used + const Wrapper: WrappedComponent = (props: OwnProps) => { const [f, forceReload] = useState(true); - const store = (useStore() as unknown) as ReduxStoreHandler; + const store = useStore(); useEffect(() => { const unsubscribe = store.subscribe(() => forceReload(!f)); @@ -119,36 +138,34 @@ export function connect( mappedState: {}, }) as { current: { - state: {}; - mappedState: {}; + state: { [key: string]: any }; + mappedState: StateProps; }; }; - let mappedState; + let mappedState: StateProps; if (options?.areStatesEqual) { if (options.areStatesEqual(previous.current.state, store.getState())) { - mappedState = previous.current.mappedState; + mappedState = previous.current.mappedState as StateProps; } else { - mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : {}; + mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : ({} as StateProps); previous.current.mappedState = mappedState; } } else { - mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : {}; + mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : ({} as StateProps); previous.current.mappedState = mappedState; } - let mappedDispatch: { dispatch?: (action) => void } = {}; + let mappedDispatch: DispatchProps = {} as DispatchProps; if (mapDispatchToProps) { if (typeof mapDispatchToProps === 'object') { Object.entries(mapDispatchToProps).forEach(([key, value]) => { - mappedDispatch[key] = (...args) => { + mappedDispatch[key] = (...args: ReduxAction[]) => { store.dispatch(value(...args)); }; }); } else { mappedDispatch = mapDispatchToProps(store.dispatch, props); } - } else { - mappedDispatch.dispatch = store.dispatch; } const mergedProps = ( mergeProps || @@ -161,7 +178,7 @@ export function connect( const node = createElement(Component, mergedProps); return node; - } + }; return Wrapper; }; diff --git a/libs/horizon/src/horizonx/proxy/watch.ts b/libs/horizon/src/horizonx/proxy/watch.ts new file mode 100644 index 00000000..87f55280 --- /dev/null +++ b/libs/horizon/src/horizonx/proxy/watch.ts @@ -0,0 +1,8 @@ +export function watch(stateVariable:any,listener:(stateVariable:any)=>void){ + listener = listener.bind(null,stateVariable); + stateVariable.addListener(listener); + + return ()=>{ + stateVariable.removeListener(listener); + } +} \ No newline at end of file diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index 00d4695e..f8055275 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -35,9 +35,7 @@ type StoreHandler, C extends UserComp $queue: QueuedStoreActions; $a: StoreActions; $c: UserComputedValues; -} & { [K in keyof S]: S[K] } & - { [K in keyof A]: Action } & - { [K in keyof C]: ReturnType }; +} & { [K in keyof S]: S[K] } & { [K in keyof A]: Action } & { [K in keyof C]: ReturnType }; type PlannedAction> = { action: string; @@ -76,6 +74,7 @@ export function createStore, C extend //create a local shalow copy to ensure consistency (if user would change the config object after store creation) config = { id: config.id, + /* @ts-ignore*/ options: config.options, state: config.state, actions: config.actions ? { ...config.actions } : undefined, @@ -87,6 +86,7 @@ export function createStore, C extend throw new Error('store obj must be pure object'); } + /* @ts-ignore*/ const proxyObj = createProxy(config.state, !config.options?.reduxAdapter); proxyObj.$pending = false; @@ -103,7 +103,7 @@ export function createStore, C extend const $a: Partial> = {}; const $queue: Partial> = {}; const $c: Partial> = {}; - const handler = ({ + const handler = { $subscribe, $unsubscribe, $a: $a as StoreActions, @@ -111,7 +111,7 @@ export function createStore, C extend $c: $c as ComputedValues, $config: config, $queue: $queue as QueuedStoreActions, - } as unknown) as StoreHandler; + } as unknown as StoreHandler; function tryNextAction() { if (!plannedActions.length) { @@ -193,6 +193,9 @@ export function createStore, C extend Object.keys(config.state).forEach(key => { Object.defineProperty(handler, key, { get: () => proxyObj[key], + set: value => { + proxyObj[key] = value; + }, }); }); } @@ -205,7 +208,7 @@ export function createStore, C extend } export function clearVNodeObservers(vNode) { - if(!vNode.observers) return; + if (!vNode.observers) return; vNode.observers.forEach(observer => { observer.clearByVNode(vNode); }); @@ -220,14 +223,14 @@ function hookStore() { if (!processingVNode) { return; } - + if (!processingVNode.observers) { processingVNode.observers = new Set(); } if (processingVNode.tag === FunctionComponent) { // from FunctionComponent - const vNodeRef = (useRef(null) as unknown) as { current: VNode }; + const vNodeRef = useRef(null) as unknown as { current: VNode }; vNodeRef.current = processingVNode; useEffect(() => { @@ -239,7 +242,7 @@ function hookStore() { } else if (processingVNode.tag === ClassComponent) { // from ClassComponent if (!processingVNode.classComponentWillUnmount) { - processingVNode.classComponentWillUnmount = function(vNode) { + processingVNode.classComponentWillUnmount = function (vNode) { clearVNodeObservers(vNode); vNode.observers = null; }; diff --git a/package.json b/package.json index 500c07f5..96429935 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,11 @@ "@babel/plugin-transform-object-super": "7.16.7", "@babel/plugin-transform-parameters": "7.16.7", "@babel/plugin-transform-react-jsx": "7.16.7", + "@babel/plugin-transform-react-jsx-source": "^7.16.7", "@babel/plugin-transform-runtime": "7.16.7", "@babel/plugin-transform-shorthand-properties": "7.16.7", "@babel/plugin-transform-spread": "7.16.7", "@babel/plugin-transform-template-literals": "7.16.7", - "@babel/plugin-transform-react-jsx-source": "^7.16.7", "@babel/preset-env": "7.16.7", "@babel/preset-typescript": "7.16.7", "@rollup/plugin-babel": "^5.3.1", @@ -66,5 +66,8 @@ "engines": { "node": ">=10.x", "npm": ">=7.x" + }, + "dependencies": { + "ejs": "^3.1.8" } } diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx b/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx index 9d95c95f..9647bb1d 100644 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx @@ -36,6 +36,34 @@ describe('Basic store manipulation', () => { expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1'); }); + it('Should use direct setters', () => { + function App() { + const logStore = useLogStore(); + + return ( +
+ +

{logStore.logs[0]}

+
+ ); + } + + Horizon.render(, container); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + }); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('q'); + }); + it('Should use actions and update components', () => { function App() { const logStore = useLogStore(); @@ -74,7 +102,7 @@ describe('Basic store manipulation', () => { increment: state => { state.count++; }, - doublePlusOne: function(state) { + doublePlusOne: function (state) { state.count = state.count * 2; this.increment(); }, @@ -115,20 +143,20 @@ describe('Basic store manipulation', () => { count: 2, }, actions: { - doublePlusOne: function(state) { + doublePlusOne: function (state) { state.count = this.double + 1; }, }, - computed:{ - double: (state) => { - return state.count*2 - } - } + computed: { + double: state => { + return state.count * 2; + }, + }, }); - + function App() { const incrementStore = useIncrementStore(); - + return (
); } - + Horizon.render(, container); - + Horizon.act(() => { triggerClickEvent(container, BUTTON_ID); }); - + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('5'); - }) + }); }); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/watch.test.tsx b/scripts/__tests__/HorizonXText/StoreFunctionality/watch.test.tsx new file mode 100644 index 00000000..806c6029 --- /dev/null +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/watch.test.tsx @@ -0,0 +1,132 @@ +import { createStore } from "@cloudsop/horizon/src/horizonx/store/StoreHandler"; +import { watch } from "@cloudsop/horizon/src/horizonx/proxy/watch"; + +describe("watch",()=>{ + it('shouhld watch promitive state variable', async()=>{ + const useStore = createStore({ + state:{ + variable:'x' + }, + actions:{ + change:(state)=>state.variable = "a" + } + }); + + const store = useStore(); + let counter = 0; + + watch(store.$s,(state)=>{ + counter++; + expect(state.variable).toBe('a'); + }) + + store.change(); + + expect(counter).toBe(1); + }); + it('shouhld watch object variable', async()=>{ + const useStore = createStore({ + state:{ + variable:'x' + }, + actions:{ + change:(state)=>state.variable = "a" + } + }); + + const store = useStore(); + let counter = 0; + + store.$s.watch('variable',()=>{ + counter++; + }) + + store.change(); + + expect(counter).toBe(1); + }); + + it('shouhld watch array item', async()=>{ + const useStore = createStore({ + state:{ + arr:['x'] + }, + actions:{ + change:(state)=>state.arr[0]='a' + } + }); + + const store = useStore(); + let counter = 0; + + store.arr.watch('0',()=>{ + counter++; + }) + + store.change(); + + expect(counter).toBe(1); + }); + + it('shouhld watch collection item', async()=>{ + const useStore = createStore({ + state:{ + collection:new Map([ + ['a', 'a'], + ]) + }, + actions:{ + change:(state)=>state.collection.set('a','x') + } + }); + + const store = useStore(); + let counter = 0; + + store.collection.watch('a',()=>{ + counter++; + }) + + store.change(); + + expect(counter).toBe(1); + }); + + it('should watch multiple variables independedntly', async()=>{ + const useStore = createStore({ + state:{ + bool1:true, + bool2:false + }, + actions:{ + toggle1:state=>state.bool1=!state.bool1, + toggle2:state=>state.bool2=!state.bool2 + } + }); + + let counter1=0; + let counterAll=0; + const store = useStore(); + + watch(store.$s,()=>{ + counterAll++; + }) + + store.$s.watch('bool1',()=>{ + counter1++; + }); + + store.toggle1(); + store.toggle1(); + + store.toggle2(); + + store.toggle1(); + + store.toggle2(); + store.toggle2(); + + expect(counter1).toBe(3); + expect(counterAll).toBe(6); + }) +}) \ No newline at end of file diff --git a/scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.tsx b/scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.tsx index 4f6feec6..12fc9eb9 100644 --- a/scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.tsx +++ b/scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.tsx @@ -293,4 +293,4 @@ describe('Redux/React binding adapter', () => { expect(getE(BUTTON).innerHTML).toBe('1'); expect(getE(BUTTON2).innerHTML).toBe('true'); }); -}); +}); \ No newline at end of file diff --git a/scripts/__tests__/HorizonXText/adapters/connectTest.tsx b/scripts/__tests__/HorizonXText/adapters/connectTest.tsx new file mode 100644 index 00000000..a11b8249 --- /dev/null +++ b/scripts/__tests__/HorizonXText/adapters/connectTest.tsx @@ -0,0 +1,46 @@ +import { createElement } from '../../../../libs/horizon/src/external/JSXElement'; +import { createDomTextVNode } from '../../../../libs/horizon/src/renderer/vnode/VNodeCreator'; +import { createStore } from '../../../../libs/horizon/src/horizonx/adapters/redux'; +import { connect } from '../../../../libs/horizon/src/horizonx/adapters/reduxReact'; + +createStore((state: number = 0, action): number => { + if (action.type === 'add') return state + 1; + return 0; +}); + +type WrappedButtonProps = { add: () => void; count: number; text: string }; + +function Button(props: WrappedButtonProps) { + const { add, count, text } = props; + return createElement( + 'button', + { + onClick: add, + }, + createDomTextVNode(text), + createDomTextVNode(': '), + createDomTextVNode(count) + ); +} + +const connector = connect( + state => ({ count: state }), + dispatch => ({ + add: (): void => { + dispatch({ type: 'add' }); + }, + }), + (stateProps, dispatchProps, ownProps: { text: string }) => ({ + add: dispatchProps.add, + count: stateProps.count, + text: ownProps.text, + }) +); + +const ConnectedButton = connector(Button); + +function App() { + return createElement('div', {}, createElement(ConnectedButton, { text: 'click' })); +} + +export default App; diff --git a/scripts/__tests__/HorizonXText/edgeCases/multipleStores.test.tsx b/scripts/__tests__/HorizonXText/edgeCases/multipleStores.test.tsx new file mode 100644 index 00000000..63fb74e5 --- /dev/null +++ b/scripts/__tests__/HorizonXText/edgeCases/multipleStores.test.tsx @@ -0,0 +1,199 @@ +//@ts-ignore +import Horizon, { createStore } from '@cloudsop/horizon/index.ts'; +import { triggerClickEvent } from '../../jest/commonComponents'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; + +const { unmountComponentAtNode } = Horizon; + +const useStore1 = createStore({ + state:{ counter:1 }, + actions:{ + add:(state)=>state.counter++, + reset: (state)=>state.counter=1 + } +}) + +const useStore2 = createStore({ + state:{ counter2:1 }, + actions:{ + add2:(state)=>state.counter2++, + reset: (state)=>state.counter2=1 + } +}) + +describe('Using multiple stores', () => { + let container: HTMLElement | null = null; + + const BUTTON_ID = 'btn'; + const BUTTON_ID2 = 'btn2'; + const RESULT_ID = 'result'; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + useStore1().reset(); + useStore2().reset(); + }); + + afterEach(() => { + unmountComponentAtNode(container); + container?.remove(); + container = null; + }); + + it('Should use multiple stores in class component', () => { + class App extends Horizon.Component{ + render(){ + const {counter,add} = useStore1(); + const {counter2, add2} = useStore2(); + + return ( +
+ + +

{counter} {counter2}

+
+ ) + } + } + + Horizon.render(, container); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1 1'); + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + + }); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 1'); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID2); + + }); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 2'); + + }); + + it('Should use use stores in cycles and multiple methods', () => { + interface App { + store:any, + store2:any + } + class App extends Horizon.Component{ + constructor(){ + super(); + this.store = useStore1(); + this.store2 = useStore2() + } + + render(){ + const {counter,add} = useStore1(); + const store2 = useStore2(); + const {counter2, add2} = store2; + + for(let i=0; i<100; i++){ + const {counter,add} = useStore1(); + const store2 = useStore2(); + const {counter2, add2} = store2; + } + + return ( +
+ + +

{counter} {counter2}

+
+ ) + } + } + + Horizon.render(, container); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1 1'); + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + + }); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 1'); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID2); + + }); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 2'); + + }); + + it('Should use multiple stores in function component', () => { + function App() { + const {counter,add} = useStore1(); + const store2 = useStore2(); + const {counter2, add2} = store2; + + return ( +
+ + +

{counter} {counter2}

+
+ ); + } + + Horizon.render(, container); + + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1 1'); + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID); + + }); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 1'); + + Horizon.act(() => { + triggerClickEvent(container, BUTTON_ID2); + + }); + expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 2'); + }); +}); \ No newline at end of file From 5be06f068356e39e83252ed86c61588839f57066 Mon Sep 17 00:00:00 2001 From: * <*> Date: Sat, 12 Nov 2022 02:03:04 +0800 Subject: [PATCH 07/74] Match-id-efec6baa7c4396c9a4dfb7bb733134ca50f5ff34 --- libs/horizon/src/external/devtools.ts | 2 +- .../src/horizonx/devtools/constants.ts | 8 +++ libs/horizon/src/horizonx/devtools/index.ts | 51 +++++++++++++++++ .../src/horizonx/store/StoreHandler.ts | 57 ++++++++++++++++++- 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 libs/horizon/src/horizonx/devtools/constants.ts create mode 100644 libs/horizon/src/horizonx/devtools/index.ts diff --git a/libs/horizon/src/external/devtools.ts b/libs/horizon/src/external/devtools.ts index 1fb1af9b..9e8295cb 100644 --- a/libs/horizon/src/external/devtools.ts +++ b/libs/horizon/src/external/devtools.ts @@ -39,7 +39,7 @@ export const helper = { return { name: HookName.RefHook, hIndex, value: (state as Ref).current }; } else if (isEffectHook(state)) { const name = - state.effectConstant == EffectConstant.LayoutEffect || (EffectConstant.LayoutEffect | EffectConstant.DepsChange) + state.effectConstant == EffectConstant.LayoutEffect || EffectConstant.LayoutEffect | EffectConstant.DepsChange ? HookName.LayoutEffectHook : HookName.EffectHook; return { name, hIndex, value: (state as Effect).effect }; diff --git a/libs/horizon/src/horizonx/devtools/constants.ts b/libs/horizon/src/horizonx/devtools/constants.ts new file mode 100644 index 00000000..df41626f --- /dev/null +++ b/libs/horizon/src/horizonx/devtools/constants.ts @@ -0,0 +1,8 @@ +export const INITIALIZED = 'horizonx store initialized'; +export const STATE_CHANGE = 'horizonx state change'; +export const SUBSCRIBED = 'horizonx subscribed'; +export const UNSUBSCRIBED = 'horizonx unsubscribed'; +export const ACTION = 'horizonx action'; +export const ACTION_QUEUED = 'horizonx action queued'; +export const QUEUE_PENDING = 'horizonx queue pending'; +export const QUEUE_FINISHED = 'horizonx queue finished'; diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts new file mode 100644 index 00000000..884daabf --- /dev/null +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -0,0 +1,51 @@ +const sessionId = Date.now(); + +function makeStoreSnapshot({ type, data }) { + const expanded = {}; + Object.keys(data.store.$c).forEach(key => { + expanded[key] = data.store[key]; + }); + data.store.expanded = expanded; + const snapshot = makeProxySnapshot({ + data, + type, + sessionId, + }); + return snapshot; +} + +function makeProxySnapshot(obj) { + let clone; + try { + if (!obj) { + return obj; + } + if (obj.nativeEvent) return obj.type + 'Event'; + if (typeof obj === 'function') { + return obj.toString(); + } + if (Array.isArray(obj)) { + clone = []; + obj.forEach(item => clone.push(makeProxySnapshot(item))); + return clone; + } else if (typeof obj === 'object') { + clone = {}; + Object.entries(obj).forEach(([id, value]) => (clone[id] = makeProxySnapshot(value))); + return clone; + } + return obj; + } catch (err) { + throw console.log('cannot serialize object. ' + err); + } +} + +export const devtools = { + emit: (type, data) => { + console.log('store snapshot:', makeStoreSnapshot({ type, data })); + window.postMessage({ + type: 'HORIZON_DEV_TOOLS', + payload: makeStoreSnapshot({ type, data }), + from: 'dev tool hook', + }); + }, +}; diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index f8055275..af44a17f 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -6,6 +6,23 @@ import readonlyProxy from '../proxy/readonlyProxy'; import { Observer } from '../proxy/Observer'; import { FunctionComponent, ClassComponent } from '../Constants'; import { VNode } from '../../renderer/Types'; +import { devtools } from '../devtools'; +import { + ACTION, + ACTION_QUEUED, + INITIALIZED, + QUEUE_FINISHED, + STATE_CHANGE, + SUBSCRIBED, + UNSUBSCRIBED, +} from '../devtools/constants'; + +const idGenerator = { + id: 0, + get: function (prefix) { + return prefix.toString() + this.id++; + }, +}; const storeMap = new Map>(); @@ -73,7 +90,7 @@ export function createStore, C extend ): () => StoreHandler { //create a local shalow copy to ensure consistency (if user would change the config object after store creation) config = { - id: config.id, + id: config.id || idGenerator.get('UNKNOWN_STORE_'), /* @ts-ignore*/ options: config.options, state: config.state, @@ -92,10 +109,12 @@ export function createStore, C extend proxyObj.$pending = false; const $subscribe = listener => { + devtools.emit(SUBSCRIBED, { store: handler, listener }); proxyObj.addListener(listener); }; const $unsubscribe = listener => { + devtools.emit(UNSUBSCRIBED, handler); proxyObj.removeListener(listener); }; @@ -115,11 +134,13 @@ export function createStore, C extend function tryNextAction() { if (!plannedActions.length) { + devtools.emit(QUEUE_FINISHED, { store: handler }); proxyObj.$pending = false; return; } const nextAction = plannedActions.shift()!; + devtools.emit(ACTION, { store: handler, action: nextAction, fromQueue: true }); const result = config.actions ? config.actions[nextAction.action].bind(handler, proxyObj)(...nextAction.payload) : undefined; @@ -139,6 +160,14 @@ export function createStore, C extend if (config.actions) { Object.keys(config.actions).forEach(action => { ($queue as any)[action] = (...payload) => { + devtools.emit(ACTION_QUEUED, { + store: handler, + action: { + action, + payload, + }, + fromQueue: true, + }); return new Promise(resolve => { if (!proxyObj.$pending) { proxyObj.$pending = true; @@ -164,6 +193,14 @@ export function createStore, C extend }; ($a as any)[action] = function Wrapped(...payload) { + devtools.emit(ACTION, { + store: handler, + action: { + action, + payload, + }, + fromQueue: false, + }); return config.actions![action].bind(handler, proxyObj)(...payload); }; @@ -171,6 +208,14 @@ export function createStore, C extend Object.defineProperty(handler, action, { writable: false, value: (...payload) => { + devtools.emit(ACTION, { + store: handler, + action: { + action, + payload, + }, + fromQueue: false, + }); return config.actions![action].bind(handler, proxyObj)(...payload); }, }); @@ -204,6 +249,16 @@ export function createStore, C extend storeMap.set(config.id, handler); } + devtools.emit(INITIALIZED, { + store: handler, + }); + + handler.$subscribe(() => { + devtools.emit(STATE_CHANGE, { + store: handler, + }); + }); + return createStoreHook(handler); } From c0b8a5d03ae69c58b4e7805f956b0107c58e28b0 Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 14 Nov 2022 18:07:24 +0800 Subject: [PATCH 08/74] Match-id-71b6384a6cb67e909c9c996382413fe86a027a06 --- libs/horizon/src/horizonx/proxy/ProxyHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index 9ea2c7af..ccc54ab1 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -29,7 +29,7 @@ export const hookObserverMap = new WeakMap(); export function createProxy(rawObj: any, isHookObserver = true): any { // 不是对象(是原始数据类型)不用代理 - if (!isObject(rawObj)) { + if (!(rawObj && isObject(rawObj))) { return rawObj; } From 3bd5d456216fe72c2506258431daf9fb26b51fa5 Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 14 Nov 2022 18:37:15 +0800 Subject: [PATCH 09/74] Match-id-1f56d48f42d368d727c6a47fd2d56ee9b3603fc6 --- libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index a231988e..18ce8f1b 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -70,6 +70,7 @@ export function get(rawObj: object, key: string | symbol, receiver: any, singleL } export function set(rawObj: object, key: string, value: any, receiver: any): boolean { + const oldObject = JSON.stringify(rawObj); const observer = getObserver(rawObj); if (value && key == 'removeListener') { @@ -89,5 +90,6 @@ export function set(rawObj: object, key: string, value: any, receiver: any): boo observer.setProp(key); } + console.log('mutation from: ', JSON.parse(oldObject), ' to: ', ret); return ret; } From ef3e42adbe1844516a7ad50ea8dabc938ca6b4ac Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 14 Nov 2022 18:45:51 +0800 Subject: [PATCH 10/74] Match-id-926c1d67d360579f81de7d630a2785652b57aabe --- libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 18ce8f1b..b2eba435 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -70,6 +70,7 @@ export function get(rawObj: object, key: string | symbol, receiver: any, singleL } export function set(rawObj: object, key: string, value: any, receiver: any): boolean { + console.log('ObjectProxyHandler.set()'); const oldObject = JSON.stringify(rawObj); const observer = getObserver(rawObj); From f37a70552a1020aae89a496d3a5545bb4cf415e8 Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 18 Nov 2022 22:13:06 +0800 Subject: [PATCH 11/74] Match-id-a610f3bb353c9901f46e8f676e3d6b2092a862b6 --- libs/horizon/src/horizonx/CommonUtils.ts | 91 ++++++++++++++++++- libs/horizon/src/horizonx/devtools/index.ts | 1 + .../src/horizonx/proxy/HooklessObserver.ts | 14 +-- libs/horizon/src/horizonx/proxy/Observer.ts | 22 ++--- .../proxy/handlers/ArrayProxyHandler.ts | 9 +- .../proxy/handlers/CollectionProxyHandler.ts | 17 ++-- .../proxy/handlers/ObjectProxyHandler.ts | 11 +-- .../src/horizonx/store/StoreHandler.ts | 3 +- libs/horizon/src/horizonx/types.d.ts | 26 +++--- .../StoreFunctionality/utils.test.js | 80 ++++++++++++++++ 10 files changed, 221 insertions(+), 53 deletions(-) create mode 100644 scripts/__tests__/HorizonXText/StoreFunctionality/utils.test.js diff --git a/libs/horizon/src/horizonx/CommonUtils.ts b/libs/horizon/src/horizonx/CommonUtils.ts index e799c963..e6387b6a 100644 --- a/libs/horizon/src/horizonx/CommonUtils.ts +++ b/libs/horizon/src/horizonx/CommonUtils.ts @@ -19,19 +19,31 @@ export function isObject(obj: any): boolean { } export function isSet(obj: any): boolean { - return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object Set]' || obj.constructor === Set); + return ( + (obj !== null || obj !== undefined) && + (Object.prototype.toString.call(obj) === '[object Set]' || obj.constructor === Set) + ); } export function isWeakSet(obj: any): boolean { - return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object WeakSet]' || obj.constructor === WeakSet); + return ( + (obj !== null || obj !== undefined) && + (Object.prototype.toString.call(obj) === '[object WeakSet]' || obj.constructor === WeakSet) + ); } export function isMap(obj: any): boolean { - return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object Map]' || obj.constructor === Map); + return ( + (obj !== null || obj !== undefined) && + (Object.prototype.toString.call(obj) === '[object Map]' || obj.constructor === Map) + ); } export function isWeakMap(obj: any): boolean { - return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object WeakMap]' || obj.constructor === WeakMap); + return ( + (obj !== null || obj !== undefined) && + (Object.prototype.toString.call(obj) === '[object WeakMap]' || obj.constructor === WeakMap) + ); } export function isArray(obj: any): boolean { @@ -68,3 +80,74 @@ export function isSame(x, y) { return Object.is(x, y); } } + +export function getDetailedType(val: any) { + if (val === undefined) return 'undefined'; + if (val === null) return 'null'; + if (isCollection(val)) return 'collection'; + if (isPromise(val)) return 'promise'; + if (isArray(val)) return 'array'; + if (isWeakMap(val)) return 'weakMap'; + if (isMap(val)) return 'map'; + if (isWeakSet(val)) return 'weakSet'; + if (isSet(val)) return 'set'; + return typeof val; +} + +export function resolveMutation(from, to) { + if (getDetailedType(from) !== getDetailedType(to)) { + return { mutation: true, from, to }; + } + + switch (getDetailedType(from)) { + case 'array': { + let len = Math.max(from.length, to.length); + const res: any[] = []; + let found = false; + for (let i = 0; i < len; i++) { + if (from.length <= i) { + res[i] = { mutation: true, to: to[i] }; + found = true; + } else if (to.length <= i) { + res[i] = { mutation: true, from: from[i] }; + found = true; + } else { + res[i] = resolveMutation(from[i], to[i]); + if (res[i].mutation) found = true; + } + } + // TODO: resolve shifts + return { mutation: found, items: res, from, to }; + } + + case 'object': { + let keys = Object.keys({ ...from, ...to }); + const res = {}; + let found = false; + keys.forEach(key => { + if (!(key in from)) { + res[key] = { mutation: true, to: to[key] }; + found = true; + return; + } + + if (!(key in to)) { + res[key] = { mutation: true, from: from[key] }; + found = true; + return; + } + res[key] = resolveMutation(from[key], to[key]); + if (res[key].mutation) found = true; + }); + return { mutation: found, attributes: res, from, to }; + } + + // TODO: implement collections + + default: { + if (from === to) return { mutation: false }; + + return { mutation: true, from, to }; + } + } +} diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 884daabf..5aecbff3 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -41,6 +41,7 @@ function makeProxySnapshot(obj) { export const devtools = { emit: (type, data) => { + if (!window['__HORIZON_DEV_HOOK__']) return; console.log('store snapshot:', makeStoreSnapshot({ type, data })); window.postMessage({ type: 'HORIZON_DEV_TOOLS', diff --git a/libs/horizon/src/horizonx/proxy/HooklessObserver.ts b/libs/horizon/src/horizonx/proxy/HooklessObserver.ts index ba390a3a..dd5015e9 100644 --- a/libs/horizon/src/horizonx/proxy/HooklessObserver.ts +++ b/libs/horizon/src/horizonx/proxy/HooklessObserver.ts @@ -19,28 +19,28 @@ import type { IObserver } from './Observer'; * 一个对象(对象、数组、集合)对应一个Observer */ export class HooklessObserver implements IObserver { - listeners: (() => void)[] = []; + listeners: ((mutation) => void)[] = []; useProp(key: string | symbol): void {} - addListener(listener: () => void) { + addListener(listener: (mutation) => void) { this.listeners.push(listener); } - removeListener(listener: () => void) { + removeListener(listener: (mutation) => void) { this.listeners = this.listeners.filter(item => item != listener); } - setProp(key: string | symbol): void { - this.triggerChangeListeners(); + setProp(key: string | symbol, mutation: any): void { + this.triggerChangeListeners(mutation); } - triggerChangeListeners(): void { + triggerChangeListeners(mutation: any): void { this.listeners.forEach(listener => { if (!listener) { return; } - listener(); + listener(mutation); }); } diff --git a/libs/horizon/src/horizonx/proxy/Observer.ts b/libs/horizon/src/horizonx/proxy/Observer.ts index 0db792a0..2f3b15f8 100644 --- a/libs/horizon/src/horizonx/proxy/Observer.ts +++ b/libs/horizon/src/horizonx/proxy/Observer.ts @@ -24,9 +24,9 @@ export interface IObserver { removeListener: (listener: () => void) => void; - setProp: (key: string) => void; + setProp: (key: string, mutation: any) => void; - triggerChangeListeners: () => void; + triggerChangeListeners: (mutation: any) => void; triggerUpdate: (vNode: any) => void; @@ -43,9 +43,9 @@ export class Observer implements IObserver { keyVNodes = new Map(); - listeners: (() => void)[] = []; + listeners: ((mutation) => void)[] = []; - watchers = {} as { [key: string]: ((key: string, oldValue: any, newValue: any) => void)[] }; + watchers = {} as { [key: string]: ((key: string, oldValue: any, newValue: any, mutation: any) => void)[] }; // 对象的属性被使用时调用 useProp(key: string | symbol): void { @@ -76,7 +76,7 @@ export class Observer implements IObserver { } // 对象的属性被赋值时调用 - setProp(key: string | symbol): void { + setProp(key: string | symbol, mutation: any): void { const vNodes = this.keyVNodes.get(key); vNodes?.forEach((vNode: VNode) => { if (vNode.isStoreChange) { @@ -89,7 +89,7 @@ export class Observer implements IObserver { this.triggerUpdate(vNode); }); - this.triggerChangeListeners(); + this.triggerChangeListeners(mutation); } triggerUpdate(vNode: VNode): void { @@ -101,16 +101,16 @@ export class Observer implements IObserver { launchUpdateFromVNode(vNode); } - addListener(listener: () => void): void { + addListener(listener: (mutation) => void): void { this.listeners.push(listener); } - removeListener(listener: () => void): void { + removeListener(listener: (mutation) => void): void { this.listeners = this.listeners.filter(item => item != listener); } - triggerChangeListeners(): void { - this.listeners.forEach(listener => listener()); + triggerChangeListeners(mutation: any): void { + this.listeners.forEach(listener => listener(mutation)); } // 触发所有使用的props的VNode更新 @@ -118,7 +118,7 @@ export class Observer implements IObserver { const keyIt = this.keyVNodes.keys(); let keyItem = keyIt.next(); while (!keyItem.done) { - this.setProp(keyItem.value); + this.setProp(keyItem.value, {}); keyItem = keyIt.next(); } } diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index 8279512f..672253f8 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -16,6 +16,7 @@ import { getObserver } from '../ProxyHandler'; import { isSame, isValidIntegerKey } from '../../CommonUtils'; import { get as objectGet } from './ObjectProxyHandler'; +import { resolveMutation } from '../../CommonUtils'; export function createArrayProxy(rawObj: any[]): any[] { const handle = { @@ -53,6 +54,8 @@ function set(rawObj: any[], key: string, value: any, receiver: any) { const oldLength = rawObj.length; const newValue = value; + const oldArray = JSON.parse(JSON.stringify(rawObj)); + const ret = Reflect.set(rawObj, key, newValue, receiver); const newLength = rawObj.length; @@ -62,17 +65,17 @@ function set(rawObj: any[], key: string, value: any, receiver: any) { // 值不一样,触发监听器 if (observer.watchers?.[key]) { observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue); + cb(key, oldValue, newValue, resolveMutation(oldArray, rawObj)); }); } // 触发属性变化 - observer.setProp(key); + observer.setProp(key, resolveMutation(oldValue, rawObj)); } if (oldLength !== newLength) { // 触发数组的大小变化 - observer.setProp('length'); + observer.setProp('length', resolveMutation(oldValue, rawObj)); } return ret; diff --git a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts index dad28366..74fa57bc 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts @@ -15,6 +15,7 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { isMap, isWeakMap, isSame } from '../../CommonUtils'; +import { resolveMutation } from '../../CommonUtils'; const COLLECTION_CHANGE = '_collectionChange'; const handler = { @@ -91,17 +92,17 @@ function set( const observer = getObserver(rawObj); if (valChange || !rawObj.has(key)) { - observer.setProp(COLLECTION_CHANGE); + observer.setProp(COLLECTION_CHANGE, resolveMutation(oldValue, rawObj)); } if (valChange) { if (observer.watchers?.[key]) { observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue); + cb(key, oldValue, newValue, resolveMutation(oldValue, rawObj)); }); } - observer.setProp(key); + observer.setProp(key, resolveMutation(oldValue, rawObj)); } return rawObj; @@ -109,12 +110,13 @@ function set( // Set的add方法 function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object { + const oldCollection = JSON.parse(JSON.stringify(rawObj)); if (!rawObj.has(value)) { rawObj.add(value); const observer = getObserver(rawObj); - observer.setProp(value); - observer.setProp(COLLECTION_CHANGE); + observer.setProp(value, resolveMutation(oldCollection, rawObj)); + observer.setProp(COLLECTION_CHANGE, resolveMutation(oldCollection, rawObj)); } return rawObj; @@ -138,12 +140,13 @@ function clear(rawObj: { size: number; clear: () => void }) { } function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) { + const oldCollection = JSON.parse(JSON.stringify(rawObj)); if (rawObj.has(key)) { rawObj.delete(key); const observer = getObserver(rawObj); - observer.setProp(key); - observer.setProp(COLLECTION_CHANGE); + observer.setProp(key, resolveMutation(oldCollection, rawObj)); + observer.setProp(COLLECTION_CHANGE, resolveMutation(oldCollection, rawObj)); return true; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index b2eba435..9e0aac15 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import { isSame } from '../../CommonUtils'; +import { isSame, resolveMutation } from '../../CommonUtils'; import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { OBSERVER_KEY } from '../../Constants'; @@ -70,8 +70,7 @@ export function get(rawObj: object, key: string | symbol, receiver: any, singleL } export function set(rawObj: object, key: string, value: any, receiver: any): boolean { - console.log('ObjectProxyHandler.set()'); - const oldObject = JSON.stringify(rawObj); + const oldObject = JSON.parse(JSON.stringify(rawObj)); const observer = getObserver(rawObj); if (value && key == 'removeListener') { @@ -85,12 +84,10 @@ export function set(rawObj: object, key: string, value: any, receiver: any): boo if (!isSame(newValue, oldValue)) { if (observer.watchers?.[key]) { observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue); + cb(key, oldValue, newValue, resolveMutation(oldObject, rawObj)); }); } - observer.setProp(key); + observer.setProp(key, resolveMutation(oldObject, rawObj)); } - - console.log('mutation from: ', JSON.parse(oldObject), ' to: ', ret); return ret; } diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index dec1a61a..42db626d 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -191,9 +191,10 @@ export function createStore, C extend store: storeObj, }); - storeObj.$subscribe(() => { + proxyObj.addListener(mutation => { devtools.emit(STATE_CHANGE, { store: storeObj, + mutation, }); }); diff --git a/libs/horizon/src/horizonx/types.d.ts b/libs/horizon/src/horizonx/types.d.ts index f1a88e56..eec4a826 100644 --- a/libs/horizon/src/horizonx/types.d.ts +++ b/libs/horizon/src/horizonx/types.d.ts @@ -16,13 +16,13 @@ export interface IObserver { useProp: (key: string | symbol) => void; - addListener: (listener: () => void) => void; + addListener: (listener: (mutation: any) => void) => void; - removeListener: (listener: () => void) => void; + removeListener: (listener: (mutation: any) => void) => void; - setProp: (key: string | symbol) => void; + setProp: (key: string | symbol, mutation: any) => void; - triggerChangeListeners: () => void; + triggerChangeListeners: (mutation: any) => void; triggerUpdate: (vNode: any) => void; @@ -42,13 +42,13 @@ export type StoreConfig, C extends Us }; export type UserActions = { - [K: string]: ActionFunction + [K: string]: ActionFunction; }; type ActionFunction = (this: StoreObj, state: S, ...args: any[]) => any; export type StoreActions> = { - [K in keyof A]: Action + [K in keyof A]: Action; }; type Action, S extends object> = ( @@ -61,8 +61,8 @@ export type StoreObj, C extends UserC $a: StoreActions; $c: UserComputedValues; $queue: QueuedStoreActions; - $subscribe: (listener: () => void) => void; - $unsubscribe: (listener: () => void) => void; + $subscribe: (listener: (mutation) => void) => void; + $unsubscribe: (listener: (mutation) => void) => void; } & { [K in keyof S]: S[K] } & { [K in keyof A]: Action } & { [K in keyof C]: ReturnType }; export type PlannedAction> = { @@ -74,11 +74,11 @@ export type PlannedAction> = { type RemoveFirstFromTuple = T['length'] extends 0 ? [] : ((...b: T) => void) extends (a, ...b: infer I) => void - ? I - : []; + ? I + : []; export type UserComputedValues = { - [K: string]: ComputedFunction + [K: string]: ComputedFunction; }; type ComputedFunction = (state: S) => any; @@ -89,9 +89,9 @@ export type AsyncAction, S extends object> = ( ) => Promise>; export type QueuedStoreActions> = { - [K in keyof A]: AsyncAction + [K in keyof A]: AsyncAction; }; export type ComputedValues> = { - [K in keyof C]: ReturnType + [K in keyof C]: ReturnType; }; diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/utils.test.js b/scripts/__tests__/HorizonXText/StoreFunctionality/utils.test.js new file mode 100644 index 00000000..7d1f786f --- /dev/null +++ b/scripts/__tests__/HorizonXText/StoreFunctionality/utils.test.js @@ -0,0 +1,80 @@ +import { resolveMutation } from '../../../../libs/horizon/src/horizonx/CommonUtils'; + +describe('Mutation resolve', () => { + it('should resolve mutation different types', () => { + const mutation = resolveMutation(null, 42); + + expect(mutation.mutation).toBe(true); + expect(mutation.from).toBe(null); + expect(mutation.to).toBe(42); + }); + + it('should resolve mutation same type types, different values', () => { + const mutation = resolveMutation(13, 42); + + expect(mutation.mutation).toBe(true); + expect(mutation.from).toBe(13); + expect(mutation.to).toBe(42); + }); + + it('should resolve mutation same type types, same values', () => { + const mutation = resolveMutation(42, 42); + + expect(mutation.mutation).toBe(false); + expect(Object.keys(mutation).length).toBe(1); + }); + + it('should resolve mutation same type types, same objects', () => { + const mutation = resolveMutation({ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } }); + + expect(mutation.mutation).toBe(false); + }); + + it('should resolve mutation same type types, same array', () => { + const mutation = resolveMutation([1, 2, 3, 4, 5], [1, 2, 3, 4, 5]); + + expect(mutation.mutation).toBe(false); + }); + + it('should resolve mutation same type types, longer array', () => { + const mutation = resolveMutation([1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6]); + + expect(mutation.mutation).toBe(true); + expect(mutation.items[5].mutation).toBe(true); + expect(mutation.items[5].to).toBe(6); + }); + + it('should resolve mutation same type types, shorter array', () => { + const mutation = resolveMutation([1, 2, 3, 4, 5], [1, 2, 3, 4]); + + expect(mutation.mutation).toBe(true); + expect(mutation.items[4].mutation).toBe(true); + expect(mutation.items[4].from).toBe(5); + }); + + it('should resolve mutation same type types, changed array', () => { + const mutation = resolveMutation([1, 2, 3, 4, 5], [1, 2, 3, 4, 'a']); + + expect(mutation.mutation).toBe(true); + expect(mutation.items[4].mutation).toBe(true); + expect(mutation.items[4].from).toBe(5); + expect(mutation.items[4].to).toBe('a'); + }); + + it('should resolve mutation same type types, same object', () => { + const mutation = resolveMutation({ a: 1, b: 2 }, { a: 1, b: 2 }); + + console.log(mutation); + expect(mutation.mutation).toBe(false); + }); + + it('should resolve mutation same type types, changed object', () => { + const mutation = resolveMutation({ a: 1, b: 2, c: 3 }, { a: 1, c: 2 }); + + expect(mutation.mutation).toBe(true); + expect(mutation.attributes.a.mutation).toBe(false); + expect(mutation.attributes.b.mutation).toBe(true); + expect(mutation.attributes.b.from).toBe(2); + expect(mutation.attributes.c.to).toBe(2); + }); +}); From 0b11860c9d92ec06c84948f40a32252f6b0cec65 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 30 Nov 2022 23:00:15 +0800 Subject: [PATCH 12/74] Match-id-760cbbb23b2babb6aab29450b9834f032fec4914 --- .../src/horizonx/devtools/constants.ts | 2 + libs/horizon/src/horizonx/devtools/index.ts | 69 ++++++++++++++++++- libs/horizon/src/horizonx/proxy/Observer.ts | 27 +++++++- .../src/horizonx/proxy/ProxyHandler.ts | 2 +- .../proxy/handlers/ArrayProxyHandler.ts | 11 +-- .../proxy/handlers/CollectionProxyHandler.ts | 27 +++++--- .../proxy/handlers/ObjectProxyHandler.ts | 8 ++- .../src/horizonx/store/StoreHandler.ts | 23 +++++-- 8 files changed, 140 insertions(+), 29 deletions(-) diff --git a/libs/horizon/src/horizonx/devtools/constants.ts b/libs/horizon/src/horizonx/devtools/constants.ts index df41626f..3e105125 100644 --- a/libs/horizon/src/horizonx/devtools/constants.ts +++ b/libs/horizon/src/horizonx/devtools/constants.ts @@ -6,3 +6,5 @@ export const ACTION = 'horizonx action'; export const ACTION_QUEUED = 'horizonx action queued'; export const QUEUE_PENDING = 'horizonx queue pending'; export const QUEUE_FINISHED = 'horizonx queue finished'; +export const RENDER_TRIGGERED = 'horizonx render triggered'; +export const OBSERVED_COMPONENTS = 'horizonx observed components'; diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 5aecbff3..7484fa88 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -1,5 +1,12 @@ +import { getStore, getAllStores } from '../store/StoreHandler'; +import { OBSERVED_COMPONENTS } from './constants'; + const sessionId = Date.now(); +export function isPanelActive() { + return window['__HORIZON_DEV_HOOK__']; +} + function makeStoreSnapshot({ type, data }) { const expanded = {}; Object.keys(data.store.$c).forEach(key => { @@ -40,9 +47,12 @@ function makeProxySnapshot(obj) { } export const devtools = { + getVNodeId: vNode => { + if (!isPanelActive()) return; + getVNodeId(vNode); + }, emit: (type, data) => { - if (!window['__HORIZON_DEV_HOOK__']) return; - console.log('store snapshot:', makeStoreSnapshot({ type, data })); + if (!isPanelActive()) return; window.postMessage({ type: 'HORIZON_DEV_TOOLS', payload: makeStoreSnapshot({ type, data }), @@ -50,3 +60,58 @@ export const devtools = { }); }, }; + +function getAffectedComponents() { + const allStores = getAllStores(); + const keys = Object.keys(allStores); + let res = {}; + keys.forEach(key => { + const subRes = new Set(); + const process = Array.from(allStores[key].$config.state._horizonObserver.keyVNodes.values()); + while (process.length) { + let pivot = process.shift(); + if (pivot?.tag) subRes.add(pivot); + if (pivot?.toString() === '[object Set]') Array.from(pivot).forEach(item => process.push(item)); + } + res[key] = Array.from(subRes).map(vnode => { + return { + name: vnode?.type + .toString() + .replace(/\{.*\}/gms, '{...}') + .replace('function ', ''), + nodeId: window.__HORIZON_DEV_HOOK__.getVnodeId(vnode), + }; + }); + }); + + return res; +} + +window.addEventListener('message', messageEvent => { + if (messageEvent.data.payload.type === 'horizonx request observed components') { + // get observed components + setTimeout(() => { + window.postMessage({ + type: 'HORIZON_DEV_TOOLS', + payload: { type: OBSERVED_COMPONENTS, data: getAffectedComponents() }, + from: 'dev tool hook', + }); + }, 100); + } + + if (messageEvent.data.payload.type === 'horizonx executue action') { + const data = messageEvent.data.payload.data; + const store = getStore(data.storeId); + if (!store?.[data.action]) { + } + + const action = store[data.action]; + const params = data.params; + action(...params).bind(store); + } +}); + +export function getVNodeId(vNode) { + window['__HORIZON_DEV_HOOK__'].send(); + return window['__HORIZON_DEV_HOOK__'].getVnodeId(vNode); +} diff --git a/libs/horizon/src/horizonx/proxy/Observer.ts b/libs/horizon/src/horizonx/proxy/Observer.ts index 2f3b15f8..adef63fc 100644 --- a/libs/horizon/src/horizonx/proxy/Observer.ts +++ b/libs/horizon/src/horizonx/proxy/Observer.ts @@ -16,6 +16,7 @@ import { launchUpdateFromVNode } from '../../renderer/TreeBuilder'; import { getProcessingVNode } from '../../renderer/GlobalVar'; import { VNode } from '../../renderer/vnode/VNode'; +import { devtools } from '../devtools'; export interface IObserver { useProp: (key: string) => void; @@ -89,7 +90,8 @@ export class Observer implements IObserver { this.triggerUpdate(vNode); }); - this.triggerChangeListeners(mutation); + // NOTE: mutations are different in dev and production. + this.triggerChangeListeners({ mutation, vNodes }); } triggerUpdate(vNode: VNode): void { @@ -109,8 +111,27 @@ export class Observer implements IObserver { this.listeners = this.listeners.filter(item => item != listener); } - triggerChangeListeners(mutation: any): void { - this.listeners.forEach(listener => listener(mutation)); + triggerChangeListeners({ mutation, vNodes }): void { + const nodesList = vNodes ? Array.from(vNodes) : []; + this.listeners.forEach(listener => + listener({ + mutation, + vNodes: nodesList.map(vNode => { + let realNode = vNode.realNode; + let searchedNode = vNode; + while (!realNode) { + searchedNode = searchedNode.child; + realNode = searchedNode.realNode; + } + return { + type: vNode?.type?.name, + id: devtools.getVNodeId(vNode), + path: vNode.path, + element: realNode?.outerHTML?.substr(0, 100), + }; + }), + }) + ); } // 触发所有使用的props的VNode更新 diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index ccc54ab1..fda6d653 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -27,7 +27,7 @@ const proxyMap = new WeakMap(); export const hookObserverMap = new WeakMap(); -export function createProxy(rawObj: any, isHookObserver = true): any { +export function createProxy(rawObj: any, id, isHookObserver = true): any { // 不是对象(是原始数据类型)不用代理 if (!(rawObj && isObject(rawObj))) { return rawObj; diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index 672253f8..9e9c2845 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -17,6 +17,7 @@ import { getObserver } from '../ProxyHandler'; import { isSame, isValidIntegerKey } from '../../CommonUtils'; import { get as objectGet } from './ObjectProxyHandler'; import { resolveMutation } from '../../CommonUtils'; +import { isPanelActive } from '../../devtools'; export function createArrayProxy(rawObj: any[]): any[] { const handle = { @@ -54,28 +55,30 @@ function set(rawObj: any[], key: string, value: any, receiver: any) { const oldLength = rawObj.length; const newValue = value; - const oldArray = JSON.parse(JSON.stringify(rawObj)); + const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; const ret = Reflect.set(rawObj, key, newValue, receiver); const newLength = rawObj.length; const observer = getObserver(rawObj); + const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : { mutation: true, from: [], to: rawObj }; + if (!isSame(newValue, oldValue)) { // 值不一样,触发监听器 if (observer.watchers?.[key]) { observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, resolveMutation(oldArray, rawObj)); + cb(key, oldValue, newValue, mutation); }); } // 触发属性变化 - observer.setProp(key, resolveMutation(oldValue, rawObj)); + observer.setProp(key, mutation); } if (oldLength !== newLength) { // 触发数组的大小变化 - observer.setProp('length', resolveMutation(oldValue, rawObj)); + observer.setProp('length', mutation); } return ret; diff --git a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts index 74fa57bc..05174cc5 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts @@ -16,6 +16,7 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { isMap, isWeakMap, isSame } from '../../CommonUtils'; import { resolveMutation } from '../../CommonUtils'; +import { isPanelActive } from '../../devtools'; const COLLECTION_CHANGE = '_collectionChange'; const handler = { @@ -91,18 +92,20 @@ function set( const valChange = !isSame(newValue, oldValue); const observer = getObserver(rawObj); + const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : { mutation: true, from: null, to: rawObj }; + if (valChange || !rawObj.has(key)) { - observer.setProp(COLLECTION_CHANGE, resolveMutation(oldValue, rawObj)); + observer.setProp(COLLECTION_CHANGE, mutation); } if (valChange) { if (observer.watchers?.[key]) { observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, resolveMutation(oldValue, rawObj)); + cb(key, oldValue, newValue, mutation); }); } - observer.setProp(key, resolveMutation(oldValue, rawObj)); + observer.setProp(key, mutation); } return rawObj; @@ -110,13 +113,16 @@ function set( // Set的add方法 function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object { - const oldCollection = JSON.parse(JSON.stringify(rawObj)); + const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; if (!rawObj.has(value)) { rawObj.add(value); const observer = getObserver(rawObj); - observer.setProp(value, resolveMutation(oldCollection, rawObj)); - observer.setProp(COLLECTION_CHANGE, resolveMutation(oldCollection, rawObj)); + const mutation = isPanelActive() + ? resolveMutation(oldCollection, rawObj) + : { mutation: true, from: null, to: rawObj }; + observer.setProp(value, mutation); + observer.setProp(COLLECTION_CHANGE, mutation); } return rawObj; @@ -140,13 +146,16 @@ function clear(rawObj: { size: number; clear: () => void }) { } function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) { - const oldCollection = JSON.parse(JSON.stringify(rawObj)); + const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; if (rawObj.has(key)) { rawObj.delete(key); const observer = getObserver(rawObj); - observer.setProp(key, resolveMutation(oldCollection, rawObj)); - observer.setProp(COLLECTION_CHANGE, resolveMutation(oldCollection, rawObj)); + const mutation = isPanelActive() + ? resolveMutation(oldCollection, rawObj) + : { mutation: true, from: null, to: rawObj }; + observer.setProp(key, mutation); + observer.setProp(COLLECTION_CHANGE, mutation); return true; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 9e0aac15..80da37fd 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -16,6 +16,7 @@ import { isSame, resolveMutation } from '../../CommonUtils'; import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { OBSERVER_KEY } from '../../Constants'; +import { isPanelActive } from '../../devtools'; export function createObjectProxy(rawObj: T, singleLevel = false): ProxyHandler { const proxy = new Proxy(rawObj, { @@ -70,7 +71,7 @@ export function get(rawObj: object, key: string | symbol, receiver: any, singleL } export function set(rawObj: object, key: string, value: any, receiver: any): boolean { - const oldObject = JSON.parse(JSON.stringify(rawObj)); + const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; const observer = getObserver(rawObj); if (value && key == 'removeListener') { @@ -80,14 +81,15 @@ export function set(rawObj: object, key: string, value: any, receiver: any): boo const newValue = value; const ret = Reflect.set(rawObj, key, newValue, receiver); + const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : { mutation: true, from: null, to: rawObj }; if (!isSame(newValue, oldValue)) { if (observer.watchers?.[key]) { observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, resolveMutation(oldObject, rawObj)); + cb(key, oldValue, newValue, mutation); }); } - observer.setProp(key, resolveMutation(oldObject, rawObj)); + observer.setProp(key, mutation); } return ret; } diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index 42db626d..8b3aa154 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -14,7 +14,7 @@ */ import { useEffect, useRef } from '../../renderer/hooks/HookExternal'; -import { getProcessingVNode } from '../../renderer/GlobalVar'; +import { getProcessingVNode, getStartVNode } from '../../renderer/GlobalVar'; import { createProxy } from '../proxy/ProxyHandler'; import readonlyProxy from '../proxy/readonlyProxy'; import { Observer } from '../proxy/Observer'; @@ -60,7 +60,9 @@ export function createStore, C extend throw new Error('store obj must be pure object'); } - const proxyObj = createProxy(config.state, !config.options?.isReduxAdapter); + const id = config.id || idGenerator.get('UNNAMED_STORE'); + + const proxyObj = createProxy(config.state, id, !config.options?.isReduxAdapter); proxyObj.$pending = false; @@ -68,6 +70,7 @@ export function createStore, C extend const $queue: Partial> = {}; const $c: Partial> = {}; const storeObj = { + id, $s: proxyObj, $a: $a as StoreActions, $c: $c as ComputedValues, @@ -183,18 +186,16 @@ export function createStore, C extend }); } - if (config.id) { - storeMap.set(config.id, storeObj); - } + storeMap.set(id, storeObj); devtools.emit(INITIALIZED, { store: storeObj, }); - proxyObj.addListener(mutation => { + proxyObj.addListener(change => { devtools.emit(STATE_CHANGE, { store: storeObj, - mutation, + change, }); }); @@ -299,6 +300,14 @@ export function useStore, C extends U return storeObj as StoreObj; } +export function getStore(id: string) { + return storeMap.get(id); +} + +export function getAllStores() { + return Object.fromEntries(storeMap); +} + export function clearStore(id: string): void { storeMap.delete(id); } From 40160ad11b651852b4fe799c1eb04f64e0d9d70f Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 7 Dec 2022 18:12:30 +0800 Subject: [PATCH 13/74] Match-id-e808ef3ee3be61db0d3fa07658d3e094257efd66 --- libs/horizon/src/horizonx/devtools/index.ts | 23 ++++++++++++--------- libs/horizon/src/horizonx/proxy/Observer.ts | 4 +++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 7484fa88..e7f3514e 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -3,10 +3,12 @@ import { OBSERVED_COMPONENTS } from './constants'; const sessionId = Date.now(); +// this function is used to detect devtool connection export function isPanelActive() { return window['__HORIZON_DEV_HOOK__']; } +// serializes store and creates expanded object with baked-in containing current computed values function makeStoreSnapshot({ type, data }) { const expanded = {}; Object.keys(data.store.$c).forEach(key => { @@ -21,6 +23,7 @@ function makeStoreSnapshot({ type, data }) { return snapshot; } +// safely serializes variables containing values wrapped in Proxy object function makeProxySnapshot(obj) { let clone; try { @@ -47,10 +50,13 @@ function makeProxySnapshot(obj) { } export const devtools = { + // returns vNode id from horizon devtools getVNodeId: vNode => { if (!isPanelActive()) return; - getVNodeId(vNode); + window['__HORIZON_DEV_HOOK__'].send(); // update list first + return window['__HORIZON_DEV_HOOK__'].getVnodeId(vNode); }, + // sends horizonx devtool message to extension emit: (type, data) => { if (!isPanelActive()) return; window.postMessage({ @@ -61,6 +67,7 @@ export const devtools = { }, }; +// collects components that are dependant on horizonx store and their ids function getAffectedComponents() { const allStores = getAllStores(); const keys = Object.keys(allStores); @@ -87,8 +94,9 @@ function getAffectedComponents() { return res; } +// listens to messages from background window.addEventListener('message', messageEvent => { - if (messageEvent.data.payload.type === 'horizonx request observed components') { + if (messageEvent?.data?.payload?.type === 'horizonx request observed components') { // get observed components setTimeout(() => { window.postMessage({ @@ -99,19 +107,14 @@ window.addEventListener('message', messageEvent => { }, 100); } + // executes store action if (messageEvent.data.payload.type === 'horizonx executue action') { const data = messageEvent.data.payload.data; const store = getStore(data.storeId); - if (!store?.[data.action]) { - } + if (!store?.[data.action]) return; const action = store[data.action]; const params = data.params; - action(...params).bind(store); + action(...params); } }); - -export function getVNodeId(vNode) { - window['__HORIZON_DEV_HOOK__'].send(); - return window['__HORIZON_DEV_HOOK__'].getVnodeId(vNode); -} diff --git a/libs/horizon/src/horizonx/proxy/Observer.ts b/libs/horizon/src/horizonx/proxy/Observer.ts index adef63fc..b1aa43d7 100644 --- a/libs/horizon/src/horizonx/proxy/Observer.ts +++ b/libs/horizon/src/horizonx/proxy/Observer.ts @@ -79,7 +79,9 @@ export class Observer implements IObserver { // 对象的属性被赋值时调用 setProp(key: string | symbol, mutation: any): void { const vNodes = this.keyVNodes.get(key); - vNodes?.forEach((vNode: VNode) => { + //NOTE: using Set directly can lead to deadlock + const vNodeArray = Array.from(vNodes || []); + vNodeArray?.forEach((vNode: VNode) => { if (vNode.isStoreChange) { // VNode已经被触发过,不再重复触发 return; From 4712e8a621f3178f30b70a3d90eee6f3d7dbe6d1 Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 10 Jan 2023 19:55:01 +0800 Subject: [PATCH 14/74] Match-id-8c8edc92392dcae4fa155fe80ecae735f6f4d0fc --- libs/horizon/src/renderer/components/BaseClassComponent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/horizon/src/renderer/components/BaseClassComponent.ts b/libs/horizon/src/renderer/components/BaseClassComponent.ts index fff07c8d..8a931e04 100644 --- a/libs/horizon/src/renderer/components/BaseClassComponent.ts +++ b/libs/horizon/src/renderer/components/BaseClassComponent.ts @@ -29,7 +29,7 @@ class Component { this.context = context; } - setState(state: S) { + setState(state: S, callback?: any) { if (isDev) { console.error('Cant not call `this.setState` in the constructor of class component, it will do nothing'); } From 1c3c678883fa3aeb5afb8c987429bc9151326a5e Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 10 Jan 2023 20:09:54 +0800 Subject: [PATCH 15/74] Match-id-9952e9ebc64f02cfbab95841eee1b1788348e9f6 --- libs/horizon/src/renderer/Types.ts | 2 ++ libs/horizon/src/renderer/UpdateHandler.ts | 4 +--- libs/horizon/src/renderer/components/BaseClassComponent.ts | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libs/horizon/src/renderer/Types.ts b/libs/horizon/src/renderer/Types.ts index 1503fee9..91b6bfbd 100644 --- a/libs/horizon/src/renderer/Types.ts +++ b/libs/horizon/src/renderer/Types.ts @@ -77,3 +77,5 @@ export type Source = { fileName: string; lineNumber: number; }; + +export type Callback = () => void; diff --git a/libs/horizon/src/renderer/UpdateHandler.ts b/libs/horizon/src/renderer/UpdateHandler.ts index c906bd1c..5fd17bb1 100644 --- a/libs/horizon/src/renderer/UpdateHandler.ts +++ b/libs/horizon/src/renderer/UpdateHandler.ts @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import type { VNode } from './Types'; +import type { VNode, Callback } from './Types'; import { FlagUtils, ShouldCapture } from './vnode/VNodeFlags'; export type Update = { @@ -22,8 +22,6 @@ export type Update = { callback: Callback | null; }; -export type Callback = () => any; - export type Updates = Array | null; export enum UpdateState { diff --git a/libs/horizon/src/renderer/components/BaseClassComponent.ts b/libs/horizon/src/renderer/components/BaseClassComponent.ts index 8a931e04..de4ff351 100644 --- a/libs/horizon/src/renderer/components/BaseClassComponent.ts +++ b/libs/horizon/src/renderer/components/BaseClassComponent.ts @@ -13,6 +13,8 @@ * See the Mulan PSL v2 for more details. */ +import {Callback} from '../Types'; + /** * Component的api setState和forceUpdate在实例生成阶段实现 */ @@ -29,7 +31,7 @@ class Component { this.context = context; } - setState(state: S, callback?: any) { + setState(state: S, callback?: Callback) { if (isDev) { console.error('Cant not call `this.setState` in the constructor of class component, it will do nothing'); } From 67d39ec1002a1695d2a211602f018b6c8f18d129 Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 12 Jan 2023 17:47:54 +0800 Subject: [PATCH 16/74] Match-id-7cdd603ac18c622fdf10e6266e101785657b3f47 --- libs/horizon/jsx-runtime.ts | 24 ++++++++++++++++ libs/horizon/src/external/JSXElement.ts | 9 ++++++ scripts/rollup/rollup.config.js | 37 +++++++++++++++++-------- 3 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 libs/horizon/jsx-runtime.ts diff --git a/libs/horizon/jsx-runtime.ts b/libs/horizon/jsx-runtime.ts new file mode 100644 index 00000000..40b7fd86 --- /dev/null +++ b/libs/horizon/jsx-runtime.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { + TYPE_FRAGMENT as Fragment, +} from './src/external/JSXElementType'; +import { jsx } from './src/external/JSXElement'; + +export { + jsx, + Fragment +}; diff --git a/libs/horizon/src/external/JSXElement.ts b/libs/horizon/src/external/JSXElement.ts index ebb43d18..a4cf50b7 100644 --- a/libs/horizon/src/external/JSXElement.ts +++ b/libs/horizon/src/external/JSXElement.ts @@ -107,3 +107,12 @@ export function cloneElement(element, setting, ...children) { export function isValidElement(element) { return !!(element && element.vtype === TYPE_COMMON_ELEMENT); } + +// 兼容高版本的babel编译方式 +export function jsx(type, setting, key) { + if (setting.key === undefined && key !== undefined) { + setting.key = key; + } + + return buildElement(false, type, setting, []); +} diff --git a/scripts/rollup/rollup.config.js b/scripts/rollup/rollup.config.js index 05bd691e..57e17279 100644 --- a/scripts/rollup/rollup.config.js +++ b/scripts/rollup/rollup.config.js @@ -37,6 +37,18 @@ if (!fs.existsSync(outDir)) { } const outputResolve = (...p) => path.resolve(outDir, ...p); +const BasicPlugins = [ + nodeResolve({ + extensions, + modulesOnly: true, + }), + babel({ + exclude: 'node_modules/**', + configFile: path.join(__dirname, '../../babel.config.js'), + babelHelpers: 'runtime', + extensions, + }) +]; function getOutputName(mode) { return mode === 'production' ? `horizon.${mode}.min.js` : `horizon.${mode}.js`; @@ -61,16 +73,7 @@ function genConfig(mode) { }, ], plugins: [ - nodeResolve({ - extensions, - modulesOnly: true, - }), - babel({ - exclude: 'node_modules/**', - configFile: path.join(__dirname, '../../babel.config.js'), - babelHelpers: 'runtime', - extensions, - }), + ...BasicPlugins, replace({ values: { 'process.env.NODE_ENV': `"${mode}"`, @@ -96,4 +99,16 @@ function genConfig(mode) { }; } -export default [genConfig('development'), genConfig('production')]; +function genJSXRuntimeConfig() { + return { + input: path.resolve(libDir, 'jsx-runtime.ts'), + output: [ + { + file: outputResolve('jsx-runtime.js'), + format: 'cjs', + } + ], + plugins: BasicPlugins + }; +} +export default [genConfig('development'), genConfig('production'), genJSXRuntimeConfig()]; From cadaabfd3b863bb76438f2a980548ee115ab78d1 Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 12 Jan 2023 17:48:28 +0800 Subject: [PATCH 17/74] Match-id-1f58ff819dfbd01d0b80fd9bfaa198bcb73f0b50 --- .../src/renderer/taskExecutor/BrowserAsync.ts | 33 ++++++++++++++----- .../src/renderer/taskExecutor/TaskExecutor.ts | 7 +--- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts b/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts index 28e17a84..2aeead9f 100644 --- a/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts +++ b/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts @@ -19,11 +19,9 @@ let isMessageLoopRunning = false; let browserCallback = null; -const { port1, port2 } = new MessageChannel(); - -export function isOverTime() { - return false; -} +let port1 = null; +let port2 = null; +let isTestRuntime = false; // 1、设置deadline;2、回调TaskExecutor传过来的browserCallback const callRenderTasks = () => { @@ -41,21 +39,38 @@ const callRenderTasks = () => { browserCallback = null; } else { // 还有task,继续调用 - port2.postMessage(null); + asyncCall(); } } catch (error) { - port2.postMessage(null); + asyncCall(); throw error; } }; -port1.onmessage = callRenderTasks; +if (typeof MessageChannel === 'function') { + const mc = new MessageChannel(); + port1 = mc.port1; + port1.onmessage = callRenderTasks; + port2 = mc.port2; +} else { + // 测试环境没有 MessageChannel + isTestRuntime = true; +} + +function asyncCall() { + if (isTestRuntime) { + setTimeout(callRenderTasks, 0); + } else { + port2.postMessage(null); + } +} export function requestBrowserCallback(callback) { browserCallback = callback; if (!isMessageLoopRunning) { isMessageLoopRunning = true; - port2.postMessage(null); + asyncCall(); } } + diff --git a/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts b/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts index 8613cfca..ac5ce9b3 100644 --- a/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts +++ b/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts @@ -18,7 +18,7 @@ */ import { Node } from '../taskExecutor/TaskQueue'; -import { requestBrowserCallback, isOverTime } from './BrowserAsync'; +import { requestBrowserCallback } from './BrowserAsync'; import { add, shift, first, remove } from './TaskQueue'; @@ -44,11 +44,6 @@ function callTasks() { // 循环执行task while (task !== null) { - if (isOverTime()) { - // 超过了deadline - break; - } - const callback = task.callback; if (callback !== null) { task.callback = null; From 673a63c40daa3afa44b344fba59517e010299333 Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 13 Jan 2023 10:38:17 +0800 Subject: [PATCH 18/74] Match-id-a6a5ea6fabf8fd82802778a00a09be2442e7a0b5 --- libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts | 4 ++++ libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts b/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts index 2aeead9f..6da28613 100644 --- a/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts +++ b/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts @@ -23,6 +23,10 @@ let port1 = null; let port2 = null; let isTestRuntime = false; +export function isOverTime() { + return false; +} + // 1、设置deadline;2、回调TaskExecutor传过来的browserCallback const callRenderTasks = () => { if (browserCallback === null) { diff --git a/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts b/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts index ac5ce9b3..8613cfca 100644 --- a/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts +++ b/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts @@ -18,7 +18,7 @@ */ import { Node } from '../taskExecutor/TaskQueue'; -import { requestBrowserCallback } from './BrowserAsync'; +import { requestBrowserCallback, isOverTime } from './BrowserAsync'; import { add, shift, first, remove } from './TaskQueue'; @@ -44,6 +44,11 @@ function callTasks() { // 循环执行task while (task !== null) { + if (isOverTime()) { + // 超过了deadline + break; + } + const callback = task.callback; if (callback !== null) { task.callback = null; From cdc872be3f6e7c3cf1f3a3b65023569bb6fa5e2d Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 16 Jan 2023 20:04:55 +0800 Subject: [PATCH 19/74] Match-id-a889c6b66e320203447be4d1bcf1011b502b27c4 --- libs/horizon/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/horizon/index.ts b/libs/horizon/index.ts index 59045fbb..f71aa9e6 100644 --- a/libs/horizon/index.ts +++ b/libs/horizon/index.ts @@ -65,6 +65,11 @@ const act = fun => { asyncUpdates(fun); callRenderQueueImmediate(); runAsyncEffects(); + + // 兼容返回Promise + return new Promise((resolve, reject) => { + resolve(null); + }); }; import { From 2aaad576fd2eabefa8eccee6847c0198cf31772a Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 16 Jan 2023 20:29:01 +0800 Subject: [PATCH 20/74] Match-id-13432ea7f850d2d66d77c7fc83bbaf071859351f --- libs/horizon/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libs/horizon/index.ts b/libs/horizon/index.ts index f71aa9e6..737861a7 100644 --- a/libs/horizon/index.ts +++ b/libs/horizon/index.ts @@ -67,9 +67,7 @@ const act = fun => { runAsyncEffects(); // 兼容返回Promise - return new Promise((resolve, reject) => { - resolve(null); - }); + return Promise.resolve(); }; import { From f3c0cf9a05a99a47ecbf69396fb6acb880f49425 Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 19 Jan 2023 16:56:53 +0800 Subject: [PATCH 21/74] Match-id-470122e11f1af7f936f3b0991fd467d8fd693eda --- CHANGELOG.md | 4 ++ libs/horizon/global.d.ts | 1 + libs/horizon/index.ts | 14 +---- libs/horizon/package.json | 2 +- libs/horizon/src/external/JSXElement.ts | 16 +++++- libs/horizon/src/external/TestUtil.ts | 69 +++++++++++++++++++++++ libs/horizon/src/renderer/ErrorHandler.ts | 2 +- 7 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 libs/horizon/src/external/TestUtil.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d71ca87c..2eb75f8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.34 (2023-01-11) +- **core**: #95 新增jsx接口 +- **core**: #96 #97 fix testing-library 的UT错误 + ## 0.0.33 (2023-01-11) - **horizonX-devtool**: 修复IE中报错 diff --git a/libs/horizon/global.d.ts b/libs/horizon/global.d.ts index 34c32508..9da3e079 100644 --- a/libs/horizon/global.d.ts +++ b/libs/horizon/global.d.ts @@ -19,3 +19,4 @@ declare var isDev: boolean; declare var isTest: boolean; declare const __VERSION__: string; +declare var setImmediate: Function; diff --git a/libs/horizon/index.ts b/libs/horizon/index.ts index 737861a7..90d62f6e 100644 --- a/libs/horizon/index.ts +++ b/libs/horizon/index.ts @@ -42,9 +42,6 @@ import { useState, useDebugValue, } from './src/renderer/hooks/HookExternal'; -import { asyncUpdates } from './src/renderer/TreeBuilder'; -import { callRenderQueueImmediate } from './src/renderer/taskExecutor/RenderQueue'; -import { runAsyncEffects } from './src/renderer/submit/HookEffectHandler'; import { isContextProvider, isContextConsumer, @@ -59,16 +56,7 @@ import { import { createStore, useStore, clearStore } from './src/horizonx/store/StoreHandler'; import * as reduxAdapter from './src/horizonx/adapters/redux'; import { watch } from './src/horizonx/proxy/watch'; - -// act用于测试,作用是:如果fun触发了刷新(包含了异步刷新),可以保证在act后面的代码是在刷新完成后才执行。 -const act = fun => { - asyncUpdates(fun); - callRenderQueueImmediate(); - runAsyncEffects(); - - // 兼容返回Promise - return Promise.resolve(); -}; +import { act } from './src/external/TestUtil'; import { render, diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 8b7332a0..aa78f321 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.33", + "version": "0.0.34", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/external/JSXElement.ts b/libs/horizon/src/external/JSXElement.ts index a4cf50b7..614733be 100644 --- a/libs/horizon/src/external/JSXElement.ts +++ b/libs/horizon/src/external/JSXElement.ts @@ -25,10 +25,10 @@ import { Source } from '../renderer/Types'; * props 其他常规属性 */ export function JSXElement(type, key, ref, vNode, props, source: Source | null) { - return { + const ele = { // 元素标识符 vtype: TYPE_COMMON_ELEMENT, - src: isDev ? source : null, + src: null, // 属于元素的内置属性 type: type, @@ -39,6 +39,18 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null) // 所属的class组件 belongClassVNode: vNode, }; + + if (isDev) { + // 为了test判断两个JSXElement对象是否相等时忽略src属性,需要设置src的enumerable为false + Object.defineProperty(ele, 'src', { + configurable: false, + enumerable: false, + writable: false, + value: source, + }); + } + + return ele; } function isValidKey(key) { diff --git a/libs/horizon/src/external/TestUtil.ts b/libs/horizon/src/external/TestUtil.ts new file mode 100644 index 00000000..6eccbbe5 --- /dev/null +++ b/libs/horizon/src/external/TestUtil.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + + +import {asyncUpdates} from '../renderer/TreeBuilder'; +import {callRenderQueueImmediate} from '../renderer/taskExecutor/RenderQueue'; +import {runAsyncEffects} from '../renderer/submit/HookEffectHandler'; +import {isPromise} from '../renderer/ErrorHandler'; + +// act用于测试,作用是:如果fun触发了刷新(包含了异步刷新),可以保证在act后面的代码是在刷新完成后才执行。 +function act(fun) { + const funRet = asyncUpdates(fun); + + callRenderQueueImmediate(); + runAsyncEffects(); + // effects可能产生刷新任务,这里再执行一次 + callRenderQueueImmediate(); + + // 如果fun返回的是Promise + if (isPromise(funRet)) { + // testing-library会返回Promise + return { + then(resolve, reject) { + funRet.then( + () => { + if (typeof setImmediate === 'function') { + // 通过setImmediate回调,用于等待业务的setTimeout完成 + setImmediate(() => { + callRenderQueueImmediate(); + runAsyncEffects(); + resolve(); + }); + } else { + callRenderQueueImmediate(); + runAsyncEffects(); + resolve(); + } + }, + err => { + reject(err); + }, + ); + }, + }; + } else { + return { + then(resolve) { + resolve(); + }, + }; + } + +} + +export { + act +} diff --git a/libs/horizon/src/renderer/ErrorHandler.ts b/libs/horizon/src/renderer/ErrorHandler.ts index af115325..7ba23cec 100644 --- a/libs/horizon/src/renderer/ErrorHandler.ts +++ b/libs/horizon/src/renderer/ErrorHandler.ts @@ -72,7 +72,7 @@ function createClassErrorUpdate(vNode: VNode, error: any): Update { } return update; } -function isPromise(error: any): error is PromiseType { +export function isPromise(error: any): error is PromiseType { return error !== null && typeof error === 'object' && typeof error.then === 'function'; } // 处理capture和bubble阶段抛出的错误 From 8c13846d2def98ba9c0404487cce00514e2c6d9f Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 19 Jan 2023 17:44:36 +0800 Subject: [PATCH 22/74] Match-id-bb9faf6ae4ba719bc43b183906c421855724b0b7 --- libs/horizon/src/external/TestUtil.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/libs/horizon/src/external/TestUtil.ts b/libs/horizon/src/external/TestUtil.ts index 6eccbbe5..26d87660 100644 --- a/libs/horizon/src/external/TestUtil.ts +++ b/libs/horizon/src/external/TestUtil.ts @@ -19,14 +19,15 @@ import {callRenderQueueImmediate} from '../renderer/taskExecutor/RenderQueue'; import {runAsyncEffects} from '../renderer/submit/HookEffectHandler'; import {isPromise} from '../renderer/ErrorHandler'; +interface Thenable { + then(resolve: (val?: any) => void, reject: (err: any) => void): void; +} + // act用于测试,作用是:如果fun触发了刷新(包含了异步刷新),可以保证在act后面的代码是在刷新完成后才执行。 -function act(fun) { +function act(fun: () => void | Thenable): Thenable { const funRet = asyncUpdates(fun); - callRenderQueueImmediate(); - runAsyncEffects(); - // effects可能产生刷新任务,这里再执行一次 - callRenderQueueImmediate(); + callRenderQueue(); // 如果fun返回的是Promise if (isPromise(funRet)) { @@ -38,13 +39,11 @@ function act(fun) { if (typeof setImmediate === 'function') { // 通过setImmediate回调,用于等待业务的setTimeout完成 setImmediate(() => { - callRenderQueueImmediate(); - runAsyncEffects(); + callRenderQueue(); resolve(); }); } else { - callRenderQueueImmediate(); - runAsyncEffects(); + callRenderQueue(); resolve(); } }, @@ -61,9 +60,15 @@ function act(fun) { }, }; } +} +function callRenderQueue() { + callRenderQueueImmediate(); + runAsyncEffects(); + // effects可能产生刷新任务,这里再执行一次 + callRenderQueueImmediate(); } export { act -} +}; From efdc5f5b0a2f66a5187fa758320bbb9a463a2876 Mon Sep 17 00:00:00 2001 From: * <*> Date: Sat, 28 Jan 2023 09:54:07 +0800 Subject: [PATCH 23/74] Match-id-a4d79ffb0f91ea94e459845a9b689e6a7125798f --- CHANGELOG.md | 5 ++++- libs/horizon/package.json | 2 +- libs/horizon/src/external/JSXElement.ts | 11 +++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eb75f8d..542c6277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -## 0.0.34 (2023-01-11) +## 0.0.35 (2023-01-28) +- **core**: 在 cloneDeep JSXElement 的时候会出现死循环 + +## 0.0.34 (2023-01-19) - **core**: #95 新增jsx接口 - **core**: #96 #97 fix testing-library 的UT错误 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index aa78f321..39d033c1 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.34", + "version": "0.0.35", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/external/JSXElement.ts b/libs/horizon/src/external/JSXElement.ts index 614733be..3de212b3 100644 --- a/libs/horizon/src/external/JSXElement.ts +++ b/libs/horizon/src/external/JSXElement.ts @@ -37,11 +37,18 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null) props: props, // 所属的class组件 - belongClassVNode: vNode, + belongClassVNode: null, }; + // 在 cloneDeep JSXElement 的时候会出现死循环,需要设置belongClassVNode的enumerable为false + Object.defineProperty(ele, 'belongClassVNode', { + configurable: false, + enumerable: false, + value: vNode, + }); + if (isDev) { - // 为了test判断两个JSXElement对象是否相等时忽略src属性,需要设置src的enumerable为false + // 为了test判断两个 JSXElement 对象是否相等时忽略src属性,需要设置src的enumerable为false Object.defineProperty(ele, 'src', { configurable: false, enumerable: false, From 9758a9981e496634e50c41806b7416442cbdb95d Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 30 Jan 2023 11:57:45 +0800 Subject: [PATCH 24/74] Match-id-0792823163a9df4d31c3755f318c7ceb06153781 --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- libs/horizon/src/dom/DOMOperator.ts | 10 ++++++++-- libs/horizon/src/dom/utils/DomCreator.ts | 6 +++--- libs/horizon/src/renderer/RootStack.ts | 1 + 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 542c6277..08d2e879 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.36 (2023-01-30) +- **core**: #100 horizon从上层页面透传到iframe页面里使用,创建的dom元素instanceof HTMLElement为false + ## 0.0.35 (2023-01-28) - **core**: 在 cloneDeep JSXElement 的时候会出现死循环 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 39d033c1..20c8dfc2 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.35", + "version": "0.0.36", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index ed3047b8..a7abe22d 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/horizon/src/dom/DOMOperator.ts @@ -16,7 +16,7 @@ import { saveVNode, updateVNodeProps } from './DOMInternalKeys'; import { createDom } from './utils/DomCreator'; import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler'; -import { shouldAutoFocus } from './utils/Common'; +import { isDocument, shouldAutoFocus } from './utils/Common'; import { NSS } from './utils/DomCreator'; import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler'; import type { VNode } from '../renderer/Types'; @@ -26,6 +26,7 @@ import { isNativeElement, validateProps } from './validators/ValidateProps'; import { watchValueChange } from './valueHandler/ValueChangeHandler'; import { DomComponent, DomText } from '../renderer/vnode/VNodeTags'; import { updateCommonProp } from './DOMPropertiesHandler/UpdateCommonProp'; +import {getCurrentRoot} from '../renderer/RootStack'; export type Props = Record & { autoFocus?: boolean; @@ -70,7 +71,12 @@ export function resetAfterSubmit(): void { // 创建 DOM 对象 export function newDom(tagName: string, props: Props, parentNamespace: string, vNode: VNode): Element { - const dom: Element = createDom(tagName, parentNamespace); + // document取值于treeRoot对应的DOM的ownerDocument。 + // 解决:在iframe中使用top的horizon时,horizon在创建DOM时用到的document并不是iframe的document,而是top中的document的问题。 + const rootDom = getCurrentRoot().realNode; + const doc = isDocument(rootDom) ? rootDom : rootDom.ownerDocument; + + const dom: Element = createDom(tagName, parentNamespace, doc); // 将 vNode 节点挂到 DOM 对象上 saveVNode(vNode, dom); // 将属性挂到 DOM 对象上 diff --git a/libs/horizon/src/dom/utils/DomCreator.ts b/libs/horizon/src/dom/utils/DomCreator.ts index 3eb2d1ca..26fe2840 100644 --- a/libs/horizon/src/dom/utils/DomCreator.ts +++ b/libs/horizon/src/dom/utils/DomCreator.ts @@ -20,15 +20,15 @@ export const NSS = { }; // 创建DOM元素 -export function createDom(tagName: string, parentNamespace: string): Element { +export function createDom(tagName: string, parentNamespace: string, doc: Document): Element { let dom: Element; const selfNamespace = NSS[tagName] || NSS.html; const ns = parentNamespace !== NSS.html ? parentNamespace : selfNamespace; if (ns !== NSS.html) { - dom = document.createElementNS(ns, tagName); + dom = doc.createElementNS(ns, tagName); } else { - dom = document.createElement(tagName); + dom = doc.createElement(tagName); } return dom; } diff --git a/libs/horizon/src/renderer/RootStack.ts b/libs/horizon/src/renderer/RootStack.ts index 110cad72..6a793a0f 100644 --- a/libs/horizon/src/renderer/RootStack.ts +++ b/libs/horizon/src/renderer/RootStack.ts @@ -14,6 +14,7 @@ */ import { VNode } from './vnode/VNode'; + const currentRootStack: VNode[] = []; export function getCurrentRoot() { return currentRootStack[currentRootStack.length - 1]; From 71db7f0f6ba43721471219fcc3b8e94c1314b735 Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 31 Jan 2023 16:57:38 +0800 Subject: [PATCH 25/74] Match-id-5bad0bb66ce628a366c9f96c499b0f7c84ec9b8e --- libs/horizon/jsx-runtime.ts | 3 +- scripts/rollup/rollup.config.js | 63 +++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/libs/horizon/jsx-runtime.ts b/libs/horizon/jsx-runtime.ts index 40b7fd86..f130dc06 100644 --- a/libs/horizon/jsx-runtime.ts +++ b/libs/horizon/jsx-runtime.ts @@ -16,9 +16,10 @@ import { TYPE_FRAGMENT as Fragment, } from './src/external/JSXElementType'; -import { jsx } from './src/external/JSXElement'; +import { jsx, jsx as jsxs } from './src/external/JSXElement'; export { jsx, + jsxs, Fragment }; diff --git a/scripts/rollup/rollup.config.js b/scripts/rollup/rollup.config.js index 57e17279..4e2e7cfd 100644 --- a/scripts/rollup/rollup.config.js +++ b/scripts/rollup/rollup.config.js @@ -37,26 +37,42 @@ if (!fs.existsSync(outDir)) { } const outputResolve = (...p) => path.resolve(outDir, ...p); -const BasicPlugins = [ - nodeResolve({ - extensions, - modulesOnly: true, - }), - babel({ - exclude: 'node_modules/**', - configFile: path.join(__dirname, '../../babel.config.js'), - babelHelpers: 'runtime', - extensions, - }) -]; + +const isDev = (mode) => { + return mode === 'development'; +} + +const getBasicPlugins = (mode) => { + return [ + nodeResolve({ + extensions, + modulesOnly: true, + }), + babel({ + exclude: 'node_modules/**', + configFile: path.join(__dirname, '../../babel.config.js'), + babelHelpers: 'runtime', + extensions, + }), + replace({ + values: { + 'process.env.NODE_ENV': `"${mode}"`, + isDev: isDev(mode).toString(), + isTest: false, + __VERSION__: `"${horizonVersion}"`, + }, + preventAssignment: true, + }), + ]; +} + function getOutputName(mode) { return mode === 'production' ? `horizon.${mode}.min.js` : `horizon.${mode}.js`; } function genConfig(mode) { - const isDev = mode === 'development'; - const sourcemap = isDev ? 'inline' : false; + const sourcemap = isDev(mode) ? 'inline' : false; return { input: path.resolve(libDir, 'index.ts'), output: [ @@ -73,16 +89,7 @@ function genConfig(mode) { }, ], plugins: [ - ...BasicPlugins, - replace({ - values: { - 'process.env.NODE_ENV': `"${mode}"`, - isDev: isDev.toString(), - isTest: false, - __VERSION__: `"${horizonVersion}"`, - }, - preventAssignment: true, - }), + ...getBasicPlugins(mode), execute('npm run build-types'), mode === 'production' && terser(), copy([ @@ -99,7 +106,7 @@ function genConfig(mode) { }; } -function genJSXRuntimeConfig() { +function genJSXRuntimeConfig(mode) { return { input: path.resolve(libDir, 'jsx-runtime.ts'), output: [ @@ -108,7 +115,9 @@ function genJSXRuntimeConfig() { format: 'cjs', } ], - plugins: BasicPlugins + plugins: [ + ...getBasicPlugins(mode) + ] }; } -export default [genConfig('development'), genConfig('production'), genJSXRuntimeConfig()]; +export default [genConfig('development'), genConfig('production'), genJSXRuntimeConfig('')]; From a87766e0f05102d5475739b9fd5db0887bc731ae Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 31 Jan 2023 17:48:27 +0800 Subject: [PATCH 26/74] Match-id-074f59ec0016e87e4f2c57740fb016379ad8b90c --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08d2e879..a6b0e35c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.37 (2023-01-31) +- **core**: 增加jsxs方法 + ## 0.0.36 (2023-01-30) - **core**: #100 horizon从上层页面透传到iframe页面里使用,创建的dom元素instanceof HTMLElement为false diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 20c8dfc2..2c9aeeff 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.36", + "version": "0.0.37", "homepage": "", "bugs": "", "main": "index.js", From de2328827bda114086679f6bfe225f4ad673f6eb Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 1 Feb 2023 11:07:23 +0800 Subject: [PATCH 27/74] Match-id-64d8ec9cf05e0338f4d584bf7221bcfb50cbe776 --- CHANGELOG.md | 3 +++ libs/horizon/index.ts | 4 ++++ libs/horizon/package.json | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b0e35c..12d81d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.38 (2023-02-01) +- **core**: 增加flushSync接口 + ## 0.0.37 (2023-01-31) - **core**: 增加jsxs方法 diff --git a/libs/horizon/index.ts b/libs/horizon/index.ts index 90d62f6e..5fffa7c9 100644 --- a/libs/horizon/index.ts +++ b/libs/horizon/index.ts @@ -66,6 +66,8 @@ import { unmountComponentAtNode, } from './src/dom/DOMExternal'; +import { syncUpdates as flushSync } from './src/renderer/TreeBuilder'; + const Horizon = { Children, createRef, @@ -98,6 +100,7 @@ const Horizon = { findDOMNode, unmountComponentAtNode, act, + flushSync, createStore, useStore, clearStore, @@ -147,6 +150,7 @@ export { findDOMNode, unmountComponentAtNode, act, + flushSync, // 状态管理器HorizonX接口 createStore, useStore, diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 2c9aeeff..c9a179bc 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.37", + "version": "0.0.38", "homepage": "", "bugs": "", "main": "index.js", From 3124afc8f8981097e945016317fdbaf8cd4b91c2 Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 2 Feb 2023 07:12:36 +0800 Subject: [PATCH 28/74] Match-id-8a23de5aec1505a2c6c8e028a30493badb356e08 --- libs/horizon/src/horizonx/devtools/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 35f924e6..7ea843ea 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -52,7 +52,7 @@ function makeProxySnapshot(obj) { export const devtools = { // returns vNode id from horizon devtools getVNodeId: vNode => { - if (!isPanelActive()) return; + if (!isPanelActive() || !window['__HORIZON_DEV_HOOK__'].getVnodeId) return; window['__HORIZON_DEV_HOOK__'].send(); // update list first return window['__HORIZON_DEV_HOOK__'].getVnodeId(vNode); }, From 8e80651ee5523bd4c07c1adf636d7bf9b8ee74d0 Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 9 Feb 2023 14:51:56 +0800 Subject: [PATCH 29/74] Match-id-5c4291c86acb903dd0fa11acc884291cf546f67d --- .cloudbuild/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.cloudbuild/test.yml b/.cloudbuild/test.yml index 0593afd8..2681012f 100644 --- a/.cloudbuild/test.yml +++ b/.cloudbuild/test.yml @@ -22,7 +22,8 @@ steps: - checkout: path: horizon-core - gitlab: - url: https://szv-open.codehub.huawei.com/innersource/shanhai/wutong/react/horizon-test.git + url: https://szv-open.codehub.huawei.com/innersource/fenghuang/horizon/horizon-test.git + branch: one_tree_dev path: horizon-test BUILD: From 2f7787c1cff9554d32a6ef4f92b94c3c4038b7b0 Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 13 Feb 2023 14:51:35 +0800 Subject: [PATCH 30/74] Match-id-bae8261fcf2fb4e5c1748e8ce921736fe3bd9ae4 --- jest.config.js | 4 +- libs/horizon/src/horizonx/CommonUtils.ts | 28 +- libs/horizon/src/horizonx/devtools/index.ts | 108 ++++- .../src/horizonx/proxy/HooklessObserver.ts | 4 + .../src/horizonx/proxy/ProxyHandler.ts | 26 +- .../proxy/handlers copy/ArrayProxyHandler.ts | 85 ++++ .../handlers copy/CollectionProxyHandler.ts | 235 +++++++++++ .../proxy/handlers copy/ObjectProxyHandler.ts | 92 +++++ .../proxy/handlers/ArrayProxyHandler.ts | 121 ++++-- .../proxy/handlers/CollectionProxyHandler.ts | 232 +---------- .../src/horizonx/proxy/handlers/MapProxy.ts | 386 ++++++++++++++++++ .../proxy/handlers/ObjectProxyHandler.ts | 127 +++--- .../src/horizonx/proxy/handlers/SetProxy.ts | 297 ++++++++++++++ .../horizonx/proxy/handlers/WeakMapProxy.ts | 197 +++++++++ .../horizonx/proxy/handlers/WeakSetProxy.ts | 133 ++++++ .../src/horizonx/store/StoreHandler.ts | 49 ++- libs/horizon/src/horizonx/types.d.ts | 1 + .../StateManager/StateArray.test.tsx | 0 .../StateManager/StateMap.test.tsx | 6 +- .../StateManager/StateMixType.test.tsx | 0 .../StateManager/StateSet.test.tsx | 0 .../StateManager/StateWeakMap.test.tsx | 0 .../StateManager/StateWeakSet.test.tsx | 0 .../StoreFunctionality/async.test.tsx | 0 .../StoreFunctionality/basicAccess.test.tsx | 0 .../StoreFunctionality/cloneDeep.test.js | 0 .../StoreFunctionality/dollarAccess.test.tsx | 0 .../StoreFunctionality/otherCases.test.tsx | 0 .../StoreFunctionality/reset.js | 0 .../StoreFunctionality/store.ts | 0 .../StoreFunctionality/utils.test.js | 15 +- .../StoreFunctionality/watch.test.tsx | 2 +- .../adapters/ReduxAdapter.test.tsx | 0 .../adapters/ReduxAdapterThunk.test.tsx | 0 .../adapters/ReduxReactAdapter.test.tsx | 0 .../adapters/connectTest.tsx | 0 .../class/ClassException.test.tsx | 0 .../class/ClassStateArray.test.tsx | 0 .../class/ClassStateMap.test.tsx | 0 .../clear/ClassVNodeClear.test.tsx | 0 .../clear/FunctionVNodeClear.test.tsx | 0 .../edgeCases/deepVariableObserver.test.tsx | 155 +++++++ .../edgeCases/multipleStores.test.tsx | 0 .../edgeCases/proxy.test.tsx | 0 44 files changed, 1977 insertions(+), 326 deletions(-) create mode 100644 libs/horizon/src/horizonx/proxy/handlers copy/ArrayProxyHandler.ts create mode 100644 libs/horizon/src/horizonx/proxy/handlers copy/CollectionProxyHandler.ts create mode 100644 libs/horizon/src/horizonx/proxy/handlers copy/ObjectProxyHandler.ts create mode 100644 libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts create mode 100644 libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts create mode 100644 libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts create mode 100644 libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts rename scripts/__tests__/{HorizonXText => HorizonXTest}/StateManager/StateArray.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StateManager/StateMap.test.tsx (98%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StateManager/StateMixType.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StateManager/StateSet.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StateManager/StateWeakMap.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StateManager/StateWeakSet.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StoreFunctionality/async.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StoreFunctionality/basicAccess.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StoreFunctionality/cloneDeep.test.js (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StoreFunctionality/dollarAccess.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StoreFunctionality/otherCases.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StoreFunctionality/reset.js (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StoreFunctionality/store.ts (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StoreFunctionality/utils.test.js (87%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/StoreFunctionality/watch.test.tsx (98%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/adapters/ReduxAdapter.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/adapters/ReduxAdapterThunk.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/adapters/ReduxReactAdapter.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/adapters/connectTest.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/class/ClassException.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/class/ClassStateArray.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/class/ClassStateMap.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/clear/ClassVNodeClear.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/clear/FunctionVNodeClear.test.tsx (100%) create mode 100644 scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx rename scripts/__tests__/{HorizonXText => HorizonXTest}/edgeCases/multipleStores.test.tsx (100%) rename scripts/__tests__/{HorizonXText => HorizonXTest}/edgeCases/proxy.test.tsx (100%) diff --git a/jest.config.js b/jest.config.js index ddff3e15..911c51e0 100644 --- a/jest.config.js +++ b/jest.config.js @@ -26,8 +26,10 @@ module.exports = { testEnvironment: 'jest-environment-jsdom-sixteen', testMatch: [ + // '/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx', + // '/scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx', '/scripts/__tests__/**/*.test.js', - '/scripts/__tests__/**/*.test.tsx' + '/scripts/__tests__/**/*.test.tsx', ], timers: 'fake', diff --git a/libs/horizon/src/horizonx/CommonUtils.ts b/libs/horizon/src/horizonx/CommonUtils.ts index e6387b6a..cf2c2bb8 100644 --- a/libs/horizon/src/horizonx/CommonUtils.ts +++ b/libs/horizon/src/horizonx/CommonUtils.ts @@ -84,7 +84,6 @@ export function isSame(x, y) { export function getDetailedType(val: any) { if (val === undefined) return 'undefined'; if (val === null) return 'null'; - if (isCollection(val)) return 'collection'; if (isPromise(val)) return 'promise'; if (isArray(val)) return 'array'; if (isWeakMap(val)) return 'weakMap'; @@ -121,7 +120,24 @@ export function resolveMutation(from, to) { } case 'object': { - let keys = Object.keys({ ...from, ...to }); + if (from._type && from._type === to._type) { + if (from._type === 'Map') { + const entries = resolveMutation(from.entries, to.entries); + return { + mutation: entries.items.some(item => item.mutation), + from, + to, + entries: entries.items, + }; + } + + if (from._type === 'Set') { + const values = resolveMutation(from.values, to.values); + return { mutation: values.items.some(item => item.mutation), from, to, values: values.items }; + } + } + + let keys = Object.keys({ ...from, ...to }).filter(key => key !== '_horizonObserver'); const res = {}; let found = false; keys.forEach(key => { @@ -142,8 +158,6 @@ export function resolveMutation(from, to) { return { mutation: found, attributes: res, from, to }; } - // TODO: implement collections - default: { if (from === to) return { mutation: false }; @@ -151,3 +165,9 @@ export function resolveMutation(from, to) { } } } + +export function omit(obj, ...attrs) { + let res = { ...obj }; + attrs.forEach(attr => delete res[attr]); + return res; +} diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index e7f3514e..192ece34 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -1,3 +1,5 @@ +import { isDomVNode } from '../../renderer/vnode/VNodeUtils'; +import { isMap, isSet, isWeakMap, isWeakSet } from '../CommonUtils'; import { getStore, getAllStores } from '../store/StoreHandler'; import { OBSERVED_COMPONENTS } from './constants'; @@ -24,28 +26,87 @@ function makeStoreSnapshot({ type, data }) { } // safely serializes variables containing values wrapped in Proxy object +function getType(value) { + if (!value) return 'nullish'; + if (value.nativeEvent) return 'event'; + if (typeof value === 'function') return 'function'; + if (value.constructor?.name === 'VNode') return 'vnode'; + if (isWeakMap(value)) return 'weakMap'; + if (isWeakSet(value)) return 'weakSet'; + if (isMap(value)) return 'map'; + if (isSet(value)) return 'set'; + if (Array.isArray(value)) return 'array'; + if (typeof value === 'object') return 'object'; + return 'primitive'; +} function makeProxySnapshot(obj) { + const type = getType(obj); let clone; + try { - if (!obj) { + //NULLISH VALUE + if (type === 'nullish') { return obj; } - if (obj.nativeEvent) return obj.type + 'Event'; - if (typeof obj === 'function') { + //EVENT + if (type === 'event') return obj.type + 'Event'; + // FUNCTION + if (type === 'function') { return obj.toString(); } - if (Array.isArray(obj)) { + // VNODE + if (type === 'vnode') { + return { + _type: 'VNode', + id: window['__HORIZON_DEV_HOOK__'].getVnodeId(obj), + tag: obj.tag, + }; + } + // WEAK MAP + if (type === 'weakMap') { + return { + _type: 'WeakMap', + }; + } + // WEAK SET + if (type === 'weakSet') { + return { + _type: 'WeakSet', + }; + } + // MAP + if (type === 'map') { + return { + _type: 'Map', + entries: Array.from(obj.entries()).map(([key, value]) => ({ + key: makeProxySnapshot(key), + value: makeProxySnapshot(value), + })), + }; + } + // SET + if (type === 'set') { + return { + _type: 'Set', + values: Array.from(obj).map(value => makeProxySnapshot(value)), + }; + } + // ARRAY + if (type === 'array') { clone = []; obj.forEach(item => clone.push(makeProxySnapshot(item))); return clone; - } else if (typeof obj === 'object') { + } + // OBJECT + if (type === 'object') { clone = {}; Object.entries(obj).forEach(([id, value]) => (clone[id] = makeProxySnapshot(value))); return clone; } + // PRIMITIVE return obj; } catch (err) { - throw console.log('cannot serialize object. ' + err); + console.error('cannot serialize object. ', { err, obj, type }); } } @@ -76,7 +137,7 @@ function getAffectedComponents() { const subRes = new Set(); const process = Array.from(allStores[key].$config.state._horizonObserver.keyVNodes.values()); while (process.length) { - let pivot = process.shift(); + let pivot = process.shift() as { tag: 'string' }; if (pivot?.tag) subRes.add(pivot); if (pivot?.toString() === '[object Set]') Array.from(pivot).forEach(item => process.push(item)); } @@ -117,4 +178,37 @@ window.addEventListener('message', messageEvent => { const params = data.params; action(...params); } + + // queues store action + if (messageEvent.data.payload.type === 'horizonx queue action') { + const data = messageEvent.data.payload.data; + const store = getStore(data.storeId); + if (!store?.[data.action]) return; + + const action = store.$queue?.[data.action]; + const params = data.params; + action(...params); + } + + // queues change store state + if (messageEvent.data.payload.type === 'horizonx change state') { + const data = messageEvent.data.payload; + const store = getStore(data.storeId); + if (!store) return; + let parent = store.$s; + if (data.operation === 'edit') { + try { + const path = messageEvent.data.payload.path; + + while (path.length > 1) { + parent = parent[path.pop()]; + } + + parent[path[0]] = messageEvent.data.payload.value; + } catch (err) { + console.error(err); + } + } + // TODO:implement add and delete element + } }); diff --git a/libs/horizon/src/horizonx/proxy/HooklessObserver.ts b/libs/horizon/src/horizonx/proxy/HooklessObserver.ts index dd5015e9..a47ad752 100644 --- a/libs/horizon/src/horizonx/proxy/HooklessObserver.ts +++ b/libs/horizon/src/horizonx/proxy/HooklessObserver.ts @@ -31,6 +31,10 @@ export class HooklessObserver implements IObserver { this.listeners = this.listeners.filter(item => item != listener); } + getListeners() { + return this.listeners; + } + setProp(key: string | symbol, mutation: any): void { this.triggerChangeListeners(mutation); } diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index fda6d653..d165675e 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -27,7 +27,7 @@ const proxyMap = new WeakMap(); export const hookObserverMap = new WeakMap(); -export function createProxy(rawObj: any, id, isHookObserver = true): any { +export function createProxy(rawObj: any, isHookObserver = true, listener: { current: (...args) => any }): any { // 不是对象(是原始数据类型)不用代理 if (!(rawObj && isObject(rawObj))) { return rawObj; @@ -56,16 +56,32 @@ export function createProxy(rawObj: any, id, isHookObserver = true): any { // 创建Proxy let proxyObj; if (!isHookObserver) { - proxyObj = createObjectProxy(rawObj, true); + proxyObj = createObjectProxy(rawObj, true, { + current: change => { + listener.current(change); + }, + }); } else if (isArray(rawObj)) { // 数组 - proxyObj = createArrayProxy(rawObj as []); + proxyObj = createArrayProxy(rawObj as [], { + current: change => { + listener.current(change); + }, + }); } else if (isCollection(rawObj)) { // 集合 - proxyObj = createCollectionProxy(rawObj); + proxyObj = createCollectionProxy(rawObj, true, { + current: change => { + listener.current(change); + }, + }); } else { // 原生对象 或 函数 - proxyObj = createObjectProxy(rawObj); + proxyObj = createObjectProxy(rawObj, false, { + current: change => { + listener?.current(change); + }, + }); } proxyMap.set(rawObj, proxyObj); diff --git a/libs/horizon/src/horizonx/proxy/handlers copy/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers copy/ArrayProxyHandler.ts new file mode 100644 index 00000000..9e9c2845 --- /dev/null +++ b/libs/horizon/src/horizonx/proxy/handlers copy/ArrayProxyHandler.ts @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { getObserver } from '../ProxyHandler'; +import { isSame, isValidIntegerKey } from '../../CommonUtils'; +import { get as objectGet } from './ObjectProxyHandler'; +import { resolveMutation } from '../../CommonUtils'; +import { isPanelActive } from '../../devtools'; + +export function createArrayProxy(rawObj: any[]): any[] { + const handle = { + get, + set, + }; + + return new Proxy(rawObj, handle); +} + +function get(rawObj: any[], key: string, receiver: any) { + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + if (isValidIntegerKey(key) || key === 'length') { + return objectGet(rawObj, key, receiver); + } + + return Reflect.get(rawObj, key, receiver); +} + +function set(rawObj: any[], key: string, value: any, receiver: any) { + const oldValue = rawObj[key]; + const oldLength = rawObj.length; + const newValue = value; + + const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + + const ret = Reflect.set(rawObj, key, newValue, receiver); + + const newLength = rawObj.length; + const observer = getObserver(rawObj); + + const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : { mutation: true, from: [], to: rawObj }; + + if (!isSame(newValue, oldValue)) { + // 值不一样,触发监听器 + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue, mutation); + }); + } + + // 触发属性变化 + observer.setProp(key, mutation); + } + + if (oldLength !== newLength) { + // 触发数组的大小变化 + observer.setProp('length', mutation); + } + + return ret; +} diff --git a/libs/horizon/src/horizonx/proxy/handlers copy/CollectionProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers copy/CollectionProxyHandler.ts new file mode 100644 index 00000000..05174cc5 --- /dev/null +++ b/libs/horizon/src/horizonx/proxy/handlers copy/CollectionProxyHandler.ts @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; +import { isMap, isWeakMap, isSame } from '../../CommonUtils'; +import { resolveMutation } from '../../CommonUtils'; +import { isPanelActive } from '../../devtools'; + +const COLLECTION_CHANGE = '_collectionChange'; +const handler = { + get, + set, + add, + delete: deleteFun, + clear, + has, + entries, + forEach, + keys, + values, + // 判断Symbol类型,兼容IE + [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf, +}; + +export function createCollectionProxy(rawObj: Object, hookObserver = true): Object { + const boundHandler = {}; + Object.entries(handler).forEach(([id, val]) => { + boundHandler[id] = (...args: any[]) => { + return (val as any)(...args, hookObserver); + }; + }); + return new Proxy(rawObj, { ...boundHandler }); +} + +function get(rawObj: { size: number }, key: any, receiver: any): any { + if (key === 'size') { + return size(rawObj); + } else if (key === 'get') { + return getFun.bind(null, rawObj); + } else if (Object.prototype.hasOwnProperty.call(handler, key)) { + const value = Reflect.get(handler, key, receiver); + return value.bind(null, rawObj); + } else if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + return Reflect.get(rawObj, key, receiver); +} + +function getFun(rawObj: { get: (key: any) => any }, key: any) { + const observer = getObserver(rawObj); + observer.useProp(key); + + const value = rawObj.get(key); + // 对于value也需要进一步代理 + const valProxy = createProxy(value, hookObserverMap.get(rawObj)); + + return valProxy; +} + +// Map的set方法 +function set( + rawObj: { get: (key: any) => any; set: (key: any, value: any) => any; has: (key: any) => boolean }, + key: any, + value: any +) { + const oldValue = rawObj.get(key); + const newValue = value; + rawObj.set(key, newValue); + const valChange = !isSame(newValue, oldValue); + const observer = getObserver(rawObj); + + const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : { mutation: true, from: null, to: rawObj }; + + if (valChange || !rawObj.has(key)) { + observer.setProp(COLLECTION_CHANGE, mutation); + } + + if (valChange) { + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue, mutation); + }); + } + + observer.setProp(key, mutation); + } + + return rawObj; +} + +// Set的add方法 +function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object { + const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + if (!rawObj.has(value)) { + rawObj.add(value); + + const observer = getObserver(rawObj); + const mutation = isPanelActive() + ? resolveMutation(oldCollection, rawObj) + : { mutation: true, from: null, to: rawObj }; + observer.setProp(value, mutation); + observer.setProp(COLLECTION_CHANGE, mutation); + } + + return rawObj; +} + +function has(rawObj: { has: (string) => boolean }, key: any): boolean { + const observer = getObserver(rawObj); + observer.useProp(key); + + return rawObj.has(key); +} + +function clear(rawObj: { size: number; clear: () => void }) { + const oldSize = rawObj.size; + rawObj.clear(); + + if (oldSize > 0) { + const observer = getObserver(rawObj); + observer.allChange(); + } +} + +function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) { + const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + if (rawObj.has(key)) { + rawObj.delete(key); + + const observer = getObserver(rawObj); + const mutation = isPanelActive() + ? resolveMutation(oldCollection, rawObj) + : { mutation: true, from: null, to: rawObj }; + observer.setProp(key, mutation); + observer.setProp(COLLECTION_CHANGE, mutation); + + return true; + } + + return false; +} + +function size(rawObj: { size: number }) { + const observer = getObserver(rawObj); + observer.useProp(COLLECTION_CHANGE); + return rawObj.size; +} + +function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.keys()); +} + +function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.values()); +} + +function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.entries(), true); +} + +function forOf(rawObj: { + entries: () => { next: () => { value: any; done: boolean } }; + values: () => { next: () => { value: any; done: boolean } }; +}) { + const isMapType = isMap(rawObj) || isWeakMap(rawObj); + const iterator = isMapType ? rawObj.entries() : rawObj.values(); + return wrapIterator(rawObj, iterator, isMapType); +} + +function forEach( + rawObj: { forEach: (callback: (value: any, key: any) => void) => void }, + callback: (valProxy: any, keyProxy: any, rawObj: any) => void +) { + const observer = getObserver(rawObj); + observer.useProp(COLLECTION_CHANGE); + rawObj.forEach((value, key) => { + const valProxy = createProxy(value, hookObserverMap.get(rawObj)); + const keyProxy = createProxy(key, hookObserverMap.get(rawObj)); + // 最后一个参数要返回代理对象 + return callback(valProxy, keyProxy, rawObj); + }); +} + +function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }, isPair = false) { + const observer = getObserver(rawObj); + const hookObserver = hookObserverMap.get(rawObj); + observer.useProp(COLLECTION_CHANGE); + + return { + next() { + const { value, done } = rawIt.next(); + if (done) { + return { value: createProxy(value, hookObserver), done }; + } + + observer.useProp(COLLECTION_CHANGE); + + let newVal; + if (isPair) { + newVal = [createProxy(value[0], hookObserver), createProxy(value[1], hookObserver)]; + } else { + newVal = createProxy(value, hookObserver); + } + + return { value: newVal, done }; + }, + // 判断Symbol类型,兼容IE + [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() { + return this; + }, + }; +} diff --git a/libs/horizon/src/horizonx/proxy/handlers copy/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers copy/ObjectProxyHandler.ts new file mode 100644 index 00000000..721be056 --- /dev/null +++ b/libs/horizon/src/horizonx/proxy/handlers copy/ObjectProxyHandler.ts @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { isSame, resolveMutation } from '../../CommonUtils'; +import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; +import { OBSERVER_KEY } from '../../Constants'; +import { isPanelActive } from '../../devtools'; + +export function createObjectProxy(rawObj: T, singleLevel = false): ProxyHandler { + const proxy = new Proxy(rawObj, { + get: (...args) => get(...args, singleLevel), + set, + }); + + return proxy; +} + +export function get(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any { + // The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. + if (key === OBSERVER_KEY) { + return undefined; + } + + const observer = getObserver(rawObj); + + if (key === 'watch') { + return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + if (key === 'addListener') { + return observer.addListener.bind(observer); + } + + if (key === 'removeListener') { + return observer.removeListener.bind(observer); + } + + observer.useProp(key); + + const value = Reflect.get(rawObj, key, receiver); + + // 对于prototype不做代理 + if (key !== 'prototype') { + // 对于value也需要进一步代理 + const valProxy = singleLevel ? value : createProxy(value, hookObserverMap.get(rawObj)); + + return valProxy; + } + + return value; +} + +export function set(rawObj: object, key: string, value: any, receiver: any): boolean { + const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + const observer = getObserver(rawObj); + + const oldValue = rawObj[key]; + const newValue = value; + + const ret = Reflect.set(rawObj, key, newValue, receiver); + const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : { mutation: true, from: null, to: rawObj }; + + if (!isSame(newValue, oldValue)) { + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue, mutation); + }); + } + observer.setProp(key, mutation); + } + return ret; +} diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index 9e9c2845..3938ecab 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -13,43 +13,112 @@ * See the Mulan PSL v2 for more details. */ -import { getObserver } from '../ProxyHandler'; +import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { isSame, isValidIntegerKey } from '../../CommonUtils'; -import { get as objectGet } from './ObjectProxyHandler'; import { resolveMutation } from '../../CommonUtils'; import { isPanelActive } from '../../devtools'; +import { OBSERVER_KEY } from '../../Constants'; + +export function createArrayProxy(rawObj: any[], listener: { current: (...args) => any }): any[] { + let listeners = [] as ((...args) => void)[]; + + function objectGet(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any { + // The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. + if (key === OBSERVER_KEY) { + return undefined; + } + + const observer = getObserver(rawObj); + + if (key === 'watch') { + return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + + observer.useProp(key); + + const value = Reflect.get(rawObj, key, receiver); + + // 对于prototype不做代理 + if (key !== 'prototype') { + // 对于value也需要进一步代理 + const valProxy = singleLevel + ? value + : createProxy(value, hookObserverMap.get(rawObj), { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [key]: change.mutation.from }, + { ...rawObj, [key]: change.mutation.to } + ); + listener.current(mutation); + listeners.forEach(lst => lst(mutation)); + }, + }); + + return valProxy; + } + + return value; + } + + function get(rawObj: any[], key: string, receiver: any) { + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + if (isValidIntegerKey(key) || key === 'length') { + return objectGet(rawObj, key, receiver); + } + + return Reflect.get(rawObj, key, receiver); + } -export function createArrayProxy(rawObj: any[]): any[] { const handle = { get, set, }; + getObserver(rawObj).addListener(change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + listener.current(change); + listeners.forEach(lst => lst(change)); + }); + return new Proxy(rawObj, handle); } -function get(rawObj: any[], key: string, receiver: any) { - if (key === 'watch') { - const observer = getObserver(rawObj); - - return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; - } - - if (isValidIntegerKey(key) || key === 'length') { - return objectGet(rawObj, key, receiver); - } - - return Reflect.get(rawObj, key, receiver); -} - function set(rawObj: any[], key: string, value: any, receiver: any) { const oldValue = rawObj[key]; const oldLength = rawObj.length; @@ -62,7 +131,7 @@ function set(rawObj: any[], key: string, value: any, receiver: any) { const newLength = rawObj.length; const observer = getObserver(rawObj); - const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : { mutation: true, from: [], to: rawObj }; + const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : resolveMutation(null, rawObj); if (!isSame(newValue, oldValue)) { // 值不一样,触发监听器 diff --git a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts index 05174cc5..1b0f0d37 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts @@ -13,223 +13,25 @@ * See the Mulan PSL v2 for more details. */ -import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; -import { isMap, isWeakMap, isSame } from '../../CommonUtils'; -import { resolveMutation } from '../../CommonUtils'; -import { isPanelActive } from '../../devtools'; +import { isWeakMap, isWeakSet, isSet } from '../../CommonUtils'; +import { createWeakSetProxy } from './WeakSetProxy'; +import { createSetProxy } from './SetProxy'; +import { createWeakMapProxy } from './WeakMapProxy'; +import { createMapProxy } from './MapProxy'; -const COLLECTION_CHANGE = '_collectionChange'; -const handler = { - get, - set, - add, - delete: deleteFun, - clear, - has, - entries, - forEach, - keys, - values, - // 判断Symbol类型,兼容IE - [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf, -}; - -export function createCollectionProxy(rawObj: Object, hookObserver = true): Object { - const boundHandler = {}; - Object.entries(handler).forEach(([id, val]) => { - boundHandler[id] = (...args: any[]) => { - return (val as any)(...args, hookObserver); - }; - }); - return new Proxy(rawObj, { ...boundHandler }); -} - -function get(rawObj: { size: number }, key: any, receiver: any): any { - if (key === 'size') { - return size(rawObj); - } else if (key === 'get') { - return getFun.bind(null, rawObj); - } else if (Object.prototype.hasOwnProperty.call(handler, key)) { - const value = Reflect.get(handler, key, receiver); - return value.bind(null, rawObj); - } else if (key === 'watch') { - const observer = getObserver(rawObj); - - return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; +export function createCollectionProxy( + rawObj: Object, + hookObserver = true, + listener: { current: (...args) => any } +): Object { + if (isWeakSet(rawObj)) { + return createWeakSetProxy(rawObj, hookObserver, listener); } - - return Reflect.get(rawObj, key, receiver); -} - -function getFun(rawObj: { get: (key: any) => any }, key: any) { - const observer = getObserver(rawObj); - observer.useProp(key); - - const value = rawObj.get(key); - // 对于value也需要进一步代理 - const valProxy = createProxy(value, hookObserverMap.get(rawObj)); - - return valProxy; -} - -// Map的set方法 -function set( - rawObj: { get: (key: any) => any; set: (key: any, value: any) => any; has: (key: any) => boolean }, - key: any, - value: any -) { - const oldValue = rawObj.get(key); - const newValue = value; - rawObj.set(key, newValue); - const valChange = !isSame(newValue, oldValue); - const observer = getObserver(rawObj); - - const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : { mutation: true, from: null, to: rawObj }; - - if (valChange || !rawObj.has(key)) { - observer.setProp(COLLECTION_CHANGE, mutation); + if (isSet(rawObj)) { + return createSetProxy(rawObj, hookObserver, listener); } - - if (valChange) { - if (observer.watchers?.[key]) { - observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, mutation); - }); - } - - observer.setProp(key, mutation); + if (isWeakMap(rawObj)) { + return createWeakMapProxy(rawObj, hookObserver, listener); } - - return rawObj; -} - -// Set的add方法 -function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object { - const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - if (!rawObj.has(value)) { - rawObj.add(value); - - const observer = getObserver(rawObj); - const mutation = isPanelActive() - ? resolveMutation(oldCollection, rawObj) - : { mutation: true, from: null, to: rawObj }; - observer.setProp(value, mutation); - observer.setProp(COLLECTION_CHANGE, mutation); - } - - return rawObj; -} - -function has(rawObj: { has: (string) => boolean }, key: any): boolean { - const observer = getObserver(rawObj); - observer.useProp(key); - - return rawObj.has(key); -} - -function clear(rawObj: { size: number; clear: () => void }) { - const oldSize = rawObj.size; - rawObj.clear(); - - if (oldSize > 0) { - const observer = getObserver(rawObj); - observer.allChange(); - } -} - -function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) { - const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - if (rawObj.has(key)) { - rawObj.delete(key); - - const observer = getObserver(rawObj); - const mutation = isPanelActive() - ? resolveMutation(oldCollection, rawObj) - : { mutation: true, from: null, to: rawObj }; - observer.setProp(key, mutation); - observer.setProp(COLLECTION_CHANGE, mutation); - - return true; - } - - return false; -} - -function size(rawObj: { size: number }) { - const observer = getObserver(rawObj); - observer.useProp(COLLECTION_CHANGE); - return rawObj.size; -} - -function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.keys()); -} - -function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.values()); -} - -function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.entries(), true); -} - -function forOf(rawObj: { - entries: () => { next: () => { value: any; done: boolean } }; - values: () => { next: () => { value: any; done: boolean } }; -}) { - const isMapType = isMap(rawObj) || isWeakMap(rawObj); - const iterator = isMapType ? rawObj.entries() : rawObj.values(); - return wrapIterator(rawObj, iterator, isMapType); -} - -function forEach( - rawObj: { forEach: (callback: (value: any, key: any) => void) => void }, - callback: (valProxy: any, keyProxy: any, rawObj: any) => void -) { - const observer = getObserver(rawObj); - observer.useProp(COLLECTION_CHANGE); - rawObj.forEach((value, key) => { - const valProxy = createProxy(value, hookObserverMap.get(rawObj)); - const keyProxy = createProxy(key, hookObserverMap.get(rawObj)); - // 最后一个参数要返回代理对象 - return callback(valProxy, keyProxy, rawObj); - }); -} - -function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }, isPair = false) { - const observer = getObserver(rawObj); - const hookObserver = hookObserverMap.get(rawObj); - observer.useProp(COLLECTION_CHANGE); - - return { - next() { - const { value, done } = rawIt.next(); - if (done) { - return { value: createProxy(value, hookObserver), done }; - } - - observer.useProp(COLLECTION_CHANGE); - - let newVal; - if (isPair) { - newVal = [createProxy(value[0], hookObserver), createProxy(value[1], hookObserver)]; - } else { - newVal = createProxy(value, hookObserver); - } - - return { value: newVal, done }; - }, - // 判断Symbol类型,兼容IE - [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() { - return this; - }, - }; + return createMapProxy(rawObj, hookObserver, listener); } diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts new file mode 100644 index 00000000..d5ed5d37 --- /dev/null +++ b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; +import { isSame } from '../../CommonUtils'; +import { resolveMutation } from '../../CommonUtils'; +import { isPanelActive } from '../../devtools'; + +const COLLECTION_CHANGE = '_collectionChange'; + +export function createMapProxy(rawObj: Object, hookObserver = true, listener: { current: (...args) => any }): Object { + let listeners: ((mutation) => {})[] = []; + let oldData: [any, any][] = []; + let proxies = new Map(); + + function get(rawObj: { size: number }, key: any, receiver: any): any { + if (key === 'size') { + return size(rawObj); + } + + if (key === 'get') { + return getFun.bind(null, rawObj); + } + + if (Object.prototype.hasOwnProperty.call(handler, key)) { + const value = Reflect.get(handler, key, receiver); + return value.bind(null, rawObj); + } + + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + + return Reflect.get(rawObj, key, receiver); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function getFun(rawObj: { get: (key: any) => any; has: (key: any) => boolean }, key: any) { + const keyProxy = rawObj.has(key) ? key : proxies.get(key); + if (!keyProxy) return; + const observer = getObserver(rawObj); + observer.useProp(key); + const value = rawObj.get(keyProxy); + + // 对于value也需要进一步代理 + const valProxy = createProxy(value, hookObserverMap.get(rawObj), { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [key]: change.mutation.from }, + { ...rawObj, [key]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }); + + return valProxy; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Map的set方法 + function set( + rawObj: { + get: (key: any) => any; + set: (key: any, value: any) => any; + has: (key: any) => boolean; + entries: () => [any, any][]; + }, + key: any, + value: any + ) { + if (rawObj.has(key) || rawObj.has(proxies.get(key))) { + // VALUE CHANGE (whole value for selected key is changed) + const oldValue = rawObj.get(proxies.get(key)); + if (isSame(value, oldValue)) return; + rawObj.set(proxies.get(key), value); + const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : resolveMutation(null, rawObj); + const observer = getObserver(rawObj); + observer.setProp(COLLECTION_CHANGE, mutation); + + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, oldValue, value, mutation); + }); + } + + observer.setProp(key, mutation); + oldData = [...Array.from(rawObj.entries())]; + } else { + // NEW VALUE + const keyProxy = createProxy(key, hookObserverMap.get(rawObj), { + current: change => { + // KEY CHANGE + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, ['_keyChange']: change.mutation.from }, + { ...rawObj, ['_keyChange']: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }); + proxies.set(key, keyProxy); + + rawObj.set(keyProxy, value); + const observer = getObserver(rawObj); + const mutation = resolveMutation( + { + _type: 'Map', + entries: oldData, + }, + { + _type: 'Map', + entries: Array.from(rawObj.entries()), + } + ); + observer.setProp(COLLECTION_CHANGE, mutation); + + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, null, value, mutation); + }); + } + observer.setProp(key, mutation); + oldData = [...Array.from(rawObj.entries())]; + } + + return rawObj; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function has(rawObj: { has: (any) => boolean }, key: any): boolean { + const observer = getObserver(rawObj); + observer.useProp(key); + if (rawObj.has(key)) { + return true; + } + return proxies.has(key); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function clear(rawObj: { size: number; clear: () => void; entries: () => [any, any][] }) { + const oldSize = rawObj.size; + rawObj.clear(); + + if (oldSize > 0) { + const observer = getObserver(rawObj); + observer.allChange(); + oldData = [...Array.from(rawObj.entries())]; + } + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function deleteFun( + rawObj: { has: (key: any) => boolean; delete: (key: any) => void; entries: () => [any, any][] }, + key: any + ) { + if (rawObj.has(key) || proxies.has(key)) { + rawObj.delete(key || proxies.get(key)); + + const observer = getObserver(rawObj); + const mutation = resolveMutation( + { + _type: 'Map', + entries: oldData, + }, + { + _type: 'Map', + entries: Array.from(rawObj.entries()), + } + ); + observer.setProp(key, mutation); + observer.setProp(COLLECTION_CHANGE, mutation); + + oldData = [...Array.from(rawObj.entries())]; + return true; + } + + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function size(rawObj: { size: number }) { + const observer = getObserver(rawObj); + observer.useProp(COLLECTION_CHANGE); + return rawObj.size; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.keys(), 'keys'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.values(), 'values'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.entries(), 'entries'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function forOf(rawObj: { + entries: () => { next: () => { value: any; done: boolean } }; + values: () => { next: () => { value: any; done: boolean } }; + }) { + return wrapIterator(rawObj, rawObj.entries(), 'entries'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function forEach( + rawObj: { forEach: (callback: (value: any, key: any) => void) => void }, + callback: (valProxy: any, keyProxy: any, rawObj: any) => void + ) { + const observer = getObserver(rawObj); + observer.useProp(COLLECTION_CHANGE); + rawObj.forEach((value, key) => { + const keyProxy = createProxy(value, hookObserverMap.get(rawObj), { + current: change => { + //KEY ATTRIBUTES CHANGED + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, ['_keyChange']: change.mutation.from }, + { ...rawObj, ['_keyChange']: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }); + const valProxy = createProxy(key, hookObserverMap.get(rawObj), { + current: change => { + // VALUE ATTRIBUTE CHANGED + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, key: change.mutation.from }, + { ...rawObj, key: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }); + // 最后一个参数要返回代理对象 + return callback(keyProxy, valProxy, rawObj); + }); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }, type) { + const observer = getObserver(rawObj); + const hookObserver = hookObserverMap.get(rawObj); + observer.useProp(COLLECTION_CHANGE); + + return { + next() { + const { value, done } = rawIt.next(); + if (done) { + return { + value: createProxy(value, hookObserver, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [value]: change.mutation.from }, + { ...rawObj, [value]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }), + done, + }; + } + + observer.useProp(COLLECTION_CHANGE); + let newVal; + if (type === 'entries') { + //ENTRY CHANGED + newVal = [ + createProxy(value[0], hookObserver, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, ['itemChange']: { key: change.mutation.from, value: value[1] } }, + { ...rawObj, ['itemChange']: { key: change.mutation.to, value: value[1] } } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }), + createProxy(value[1], hookObserver, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, item: { key: value[0], value: change.mutation.from } }, + { ...rawObj, item: { key: value[0], value: change.mutation.to } } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }), + ]; + } else { + // SINGLE VALUE CHANGED + newVal = createProxy(value, hookObserver, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [type === 'keys' ? 'key' : 'value']: change.mutation.from }, + { ...rawObj, [type === 'keys' ? 'key' : 'value']: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }); + } + + return { value: newVal, done }; + }, + // 判断Symbol类型,兼容IE + [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() { + return this; + }, + }; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const handler = { + get, + set, + delete: deleteFun, + clear, + has, + entries, + forEach, + keys, + values, + // 判断Symbol类型,兼容IE + [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf, + }; + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const boundHandler = {}; + Object.entries(handler).forEach(([id, val]) => { + boundHandler[id] = (...args: any[]) => { + return (val as any)(...args, hookObserver); + }; + }); + + getObserver(rawObj).addListener(change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + listener.current(change); + listeners.forEach(lst => lst(change)); + }); + return new Proxy(rawObj, { ...boundHandler }); +} diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 80da37fd..f1ac97b7 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -18,70 +18,97 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { OBSERVER_KEY } from '../../Constants'; import { isPanelActive } from '../../devtools'; -export function createObjectProxy(rawObj: T, singleLevel = false): ProxyHandler { +export function createObjectProxy( + rawObj: T, + singleLevel = false, + listener: { current: (...args) => any } +): ProxyHandler { + let listeners = [] as ((...args) => void)[]; + + function get(rawObj: object, key: string | symbol, receiver: any): any { + // The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. + if (key === OBSERVER_KEY) { + return undefined; + } + + const observer = getObserver(rawObj); + + if (key === 'watch') { + return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + + observer.useProp(key); + + const value = Reflect.get(rawObj, key, receiver); + + // 对于prototype不做代理 + if (key !== 'prototype') { + // 对于value也需要进一步代理 + const valProxy = singleLevel + ? value + : createProxy(value, hookObserverMap.get(rawObj), { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [key]: change.mutation.from }, + { ...rawObj, [key]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }); + + return valProxy; + } + + return value; + } + const proxy = new Proxy(rawObj, { - get: (...args) => get(...args, singleLevel), + get, set, }); + getObserver(rawObj).addListener(change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + listener.current(change); + listeners.forEach(lst => lst(change)); + }); + return proxy; } -export function get(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any { - // The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. - if (key === OBSERVER_KEY) { - return undefined; - } - - const observer = getObserver(rawObj); - - if (key === 'watch') { - return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; - } - - if (key === 'addListener') { - return observer.addListener.bind(observer); - } - - if (key === 'removeListener') { - return observer.removeListener.bind(observer); - } - - observer.useProp(key); - - const value = Reflect.get(rawObj, key, receiver); - - // 对于prototype不做代理 - if (key !== 'prototype') { - // 对于value也需要进一步代理 - const valProxy = singleLevel ? value : createProxy(value, hookObserverMap.get(rawObj)); - - return valProxy; - } - - return value; -} - -export function set(rawObj: object, key: string, value: any, receiver: any): boolean { +function set(rawObj: object, key: string, value: any, receiver: any): boolean { const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; const observer = getObserver(rawObj); - if (value && key == 'removeListener') { - observer.removeListener(value); - } const oldValue = rawObj[key]; const newValue = value; const ret = Reflect.set(rawObj, key, newValue, receiver); - const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : { mutation: true, from: null, to: rawObj }; + const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj); if (!isSame(newValue, oldValue)) { if (observer.watchers?.[key]) { diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts new file mode 100644 index 00000000..6a4401fa --- /dev/null +++ b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { resolveMutation } from '../../CommonUtils'; +import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; + +const COLLECTION_CHANGE = '_collectionChange'; + +export function createSetProxy( + rawObj: T, + hookObserver = true, + listener: { current: (...args) => any } +): ProxyHandler { + let listeners: ((mutation) => {})[] = []; + let proxies = new WeakMap(); + + function get(rawObj: { size: number }, key: any, receiver: any): any { + if (Object.prototype.hasOwnProperty.call(handler, key)) { + const value = Reflect.get(handler, key, receiver); + return value.bind(null, rawObj); + } + + if (key === 'size') { + return size(rawObj); + } + + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + return Reflect.get(rawObj, key, receiver); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Set的add方法 + function add(rawObj: { add: (any) => void; has: (any) => boolean; values: () => any[] }, value: any): Object { + if (!rawObj.has(proxies.get(value))) { + const proxy = createProxy(value, hookObserverMap.get(rawObj), { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, valueChange: change.mutation.from }, + { ...rawObj, valueChange: change.mutation.to } + ); + listener.current({ + ...change, + mutation, + }); + listeners.forEach(lst => + lst({ + ...change, + mutation, + }) + ); + }, + }); + const oldValues = Array.from(rawObj.values()); + + proxies.set(value, proxy); + + rawObj.add(proxies.get(value)); + + const observer = getObserver(rawObj); + const mutation = resolveMutation( + { + _type: 'Set', + values: oldValues, + }, + { + _type: 'Set', + values: Array.from(rawObj.values()), + } + ); + + observer.setProp(value, mutation); + observer.setProp(COLLECTION_CHANGE, mutation); + } + + return rawObj; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function has(rawObj: { has: (string) => boolean }, value: any): boolean { + const observer = getObserver(rawObj); + observer.useProp(value); + + return rawObj.has(proxies.get(value)); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function deleteFun( + rawObj: { has: (key: any) => boolean; delete: (value: any) => void; values: () => any[] }, + value: any + ) { + const val = rawObj.has(proxies.get(value)) ? proxies.get(value) : value; + if (rawObj.has(val)) { + const oldValues = Array.from(rawObj.values()); + rawObj.delete(val); + + proxies.delete(value); + + const observer = getObserver(rawObj); + const mutation = resolveMutation( + { + _type: 'Set', + values: oldValues, + }, + { + _type: 'Set', + values: Array.from(rawObj.values()), + } + ); + + observer.setProp(value, mutation); + observer.setProp(COLLECTION_CHANGE, mutation); + + return true; + } + + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function clear(rawObj: { size: number; clear: () => void }) { + const oldSize = rawObj.size; + rawObj.clear(); + + if (oldSize > 0) { + const observer = getObserver(rawObj); + observer.allChange(); + } + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function size(rawObj: { size: number }) { + const observer = getObserver(rawObj); + observer.useProp(COLLECTION_CHANGE); + return rawObj.size; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.keys()); + } + + function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.values()); + } + + function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.entries()); + } + + function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }) { + const observer = getObserver(rawObj); + const hookObserver = hookObserverMap.get(rawObj); + observer.useProp(COLLECTION_CHANGE); + + return { + next() { + const currentListener = { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, valueChange: change.mutation.from }, + { ...rawObj, valueChange: change.mutation.to } + ); + listener.current({ + ...change, + mutation, + }); + listeners.forEach(lst => + lst({ + ...change, + mutation, + }) + ); + }, + }; + const { value, done } = rawIt.next(); + if (done) { + return { value: createProxy(value, hookObserver, currentListener), done }; + } + + observer.useProp(COLLECTION_CHANGE); + + let newVal; + newVal = createProxy(value, hookObserver, currentListener); + + return { value: newVal, done }; + }, + // 判断Symbol类型,兼容IE + [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() { + return this; + }, + }; + } + + function forOf(rawObj: { + entries: () => { next: () => { value: any; done: boolean } }; + values: () => { next: () => { value: any; done: boolean } }; + }) { + const iterator = rawObj.values(); + return wrapIterator(rawObj, iterator); + } + + function forEach( + rawObj: { forEach: (callback: (value: any, key: any) => void) => void }, + callback: (valProxy: any, keyProxy: any, rawObj: any) => void + ) { + const observer = getObserver(rawObj); + observer.useProp(COLLECTION_CHANGE); + rawObj.forEach((value, key) => { + const currentListener = { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, valueChange: change.mutation.from }, + { ...rawObj, valueChange: change.mutation.to } + ); + listener.current({ + ...change, + mutation, + }); + listeners.forEach(lst => + lst({ + ...change, + mutation, + }) + ); + }, + }; + const valProxy = createProxy(value, hookObserverMap.get(rawObj), currentListener); + const keyProxy = createProxy(key, hookObserverMap.get(rawObj), currentListener); + // 最后一个参数要返回代理对象 + return callback(valProxy, keyProxy, rawObj); + }); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const handler = { + get, + add, + delete: deleteFun, + has, + clear, + forEach, + forOf, + entries, + keys, + values, + [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf, + }; + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + getObserver(rawObj).addListener(change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + listener.current(change); + listeners.forEach(lst => lst(change)); + }); + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const boundHandler = {}; + Object.entries(handler).forEach(([id, val]) => { + boundHandler[id] = (...args: any[]) => { + return (val as any)(...args, hookObserver); + }; + }); + return new Proxy(rawObj, { ...boundHandler }); +} diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts new file mode 100644 index 00000000..d5f87ff3 --- /dev/null +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; +import { isSame } from '../../CommonUtils'; +import { resolveMutation } from '../../CommonUtils'; +import { isPanelActive } from '../../devtools'; + +const COLLECTION_CHANGE = '_collectionChange'; + +export function createWeakMapProxy( + rawObj: Object, + hookObserver = true, + listener: { current: (...args) => any } +): Object { + let listeners: ((mutation) => {})[] = []; + + function get(rawObj: { size: number }, key: any, receiver: any): any { + if (key === 'get') { + return getFun.bind(null, rawObj); + } + + if (Object.prototype.hasOwnProperty.call(handler, key)) { + const value = Reflect.get(handler, key, receiver); + return value.bind(null, rawObj); + } + + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + + return Reflect.get(rawObj, key, receiver); + } + + function getFun(rawObj: { get: (key: any) => any }, key: any) { + const observer = getObserver(rawObj); + observer.useProp(key); + + const value = rawObj.get(key); + // 对于value也需要进一步代理 + const valProxy = createProxy(value, hookObserverMap.get(rawObj), { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [key]: change.mutation.from }, + { ...rawObj, [key]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }); + + return valProxy; + } + + // Map的set方法 + function set( + rawObj: { get: (key: any) => any; set: (key: any, value: any) => any; has: (key: any) => boolean }, + key: any, + value: any + ) { + const oldValue = rawObj.get(key); + const newValue = value; + rawObj.set(key, newValue); + const valChange = !isSame(newValue, oldValue); + const observer = getObserver(rawObj); + + const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : resolveMutation(null, rawObj); + + if (valChange || !rawObj.has(key)) { + observer.setProp(COLLECTION_CHANGE, mutation); + } + + if (valChange) { + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue, mutation); + }); + } + + observer.setProp(key, mutation); + } + + return rawObj; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Set的add方法 + function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object { + const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + if (!rawObj.has(value)) { + rawObj.add(value); + + const observer = getObserver(rawObj); + const mutation = isPanelActive() + ? resolveMutation(oldCollection, rawObj) + : { mutation: true, from: null, to: rawObj }; + observer.setProp(value, mutation); + observer.setProp(COLLECTION_CHANGE, mutation); + } + + return rawObj; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function has(rawObj: { has: (string) => boolean }, key: any): boolean { + const observer = getObserver(rawObj); + observer.useProp(key); + + return rawObj.has(key); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function clear(rawObj: { size: number; clear: () => void }) { + const oldSize = rawObj.size; + rawObj.clear(); + + if (oldSize > 0) { + const observer = getObserver(rawObj); + observer.allChange(); + } + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) { + const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + if (rawObj.has(key)) { + rawObj.delete(key); + + const observer = getObserver(rawObj); + const mutation = isPanelActive() + ? resolveMutation(oldCollection, rawObj) + : { mutation: true, from: null, to: rawObj }; + observer.setProp(key, mutation); + observer.setProp(COLLECTION_CHANGE, mutation); + + return true; + } + + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const handler = { + get, + set, + add, + delete: deleteFun, + clear, + has, + }; + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + getObserver(rawObj).addListener(change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + listener.current(change); + listeners.forEach(lst => lst(change)); + }); + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const boundHandler = {}; + Object.entries(handler).forEach(([id, val]) => { + boundHandler[id] = (...args: any[]) => { + return (val as any)(...args, hookObserver); + }; + }); + return new Proxy(rawObj, { ...boundHandler }); +} diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts new file mode 100644 index 00000000..27becbe9 --- /dev/null +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { resolveMutation } from '../../CommonUtils'; +import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; + +export function createWeakSetProxy( + rawObj: T, + hookObserver = true, + listener: { current: (...args) => any } +): ProxyHandler { + let listeners: ((mutation) => {})[] = []; + let proxies = new WeakMap(); + + function get(rawObj: { size: number }, key: any, receiver: any): any { + if (Object.prototype.hasOwnProperty.call(handler, key)) { + const value = Reflect.get(handler, key, receiver); + return value.bind(null, rawObj); + } + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + return Reflect.get(rawObj, key, receiver); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Set的add方法 + function add(rawObj: { add: (any) => void; has: (any) => boolean }, value: any): Object { + if (!rawObj.has(proxies.get(value))) { + const proxy = createProxy(value, hookObserverMap.get(rawObj), { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [value]: change.mutation.from }, + { ...rawObj, [value]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }); + + proxies.set(value, proxy); + + rawObj.add(proxies.get(value)); + + const observer = getObserver(rawObj); + const mutation = { mutation: true, from: rawObj, to: value }; + + observer.setProp(value, mutation); + } + + return rawObj; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function has(rawObj: { has: (string) => boolean }, value: any): boolean { + const observer = getObserver(rawObj); + observer.useProp(value); + + return rawObj.has(proxies.get(value)); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function deleteFun(rawObj: { has: (key: any) => boolean; delete: (value: any) => void }, value: any) { + if (rawObj.has(proxies.get(value))) { + rawObj.delete(proxies.get(value)); + + proxies.delete(value); + + const observer = getObserver(rawObj); + const mutation = { mutation: true, from: value, to: rawObj }; + + observer.setProp(value, mutation); + + return true; + } + + return false; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const handler = { + get, + add, + delete: deleteFun, + has, + }; + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + getObserver(rawObj).addListener(change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + listener.current(change); + listeners.forEach(lst => lst(change)); + }); + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const boundHandler = {}; + Object.entries(handler).forEach(([id, val]) => { + boundHandler[id] = (...args: any[]) => { + return (val as any)(...args, hookObserver); + }; + }); + return new Proxy(rawObj, { ...boundHandler }); +} diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index 8b3aa154..781deef4 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -38,6 +38,7 @@ import { ACTION_QUEUED, INITIALIZED, QUEUE_FINISHED, + QUEUE_PENDING, STATE_CHANGE, SUBSCRIBED, UNSUBSCRIBED, @@ -62,7 +63,11 @@ export function createStore, C extend const id = config.id || idGenerator.get('UNNAMED_STORE'); - const proxyObj = createProxy(config.state, id, !config.options?.isReduxAdapter); + const listener = { + current: listener => {}, + }; + + const proxyObj = createProxy(config.state, !config.options?.isReduxAdapter, listener); proxyObj.$pending = false; @@ -76,16 +81,28 @@ export function createStore, C extend $c: $c as ComputedValues, $queue: $queue as QueuedStoreActions, $config: config, + $listeners: [ + change => { + devtools.emit(STATE_CHANGE, { + store: storeObj, + change, + }); + }, + ], $subscribe: listener => { devtools.emit(SUBSCRIBED, { store: storeObj, listener }); - proxyObj.addListener(listener); + storeObj.$listeners.push(listener); }, $unsubscribe: listener => { - devtools.emit(UNSUBSCRIBED, storeObj); - proxyObj.removeListener(listener); + devtools.emit(UNSUBSCRIBED, { store: storeObj }); + storeObj.$listeners = storeObj.$listeners.filter(item => item != listener); }, } as unknown as StoreObj; + listener.current = (...args) => { + storeObj.$listeners.forEach(listener => listener(...args)); + }; + const plannedActions: PlannedAction>[] = []; // 包装actions @@ -104,7 +121,11 @@ export function createStore, C extend }); return new Promise(resolve => { if (!proxyObj.$pending) { - proxyObj.$pending = true; + proxyObj.$pending = Date.now(); + devtools.emit(QUEUE_PENDING, { + store: storeObj, + startedAt: proxyObj.$pending, + }); const result = config.actions![action].bind(storeObj, proxyObj)(...payload); @@ -192,20 +213,22 @@ export function createStore, C extend store: storeObj, }); - proxyObj.addListener(change => { - devtools.emit(STATE_CHANGE, { - store: storeObj, - change, - }); - }); - return createGetStore(storeObj); } // 通过该方法执行store.$queue中的action function tryNextAction(storeObj, proxyObj, config, plannedActions) { if (!plannedActions.length) { - proxyObj.$pending = false; + if (proxyObj.$pending) { + const timestamp = Date.now(); + const duration = timestamp - proxyObj.$pending; + proxyObj.$pending = false; + devtools.emit(QUEUE_FINISHED, { + store: storeObj, + endedAt: timestamp, + duration, + }); + } return; } diff --git a/libs/horizon/src/horizonx/types.d.ts b/libs/horizon/src/horizonx/types.d.ts index eec4a826..d617037c 100644 --- a/libs/horizon/src/horizonx/types.d.ts +++ b/libs/horizon/src/horizonx/types.d.ts @@ -61,6 +61,7 @@ export type StoreObj, C extends UserC $a: StoreActions; $c: UserComputedValues; $queue: QueuedStoreActions; + $listeners; $subscribe: (listener: (mutation) => void) => void; $unsubscribe: (listener: (mutation) => void) => void; } & { [K in keyof S]: S[K] } & { [K in keyof A]: Action } & { [K in keyof C]: ReturnType }; diff --git a/scripts/__tests__/HorizonXText/StateManager/StateArray.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateArray.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/StateManager/StateArray.test.tsx rename to scripts/__tests__/HorizonXTest/StateManager/StateArray.test.tsx diff --git a/scripts/__tests__/HorizonXText/StateManager/StateMap.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx similarity index 98% rename from scripts/__tests__/HorizonXText/StateManager/StateMap.test.tsx rename to scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx index 5f4afe65..d83031df 100644 --- a/scripts/__tests__/HorizonXText/StateManager/StateMap.test.tsx +++ b/scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx @@ -73,13 +73,13 @@ describe('测试store中的Map', () => { function Parent(props) { const userStore = useUserStore(); - const addOnePerson = function() { + const addOnePerson = function () { userStore.addOnePerson(newPerson); }; - const delOnePerson = function() { + const delOnePerson = function () { userStore.delOnePerson(newPerson); }; - const clearPersons = function() { + const clearPersons = function () { userStore.clearPersons(); }; diff --git a/scripts/__tests__/HorizonXText/StateManager/StateMixType.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateMixType.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/StateManager/StateMixType.test.tsx rename to scripts/__tests__/HorizonXTest/StateManager/StateMixType.test.tsx diff --git a/scripts/__tests__/HorizonXText/StateManager/StateSet.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateSet.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/StateManager/StateSet.test.tsx rename to scripts/__tests__/HorizonXTest/StateManager/StateSet.test.tsx diff --git a/scripts/__tests__/HorizonXText/StateManager/StateWeakMap.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateWeakMap.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/StateManager/StateWeakMap.test.tsx rename to scripts/__tests__/HorizonXTest/StateManager/StateWeakMap.test.tsx diff --git a/scripts/__tests__/HorizonXText/StateManager/StateWeakSet.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateWeakSet.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/StateManager/StateWeakSet.test.tsx rename to scripts/__tests__/HorizonXTest/StateManager/StateWeakSet.test.tsx diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/async.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/async.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/StoreFunctionality/async.test.tsx rename to scripts/__tests__/HorizonXTest/StoreFunctionality/async.test.tsx diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/basicAccess.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/StoreFunctionality/basicAccess.test.tsx rename to scripts/__tests__/HorizonXTest/StoreFunctionality/basicAccess.test.tsx diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/cloneDeep.test.js b/scripts/__tests__/HorizonXTest/StoreFunctionality/cloneDeep.test.js similarity index 100% rename from scripts/__tests__/HorizonXText/StoreFunctionality/cloneDeep.test.js rename to scripts/__tests__/HorizonXTest/StoreFunctionality/cloneDeep.test.js diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/dollarAccess.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/dollarAccess.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/StoreFunctionality/dollarAccess.test.tsx rename to scripts/__tests__/HorizonXTest/StoreFunctionality/dollarAccess.test.tsx diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/otherCases.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/otherCases.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/StoreFunctionality/otherCases.test.tsx rename to scripts/__tests__/HorizonXTest/StoreFunctionality/otherCases.test.tsx diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/reset.js b/scripts/__tests__/HorizonXTest/StoreFunctionality/reset.js similarity index 100% rename from scripts/__tests__/HorizonXText/StoreFunctionality/reset.js rename to scripts/__tests__/HorizonXTest/StoreFunctionality/reset.js diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/store.ts b/scripts/__tests__/HorizonXTest/StoreFunctionality/store.ts similarity index 100% rename from scripts/__tests__/HorizonXText/StoreFunctionality/store.ts rename to scripts/__tests__/HorizonXTest/StoreFunctionality/store.ts diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/utils.test.js b/scripts/__tests__/HorizonXTest/StoreFunctionality/utils.test.js similarity index 87% rename from scripts/__tests__/HorizonXText/StoreFunctionality/utils.test.js rename to scripts/__tests__/HorizonXTest/StoreFunctionality/utils.test.js index 7d1f786f..be07b4c4 100644 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/utils.test.js +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/utils.test.js @@ -64,7 +64,6 @@ describe('Mutation resolve', () => { it('should resolve mutation same type types, same object', () => { const mutation = resolveMutation({ a: 1, b: 2 }, { a: 1, b: 2 }); - console.log(mutation); expect(mutation.mutation).toBe(false); }); @@ -78,3 +77,17 @@ describe('Mutation resolve', () => { expect(mutation.attributes.c.to).toBe(2); }); }); + +describe('Mutation collections', () => { + it('should resolve mutation of two sets', () => { + const values = [{ a: 1 }, { b: 2 }, { c: 3 }]; + + const source = new Set([values[0], values[1], values[2]]); + + const target = new Set([values[0], values[1]]); + + const mutation = resolveMutation(source, target); + + expect(mutation.mutation).toBe(true); + }); +}); diff --git a/scripts/__tests__/HorizonXText/StoreFunctionality/watch.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/watch.test.tsx similarity index 98% rename from scripts/__tests__/HorizonXText/StoreFunctionality/watch.test.tsx rename to scripts/__tests__/HorizonXTest/StoreFunctionality/watch.test.tsx index 8d685539..c4307f26 100644 --- a/scripts/__tests__/HorizonXText/StoreFunctionality/watch.test.tsx +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/watch.test.tsx @@ -17,7 +17,7 @@ import { createStore } from '@cloudsop/horizon/src/horizonx/store/StoreHandler'; import { watch } from '@cloudsop/horizon/src/horizonx/proxy/watch'; describe('watch', () => { - it('shouhld watch promitive state variable', async () => { + it('shouhld watch primitive state variable', async () => { const useStore = createStore({ state: { variable: 'x', diff --git a/scripts/__tests__/HorizonXText/adapters/ReduxAdapter.test.tsx b/scripts/__tests__/HorizonXTest/adapters/ReduxAdapter.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/adapters/ReduxAdapter.test.tsx rename to scripts/__tests__/HorizonXTest/adapters/ReduxAdapter.test.tsx diff --git a/scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.tsx b/scripts/__tests__/HorizonXTest/adapters/ReduxAdapterThunk.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/adapters/ReduxAdapterThunk.test.tsx rename to scripts/__tests__/HorizonXTest/adapters/ReduxAdapterThunk.test.tsx diff --git a/scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.tsx b/scripts/__tests__/HorizonXTest/adapters/ReduxReactAdapter.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/adapters/ReduxReactAdapter.test.tsx rename to scripts/__tests__/HorizonXTest/adapters/ReduxReactAdapter.test.tsx diff --git a/scripts/__tests__/HorizonXText/adapters/connectTest.tsx b/scripts/__tests__/HorizonXTest/adapters/connectTest.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/adapters/connectTest.tsx rename to scripts/__tests__/HorizonXTest/adapters/connectTest.tsx diff --git a/scripts/__tests__/HorizonXText/class/ClassException.test.tsx b/scripts/__tests__/HorizonXTest/class/ClassException.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/class/ClassException.test.tsx rename to scripts/__tests__/HorizonXTest/class/ClassException.test.tsx diff --git a/scripts/__tests__/HorizonXText/class/ClassStateArray.test.tsx b/scripts/__tests__/HorizonXTest/class/ClassStateArray.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/class/ClassStateArray.test.tsx rename to scripts/__tests__/HorizonXTest/class/ClassStateArray.test.tsx diff --git a/scripts/__tests__/HorizonXText/class/ClassStateMap.test.tsx b/scripts/__tests__/HorizonXTest/class/ClassStateMap.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/class/ClassStateMap.test.tsx rename to scripts/__tests__/HorizonXTest/class/ClassStateMap.test.tsx diff --git a/scripts/__tests__/HorizonXText/clear/ClassVNodeClear.test.tsx b/scripts/__tests__/HorizonXTest/clear/ClassVNodeClear.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/clear/ClassVNodeClear.test.tsx rename to scripts/__tests__/HorizonXTest/clear/ClassVNodeClear.test.tsx diff --git a/scripts/__tests__/HorizonXText/clear/FunctionVNodeClear.test.tsx b/scripts/__tests__/HorizonXTest/clear/FunctionVNodeClear.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/clear/FunctionVNodeClear.test.tsx rename to scripts/__tests__/HorizonXTest/clear/FunctionVNodeClear.test.tsx diff --git a/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx b/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx new file mode 100644 index 00000000..6fff3531 --- /dev/null +++ b/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx @@ -0,0 +1,155 @@ +import { createStore, useStore } from '@cloudsop/horizon/src/horizonx/store/StoreHandler'; +import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; + +describe('Using deep variables', () => { + it('should listen to object variable change', () => { + let counter = 0; + const useTestStore = createStore({ + state: { a: { b: { c: 1 } } }, + }); + const testStore = useTestStore(); + testStore.$subscribe(() => { + counter++; + }); + + testStore.a.b.c = 0; + + expect(counter).toBe(1); + }); + + it('should listen to deep variable change', () => { + let counter = 0; + const useTestStore = createStore({ + state: { color: [{ a: 1 }, 255, 255] }, + }); + const testStore = useTestStore(); + testStore.$subscribe(() => { + counter++; + }); + + for (let i = 0; i < 5; i++) { + testStore.color[0].a = i; + } + testStore.color = 'x'; + + expect(counter).toBe(6); + }); + + it('should use set', () => { + const useTestStore = createStore({ + state: { data: new Set() }, + }); + const testStore = useTestStore(); + + const a = { a: true }; + + testStore.data.add(a); + + expect(testStore.data.has(a)).toBe(true); + + testStore.data.add(a); + testStore.data.add(a); + testStore.data.delete(a); + + expect(testStore.data.has(a)).toBe(false); + + testStore.data.add(a); + + const values = Array.from(testStore.data.values()); + expect(values.length).toBe(1); + + let counter = 0; + testStore.$subscribe(mutation => { + counter++; + }); + + values.forEach(val => { + val.a = !val.a; + }); + + expect(testStore.data.has(a)).toBe(true); + + expect(counter).toBe(1); + }); + + it('should use map', () => { + const useTestStore = createStore({ + state: { data: new Map() }, + }); + const testStore = useTestStore(); + + const data = { key: { a: 1 }, value: { b: 2 } }; + + testStore.data.set(data.key, data.value); + + const key = Array.from(testStore.data.keys())[0]; + + expect(testStore.data.has(key)).toBe(true); + + testStore.data.set(data.key, data.value); + testStore.data.set(data.key, data.value); + testStore.data.delete(key); + + expect(testStore.data.get(key)).toBe(); + + testStore.data.set(data.key, data.value); + + const entries = Array.from(testStore.data.entries()); + expect(entries.length).toBe(1); + + let counter = 0; + testStore.$subscribe(mutation => { + counter++; + }); + + entries.forEach(([key, value]) => { + key.a++; + value.b++; + }); + + expect(counter).toBe(2); + }); + + it('should use weakSet', () => { + const useTestStore = createStore({ + state: { data: new WeakSet() }, + }); + const testStore = useTestStore(); + + const a = { a: true }; + + testStore.data.add(a); + + expect(testStore.data.has(a)).toBe(true); + + testStore.data.add(a); + testStore.data.add(a); + testStore.data.delete(a); + + expect(testStore.data.has(a)).toBe(false); + + testStore.data.add(a); + + expect(testStore.data.has(a)).toBe(true); + }); + + it('should use weakMap', () => { + const useTestStore = createStore({ + state: { data: new WeakMap() }, + }); + const testStore = useTestStore(); + + const data = { key: { a: 1 }, value: { b: 2 } }; + + testStore.data.set(data.key, data.value); + + let counter = 0; + testStore.$subscribe(mutation => { + counter++; + }); + + testStore.data.get(data.key).b++; + + expect(counter).toBe(1); + }); +}); diff --git a/scripts/__tests__/HorizonXText/edgeCases/multipleStores.test.tsx b/scripts/__tests__/HorizonXTest/edgeCases/multipleStores.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/edgeCases/multipleStores.test.tsx rename to scripts/__tests__/HorizonXTest/edgeCases/multipleStores.test.tsx diff --git a/scripts/__tests__/HorizonXText/edgeCases/proxy.test.tsx b/scripts/__tests__/HorizonXTest/edgeCases/proxy.test.tsx similarity index 100% rename from scripts/__tests__/HorizonXText/edgeCases/proxy.test.tsx rename to scripts/__tests__/HorizonXTest/edgeCases/proxy.test.tsx From 47e7baaa4865c775ff08eb09242fc9bfb4c36b64 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 15 Feb 2023 15:11:46 +0800 Subject: [PATCH 31/74] Match-id-a8d664cc1a7599bfd5612227fe90df71d89342af --- .cloudbuild/test.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.cloudbuild/test.yml b/.cloudbuild/test.yml index 2681012f..a5c7242f 100644 --- a/.cloudbuild/test.yml +++ b/.cloudbuild/test.yml @@ -23,8 +23,7 @@ steps: path: horizon-core - gitlab: url: https://szv-open.codehub.huawei.com/innersource/fenghuang/horizon/horizon-test.git - - branch: one_tree_dev + branch: master path: horizon-test BUILD: - build_execute: From 2293727e6ba941e1de7e1583422d13cb001bca6d Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 17 Feb 2023 17:37:23 +0800 Subject: [PATCH 32/74] Match-id-b078d7aff2ccd280f952cbc2be1dce941dbb61ab --- libs/horizon/src/horizonx/devtools/index.ts | 22 +++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 192ece34..d0182afc 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -25,6 +25,7 @@ function makeStoreSnapshot({ type, data }) { return snapshot; } +<<<<<<< Updated upstream // safely serializes variables containing values wrapped in Proxy object function getType(value) { if (!value) return 'nullish'; @@ -41,6 +42,9 @@ function getType(value) { } function makeProxySnapshot(obj) { const type = getType(obj); +======= +function makeProxySnapshot(obj, visited: any[] = []) { +>>>>>>> Stashed changes let clone; try { @@ -54,6 +58,7 @@ function makeProxySnapshot(obj) { if (type === 'function') { return obj.toString(); } +<<<<<<< Updated upstream // VNODE if (type === 'vnode') { return { @@ -93,14 +98,23 @@ function makeProxySnapshot(obj) { } // ARRAY if (type === 'array') { +======= + if (Array.isArray(obj)) { + if (visited.some(item => item === obj)) return ``; +>>>>>>> Stashed changes clone = []; - obj.forEach(item => clone.push(makeProxySnapshot(item))); + obj.forEach(item => clone.push(makeProxySnapshot(item, visited.concat([obj])))); return clone; +<<<<<<< Updated upstream } // OBJECT if (type === 'object') { +======= + } else if (typeof obj === 'object') { + if (visited.some(item => item === obj)) return ``; +>>>>>>> Stashed changes clone = {}; - Object.entries(obj).forEach(([id, value]) => (clone[id] = makeProxySnapshot(value))); + Object.entries(obj).forEach(([id, value]) => (clone[id] = makeProxySnapshot(value, visited.concat([obj])))); return clone; } // PRIMITIVE @@ -134,6 +148,10 @@ function getAffectedComponents() { const keys = Object.keys(allStores); let res = {}; keys.forEach(key => { + if (!allStores[key].$config.state._horizonObserver.keyVNodes) { + res[key] = []; + return; + } const subRes = new Set(); const process = Array.from(allStores[key].$config.state._horizonObserver.keyVNodes.values()); while (process.length) { From 20694046d636925059736e1c5e374578f37d187c Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 17 Feb 2023 18:17:06 +0800 Subject: [PATCH 33/74] Match-id-e1124faaed58084937cabc0567c12f8c01af6016 --- libs/horizon/src/horizonx/devtools/index.ts | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index d0182afc..73ab8b45 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -25,7 +25,6 @@ function makeStoreSnapshot({ type, data }) { return snapshot; } -<<<<<<< Updated upstream // safely serializes variables containing values wrapped in Proxy object function getType(value) { if (!value) return 'nullish'; @@ -40,11 +39,8 @@ function getType(value) { if (typeof value === 'object') return 'object'; return 'primitive'; } -function makeProxySnapshot(obj) { - const type = getType(obj); -======= function makeProxySnapshot(obj, visited: any[] = []) { ->>>>>>> Stashed changes + const type = getType(obj); let clone; try { @@ -58,7 +54,6 @@ function makeProxySnapshot(obj, visited: any[] = []) { if (type === 'function') { return obj.toString(); } -<<<<<<< Updated upstream // VNODE if (type === 'vnode') { return { @@ -98,21 +93,14 @@ function makeProxySnapshot(obj, visited: any[] = []) { } // ARRAY if (type === 'array') { -======= - if (Array.isArray(obj)) { if (visited.some(item => item === obj)) return ``; ->>>>>>> Stashed changes clone = []; obj.forEach(item => clone.push(makeProxySnapshot(item, visited.concat([obj])))); return clone; -<<<<<<< Updated upstream } // OBJECT if (type === 'object') { -======= - } else if (typeof obj === 'object') { if (visited.some(item => item === obj)) return ``; ->>>>>>> Stashed changes clone = {}; Object.entries(obj).forEach(([id, value]) => (clone[id] = makeProxySnapshot(value, visited.concat([obj])))); return clone; From cdedd81f6ba544fedf1c961d865ba37b7e2ef3db Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 17 Feb 2023 18:20:18 +0800 Subject: [PATCH 34/74] Match-id-968a747b74974ffdecf73d9500293487f6bb2592 --- libs/horizon/src/horizonx/devtools/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 25428623..11818bdc 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -175,11 +175,7 @@ window.addEventListener('message', messageEvent => { } // executes store action -<<<<<<< HEAD - if (messageEvent.data.payload.type === 'horizonx executue action') { -======= if (messageEvent.data?.payload?.type === 'horizonx executue action') { ->>>>>>> master const data = messageEvent.data.payload.data; const store = getStore(data.storeId); if (!store?.[data.action]) return; From c7daceca48345eca18ab35a5d0196098f38e419c Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 17 Feb 2023 19:40:21 +0800 Subject: [PATCH 35/74] Match-id-6f25fe3657489f320737a4456ef62fb9abe367af --- libs/horizon/src/horizonx/devtools/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 11818bdc..7c14a438 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -186,7 +186,7 @@ window.addEventListener('message', messageEvent => { } // queues store action - if (messageEvent.data.payload.type === 'horizonx queue action') { + if (messageEvent?.data?.payload?.type === 'horizonx queue action') { const data = messageEvent.data.payload.data; const store = getStore(data.storeId); if (!store?.[data.action]) return; @@ -197,7 +197,7 @@ window.addEventListener('message', messageEvent => { } // queues change store state - if (messageEvent.data.payload.type === 'horizonx change state') { + if (messageEvent?.data?.payload?.type === 'horizonx change state') { const data = messageEvent.data.payload; const store = getStore(data.storeId); if (!store) return; From bba1a7a7db3efda81597f8d6d6088bcce093d378 Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 17 Feb 2023 21:24:21 +0800 Subject: [PATCH 36/74] Match-id-801667129e07b45fdef4e1690678d5b07c75551e --- .../proxy/handlers copy/ArrayProxyHandler.ts | 85 ------- .../handlers copy/CollectionProxyHandler.ts | 235 ------------------ .../proxy/handlers copy/ObjectProxyHandler.ts | 92 ------- 3 files changed, 412 deletions(-) delete mode 100644 libs/horizon/src/horizonx/proxy/handlers copy/ArrayProxyHandler.ts delete mode 100644 libs/horizon/src/horizonx/proxy/handlers copy/CollectionProxyHandler.ts delete mode 100644 libs/horizon/src/horizonx/proxy/handlers copy/ObjectProxyHandler.ts diff --git a/libs/horizon/src/horizonx/proxy/handlers copy/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers copy/ArrayProxyHandler.ts deleted file mode 100644 index 9e9c2845..00000000 --- a/libs/horizon/src/horizonx/proxy/handlers copy/ArrayProxyHandler.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -import { getObserver } from '../ProxyHandler'; -import { isSame, isValidIntegerKey } from '../../CommonUtils'; -import { get as objectGet } from './ObjectProxyHandler'; -import { resolveMutation } from '../../CommonUtils'; -import { isPanelActive } from '../../devtools'; - -export function createArrayProxy(rawObj: any[]): any[] { - const handle = { - get, - set, - }; - - return new Proxy(rawObj, handle); -} - -function get(rawObj: any[], key: string, receiver: any) { - if (key === 'watch') { - const observer = getObserver(rawObj); - - return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; - } - - if (isValidIntegerKey(key) || key === 'length') { - return objectGet(rawObj, key, receiver); - } - - return Reflect.get(rawObj, key, receiver); -} - -function set(rawObj: any[], key: string, value: any, receiver: any) { - const oldValue = rawObj[key]; - const oldLength = rawObj.length; - const newValue = value; - - const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - - const ret = Reflect.set(rawObj, key, newValue, receiver); - - const newLength = rawObj.length; - const observer = getObserver(rawObj); - - const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : { mutation: true, from: [], to: rawObj }; - - if (!isSame(newValue, oldValue)) { - // 值不一样,触发监听器 - if (observer.watchers?.[key]) { - observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, mutation); - }); - } - - // 触发属性变化 - observer.setProp(key, mutation); - } - - if (oldLength !== newLength) { - // 触发数组的大小变化 - observer.setProp('length', mutation); - } - - return ret; -} diff --git a/libs/horizon/src/horizonx/proxy/handlers copy/CollectionProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers copy/CollectionProxyHandler.ts deleted file mode 100644 index 05174cc5..00000000 --- a/libs/horizon/src/horizonx/proxy/handlers copy/CollectionProxyHandler.ts +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; -import { isMap, isWeakMap, isSame } from '../../CommonUtils'; -import { resolveMutation } from '../../CommonUtils'; -import { isPanelActive } from '../../devtools'; - -const COLLECTION_CHANGE = '_collectionChange'; -const handler = { - get, - set, - add, - delete: deleteFun, - clear, - has, - entries, - forEach, - keys, - values, - // 判断Symbol类型,兼容IE - [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf, -}; - -export function createCollectionProxy(rawObj: Object, hookObserver = true): Object { - const boundHandler = {}; - Object.entries(handler).forEach(([id, val]) => { - boundHandler[id] = (...args: any[]) => { - return (val as any)(...args, hookObserver); - }; - }); - return new Proxy(rawObj, { ...boundHandler }); -} - -function get(rawObj: { size: number }, key: any, receiver: any): any { - if (key === 'size') { - return size(rawObj); - } else if (key === 'get') { - return getFun.bind(null, rawObj); - } else if (Object.prototype.hasOwnProperty.call(handler, key)) { - const value = Reflect.get(handler, key, receiver); - return value.bind(null, rawObj); - } else if (key === 'watch') { - const observer = getObserver(rawObj); - - return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; - } - - return Reflect.get(rawObj, key, receiver); -} - -function getFun(rawObj: { get: (key: any) => any }, key: any) { - const observer = getObserver(rawObj); - observer.useProp(key); - - const value = rawObj.get(key); - // 对于value也需要进一步代理 - const valProxy = createProxy(value, hookObserverMap.get(rawObj)); - - return valProxy; -} - -// Map的set方法 -function set( - rawObj: { get: (key: any) => any; set: (key: any, value: any) => any; has: (key: any) => boolean }, - key: any, - value: any -) { - const oldValue = rawObj.get(key); - const newValue = value; - rawObj.set(key, newValue); - const valChange = !isSame(newValue, oldValue); - const observer = getObserver(rawObj); - - const mutation = isPanelActive() ? resolveMutation(oldValue, rawObj) : { mutation: true, from: null, to: rawObj }; - - if (valChange || !rawObj.has(key)) { - observer.setProp(COLLECTION_CHANGE, mutation); - } - - if (valChange) { - if (observer.watchers?.[key]) { - observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, mutation); - }); - } - - observer.setProp(key, mutation); - } - - return rawObj; -} - -// Set的add方法 -function add(rawObj: { add: (any) => void; set: (string, any) => any; has: (any) => boolean }, value: any): Object { - const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - if (!rawObj.has(value)) { - rawObj.add(value); - - const observer = getObserver(rawObj); - const mutation = isPanelActive() - ? resolveMutation(oldCollection, rawObj) - : { mutation: true, from: null, to: rawObj }; - observer.setProp(value, mutation); - observer.setProp(COLLECTION_CHANGE, mutation); - } - - return rawObj; -} - -function has(rawObj: { has: (string) => boolean }, key: any): boolean { - const observer = getObserver(rawObj); - observer.useProp(key); - - return rawObj.has(key); -} - -function clear(rawObj: { size: number; clear: () => void }) { - const oldSize = rawObj.size; - rawObj.clear(); - - if (oldSize > 0) { - const observer = getObserver(rawObj); - observer.allChange(); - } -} - -function deleteFun(rawObj: { has: (key: any) => boolean; delete: (key: any) => void }, key: any) { - const oldCollection = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - if (rawObj.has(key)) { - rawObj.delete(key); - - const observer = getObserver(rawObj); - const mutation = isPanelActive() - ? resolveMutation(oldCollection, rawObj) - : { mutation: true, from: null, to: rawObj }; - observer.setProp(key, mutation); - observer.setProp(COLLECTION_CHANGE, mutation); - - return true; - } - - return false; -} - -function size(rawObj: { size: number }) { - const observer = getObserver(rawObj); - observer.useProp(COLLECTION_CHANGE); - return rawObj.size; -} - -function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.keys()); -} - -function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.values()); -} - -function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.entries(), true); -} - -function forOf(rawObj: { - entries: () => { next: () => { value: any; done: boolean } }; - values: () => { next: () => { value: any; done: boolean } }; -}) { - const isMapType = isMap(rawObj) || isWeakMap(rawObj); - const iterator = isMapType ? rawObj.entries() : rawObj.values(); - return wrapIterator(rawObj, iterator, isMapType); -} - -function forEach( - rawObj: { forEach: (callback: (value: any, key: any) => void) => void }, - callback: (valProxy: any, keyProxy: any, rawObj: any) => void -) { - const observer = getObserver(rawObj); - observer.useProp(COLLECTION_CHANGE); - rawObj.forEach((value, key) => { - const valProxy = createProxy(value, hookObserverMap.get(rawObj)); - const keyProxy = createProxy(key, hookObserverMap.get(rawObj)); - // 最后一个参数要返回代理对象 - return callback(valProxy, keyProxy, rawObj); - }); -} - -function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }, isPair = false) { - const observer = getObserver(rawObj); - const hookObserver = hookObserverMap.get(rawObj); - observer.useProp(COLLECTION_CHANGE); - - return { - next() { - const { value, done } = rawIt.next(); - if (done) { - return { value: createProxy(value, hookObserver), done }; - } - - observer.useProp(COLLECTION_CHANGE); - - let newVal; - if (isPair) { - newVal = [createProxy(value[0], hookObserver), createProxy(value[1], hookObserver)]; - } else { - newVal = createProxy(value, hookObserver); - } - - return { value: newVal, done }; - }, - // 判断Symbol类型,兼容IE - [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() { - return this; - }, - }; -} diff --git a/libs/horizon/src/horizonx/proxy/handlers copy/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers copy/ObjectProxyHandler.ts deleted file mode 100644 index 721be056..00000000 --- a/libs/horizon/src/horizonx/proxy/handlers copy/ObjectProxyHandler.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -import { isSame, resolveMutation } from '../../CommonUtils'; -import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; -import { OBSERVER_KEY } from '../../Constants'; -import { isPanelActive } from '../../devtools'; - -export function createObjectProxy(rawObj: T, singleLevel = false): ProxyHandler { - const proxy = new Proxy(rawObj, { - get: (...args) => get(...args, singleLevel), - set, - }); - - return proxy; -} - -export function get(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any { - // The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. - if (key === OBSERVER_KEY) { - return undefined; - } - - const observer = getObserver(rawObj); - - if (key === 'watch') { - return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; - } - - if (key === 'addListener') { - return observer.addListener.bind(observer); - } - - if (key === 'removeListener') { - return observer.removeListener.bind(observer); - } - - observer.useProp(key); - - const value = Reflect.get(rawObj, key, receiver); - - // 对于prototype不做代理 - if (key !== 'prototype') { - // 对于value也需要进一步代理 - const valProxy = singleLevel ? value : createProxy(value, hookObserverMap.get(rawObj)); - - return valProxy; - } - - return value; -} - -export function set(rawObj: object, key: string, value: any, receiver: any): boolean { - const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - const observer = getObserver(rawObj); - - const oldValue = rawObj[key]; - const newValue = value; - - const ret = Reflect.set(rawObj, key, newValue, receiver); - const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : { mutation: true, from: null, to: rawObj }; - - if (!isSame(newValue, oldValue)) { - if (observer.watchers?.[key]) { - observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, mutation); - }); - } - observer.setProp(key, mutation); - } - return ret; -} From 5cac630e3404d9ea6b7df9efccdc2b4e7fc58ffe Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 21 Feb 2023 18:20:13 +0800 Subject: [PATCH 37/74] Match-id-c19116d686a2df946cbfab9c8e3f9e8d79ddce4b --- CHANGELOG.md | 3 ++ libs/horizon/global.d.ts | 1 + libs/horizon/package.json | 2 +- libs/horizon/src/renderer/TreeBuilder.ts | 3 ++ libs/horizon/src/renderer/vnode/VNode.ts | 2 +- libs/horizon/src/renderer/vnode/VNodeUtils.ts | 43 +++++++++++++------ package.json | 2 - 7 files changed, 38 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12d81d5e..23d98919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.39 (2023-02-21) +- **core**: #102 使用eview-react组件Dialog时,关闭组件horizon报错,且无法再打开弹框 + ## 0.0.38 (2023-02-01) - **core**: 增加flushSync接口 diff --git a/libs/horizon/global.d.ts b/libs/horizon/global.d.ts index 9da3e079..598a8670 100644 --- a/libs/horizon/global.d.ts +++ b/libs/horizon/global.d.ts @@ -20,3 +20,4 @@ declare var isDev: boolean; declare var isTest: boolean; declare const __VERSION__: string; declare var setImmediate: Function; +declare var __HORIZON_DEV_HOOK__: any; diff --git a/libs/horizon/package.json b/libs/horizon/package.json index c9a179bc..d70cdf4c 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.38", + "version": "0.0.39", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 2abace80..7d37de2f 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -312,6 +312,7 @@ function recoverTreeContext(vNode: VNode) { } parent = parent.parent; } + contextProviders.forEach(node => { setContext(node, node.props.value); }); @@ -336,6 +337,7 @@ function resetTreeContext(vNode: VNode) { function renderFromRoot(treeRoot) { runAsyncEffects(); pushCurrentRoot(treeRoot); + // 1. 构建vNode树 buildVNodeTree(treeRoot); @@ -346,6 +348,7 @@ function renderFromRoot(treeRoot) { // 2. 提交变更 submitToRender(treeRoot); + popCurrentRoot(); if (window.__HORIZON_DEV_HOOK__) { const hook = window.__HORIZON_DEV_HOOK__; diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index a5ec09bc..4214e928 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -89,7 +89,7 @@ export class VNode { oldChild: VNode | null = null; promiseResolve: boolean; // suspense的promise是否resolve devProps: any; // 用于dev插件临时保存更新props值 - suspenseState: SuspenseState; + suspenseState: SuspenseState | null; path = ''; // 保存从根到本节点的路径 diff --git a/libs/horizon/src/renderer/vnode/VNodeUtils.ts b/libs/horizon/src/renderer/vnode/VNodeUtils.ts index 6616d4c5..7e55f48d 100644 --- a/libs/horizon/src/renderer/vnode/VNodeUtils.ts +++ b/libs/horizon/src/renderer/vnode/VNodeUtils.ts @@ -17,15 +17,19 @@ * 提供:vNode的“遍历”,“查找”,“判断”的相关工具方法 */ -import type { VNode } from '../Types'; +import type {VNode} from '../Types'; -import { DomComponent, DomPortal, DomText, TreeRoot } from './VNodeTags'; -import { isComment } from '../../dom/utils/Common'; -import { getNearestVNode } from '../../dom/DOMInternalKeys'; -import { Addition, InitFlag } from './VNodeFlags'; +import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags'; +import {isComment} from '../../dom/utils/Common'; +import {getNearestVNode} from '../../dom/DOMInternalKeys'; +import {Addition, InitFlag} from './VNodeFlags'; -export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinish?: Function) { - let node: VNode | null = beginVNode; +export function travelChildren( + beginVNode: VNode | null, + handleVNode: (node: VNode) => void, + isFinish?: (node: VNode) => boolean +) { + let node = beginVNode; while (node !== null) { if (isFinish && isFinish(node)) { @@ -41,15 +45,16 @@ export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinis // 从beginVNode开始深度遍历vNode树,对每个vNode调用handleVNode方法 export function travelVNodeTree( beginVNode: VNode, - handleVNode: Function, + handleVNode: (node: VNode) => VNode | boolean | null | void, childFilter: ((node: VNode) => boolean) | null, // 返回true不处理child finishVNode: VNode, // 结束遍历节点,有时候和beginVNode不相同 - handleWhenToParent: Function | null -): VNode | null { + handleWhenToParent: ((node: VNode) => void) | null +): VNode | boolean | null | void { let node = beginVNode; while (true) { const ret = handleVNode(node); + // 如果处理一个vNode时有返回值,则中断遍历 if (ret) { return ret; @@ -68,6 +73,8 @@ export function travelVNodeTree( return null; } + const isFun = typeof handleWhenToParent === 'function'; + // 找兄弟,没有就往上再找兄弟 while (node.next === null) { if (node.parent === null || node.parent === finishVNode) { @@ -75,8 +82,8 @@ export function travelVNodeTree( } node = node.parent; - if (typeof handleWhenToParent === 'function') { - handleWhenToParent(node); + if (isFun) { + handleWhenToParent!(node); } } // 找到兄弟 @@ -89,14 +96,20 @@ export function travelVNodeTree( // 置空vNode export function clearVNode(vNode: VNode) { vNode.isCleared = true; + + // 孩子节点的parent也置空 + travelChildren(vNode.child, (node) => { + node.parent = null; + }); vNode.child = null; + + vNode.parent = null; vNode.next = null; vNode.depContexts = null; vNode.dirtyNodes = null; vNode.state = null; vNode.hooks = null; vNode.props = null; - vNode.parent = null; vNode.suspenseState = null; vNode.changeList = null; vNode.effectList = null; @@ -129,7 +142,7 @@ function isDomContainer(vNode: VNode): boolean { } export function findDomVNode(vNode: VNode): VNode | null { - return travelVNodeTree( + const ret = travelVNodeTree( vNode, node => { if (node.tag === DomComponent || node.tag === DomText) { @@ -141,6 +154,8 @@ export function findDomVNode(vNode: VNode): VNode | null { vNode, null ); + + return ret as VNode | null; } export function findDOMByClassInst(inst) { diff --git a/package.json b/package.json index f2d728f2..eae43edd 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,6 @@ "prettier": "prettier -w libs/**/*.ts", "build": "rollup --config ./scripts/rollup/rollup.config.js", "build:watch": "rollup --watch --config ./scripts/rollup/rollup.config.js", - "build:3rdLib": "node ./scripts/gen3rdLib.js build:3rdLib", - "build:3rdLib-dev": "npm run build & node ./scripts/gen3rdLib.js build:3rdLib-dev", "build:horizon3rdLib-dev": "npm run build & node ./scripts/gen3rdLib.js build:horizon3rdLib-dev", "build-types": "tsc -p ./libs/horizon/index.ts --emitDeclarationOnly --declaration --declarationDir ./build/horizon/@types --skipLibCheck || echo \"WARNING: TSC exited with status $?\"", "debug-test": "yarn test --debug", From 17c7a6ef39a81f4c77e31c54421ae865630f8b3a Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 8 Mar 2023 17:03:08 +0800 Subject: [PATCH 38/74] Match-id-583460b5b12801f5796435bb677b1f87a7994a82 --- CHANGELOG.md | 5 ++++ libs/horizon/package.json | 2 +- .../dom/DOMPropertiesHandler/StyleHandler.ts | 8 +++-- .../src/renderer/render/MemoComponent.ts | 4 ++- .../ComponentTest/FunctionComponent.test.js | 30 +++++++++++++++++++ 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23d98919..e67f773f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.0.40 (2023-03-08) +- **core**: #103 使用Memo、React.forwardRef包装组件后,defaultProps失效 +- **core**: #104 --max-segment-num的style无法使用 +- **core**: 状态管理器的优化 + ## 0.0.39 (2023-02-21) - **core**: #102 使用eview-react组件Dialog时,关闭组件horizon报错,且无法再打开弹框 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index d70cdf4c..7393bdbc 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.39", + "version": "0.0.40", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts index 012f438f..c3954fa1 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts @@ -54,8 +54,12 @@ export function setStyles(dom, styles) { const style = dom.style; Object.keys(styles).forEach(name => { const styleVal = styles[name]; - - style[name] = adjustStyleValue(name, styleVal); + // 以--开始的样式直接设置即可 + if (name.indexOf('--') === 0) { + style.setProperty(name, styleVal); + } else { + style[name] = adjustStyleValue(name, styleVal); + } }); } diff --git a/libs/horizon/src/renderer/render/MemoComponent.ts b/libs/horizon/src/renderer/render/MemoComponent.ts index 32aa4bf3..74bf093e 100644 --- a/libs/horizon/src/renderer/render/MemoComponent.ts +++ b/libs/horizon/src/renderer/render/MemoComponent.ts @@ -31,7 +31,9 @@ export function bubbleRender() {} export function captureMemoComponent(processing: VNode, shouldUpdate: boolean): VNode | null { const Component = processing.type; // 合并 函数组件或类组件 的defaultProps - const newProps = mergeDefaultProps(Component, processing.props); + let newProps = mergeDefaultProps(Component, processing.props); + // 解决Horizon.memo(Horizon.forwardRef(()=>{}))两层包装的场景 + newProps = mergeDefaultProps(Component.type, newProps); if (processing.isCreated) { let newChild: VNode | null = null; diff --git a/scripts/__tests__/ComponentTest/FunctionComponent.test.js b/scripts/__tests__/ComponentTest/FunctionComponent.test.js index 717a0d2f..2c8dedf9 100644 --- a/scripts/__tests__/ComponentTest/FunctionComponent.test.js +++ b/scripts/__tests__/ComponentTest/FunctionComponent.test.js @@ -60,4 +60,34 @@ describe('FunctionComponent Test', () => { expect(realNode).toBe(null); }); + it('测试函数组件的defaultProps:Horizon.memo(Horizon.forwardRef(()=>{}))两层包装的场景后,defaultProps依然正常', () => { + const App = () => { + return ; + }; + + const DefaultPropsComp = Horizon.forwardRef(props => { + return
{props.name}
; + }); + DefaultPropsComp.defaultProps = { + name: 'Hello!', + }; + const DefaultPropsCompMemo = Horizon.memo(DefaultPropsComp); + + Horizon.render(, container); + expect(container.querySelector('div').innerHTML).toBe('Hello!'); + }); + + it('测试', () => { + const App = () => { + return ; + }; + + const StyleComp = props => { + return
{props.name}
; + }; + + Horizon.render(, container); + expect(container.querySelector('div').style['_values']['--max-segment-num']).toBe(10); + }); + }); From 1f396143970696d39824855fa626423812f89b9f Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 15 Mar 2023 21:04:26 +0800 Subject: [PATCH 39/74] Match-id-42e6b77b81d3f947899d8ede0998c2c788be124a --- CHANGELOG.md | 4 ++ libs/horizon/index.ts | 25 +++++++---- libs/horizon/package.json | 2 +- .../src/renderer/components/ForwardRef.ts | 1 + scripts/rollup/rollup.config.js | 42 ++++++++++++------- 5 files changed, 49 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e67f773f..a9f1b53f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.41 (2023-03-15) +- **core**: #105 redux + forwardRef组合使用场景会报错,redux获取组件类型不对 +- **core**: 增加jsx-dev-runtime文件,给vite使用 + ## 0.0.40 (2023-03-08) - **core**: #103 使用Memo、React.forwardRef包装组件后,defaultProps失效 - **core**: #104 --max-segment-num的style无法使用 diff --git a/libs/horizon/index.ts b/libs/horizon/index.ts index 5fffa7c9..02244280 100644 --- a/libs/horizon/index.ts +++ b/libs/horizon/index.ts @@ -18,6 +18,8 @@ import { TYPE_PROFILER as Profiler, TYPE_STRICT_MODE as StrictMode, TYPE_SUSPENSE as Suspense, + TYPE_FORWARD_REF as ForwardRef, + TYPE_MEMO as Memo, } from './src/external/JSXElementType'; import { Component, PureComponent } from './src/renderer/components/BaseClassComponent'; @@ -87,10 +89,6 @@ const Horizon = { useReducer, useRef, useState, - Fragment, - Profiler, - StrictMode, - Suspense, createElement, cloneElement, isValidElement, @@ -115,9 +113,16 @@ const Horizon = { isPortal, isContextProvider, isContextConsumer, + ForwardRef, + Memo, + Fragment, + Profiler, + StrictMode, + Suspense, }; -export const version = __VERSION__; +// export const version = __VERSION__; +export const version = '0.0.40'; export { Children, createRef, @@ -137,10 +142,6 @@ export { useReducer, useRef, useState, - Fragment, - Profiler, - StrictMode, - Suspense, createElement, cloneElement, isValidElement, @@ -167,6 +168,12 @@ export { isPortal, isContextProvider, isContextConsumer, + ForwardRef, + Memo, + Fragment, + Profiler, + StrictMode, + Suspense, }; export default Horizon; diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 7393bdbc..c28cbac2 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.40", + "version": "0.0.41", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/renderer/components/ForwardRef.ts b/libs/horizon/src/renderer/components/ForwardRef.ts index 80270175..509d4c93 100644 --- a/libs/horizon/src/renderer/components/ForwardRef.ts +++ b/libs/horizon/src/renderer/components/ForwardRef.ts @@ -18,6 +18,7 @@ import { TYPE_FORWARD_REF } from '../../external/JSXElementType'; export function forwardRef(render: Function) { return { vtype: TYPE_FORWARD_REF, + $$typeof: TYPE_FORWARD_REF, // 规避三方件hoist-non-react-statics中,通过$$typeof获取类型,但获取不到导致的错误 render, }; } diff --git a/scripts/rollup/rollup.config.js b/scripts/rollup/rollup.config.js index 4e2e7cfd..b1677e44 100644 --- a/scripts/rollup/rollup.config.js +++ b/scripts/rollup/rollup.config.js @@ -20,8 +20,8 @@ import fs from 'fs'; import replace from '@rollup/plugin-replace'; import copy from './copy-plugin'; import execute from 'rollup-plugin-execute'; -import { terser } from 'rollup-plugin-terser'; -import { version as horizonVersion } from '@cloudsop/horizon/package.json'; +import {terser} from 'rollup-plugin-terser'; +import {version as horizonVersion} from '@cloudsop/horizon/package.json'; const extensions = ['.js', '.ts']; @@ -107,17 +107,29 @@ function genConfig(mode) { } function genJSXRuntimeConfig(mode) { - return { - input: path.resolve(libDir, 'jsx-runtime.ts'), - output: [ - { - file: outputResolve('jsx-runtime.js'), - format: 'cjs', - } - ], - plugins: [ - ...getBasicPlugins(mode) - ] - }; + return { + input: path.resolve(libDir, 'jsx-runtime.ts'), + output: { + file: outputResolve('jsx-runtime.js'), + format: 'cjs', + }, + plugins: [ + ...getBasicPlugins(mode) + ] + }; } -export default [genConfig('development'), genConfig('production'), genJSXRuntimeConfig('')]; + +function genJSXDEVRuntimeConfig(mode) { + return { + input: path.resolve(libDir, 'jsx-dev-runtime.ts'), + output: { + file: outputResolve('jsx-dev-runtime.js'), + format: 'cjs', + }, + plugins: [ + ...getBasicPlugins(mode) + ] + }; +} + +export default [genConfig('development'), genConfig('production'), genJSXRuntimeConfig(''), genJSXDEVRuntimeConfig('')]; From 54d611e3470cab2aa808e8e5b94312eb0963e5c7 Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 16 Mar 2023 12:31:08 +0800 Subject: [PATCH 40/74] Match-id-0338b4ba7000d8c21aa851908bc5dc85900328f5 --- libs/horizon/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/horizon/index.ts b/libs/horizon/index.ts index 02244280..efe6df6b 100644 --- a/libs/horizon/index.ts +++ b/libs/horizon/index.ts @@ -121,8 +121,7 @@ const Horizon = { Suspense, }; -// export const version = __VERSION__; -export const version = '0.0.40'; +export const version = __VERSION__; export { Children, createRef, From b7dc4dc2bdf19281e5b2c3b311bdc931e7c2320f Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 16 Mar 2023 12:47:58 +0800 Subject: [PATCH 41/74] Match-id-2a744b8329166dfe01fb3ba057f453f79ff0d4c8 --- libs/horizon/jsx-dev-runtime.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 libs/horizon/jsx-dev-runtime.ts diff --git a/libs/horizon/jsx-dev-runtime.ts b/libs/horizon/jsx-dev-runtime.ts new file mode 100644 index 00000000..f768f977 --- /dev/null +++ b/libs/horizon/jsx-dev-runtime.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { + TYPE_FRAGMENT as Fragment, +} from './src/external/JSXElementType'; +import { jsx as jsxDEV } from './src/external/JSXElement'; + +export { + jsxDEV, + Fragment +}; From 43602a11f2bab50b373ffd04e766dd2a2fbe18a3 Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 17 Mar 2023 13:46:45 +0800 Subject: [PATCH 42/74] Match-id-3314488ef0f0a3913b65712abcdb657534438dc2 --- .../src/renderer/components/ForwardRef.ts | 16 ++++++++++++---- libs/horizon/src/renderer/components/Memo.ts | 13 +++++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/libs/horizon/src/renderer/components/ForwardRef.ts b/libs/horizon/src/renderer/components/ForwardRef.ts index 509d4c93..77107760 100644 --- a/libs/horizon/src/renderer/components/ForwardRef.ts +++ b/libs/horizon/src/renderer/components/ForwardRef.ts @@ -13,12 +13,20 @@ * See the Mulan PSL v2 for more details. */ -import { TYPE_FORWARD_REF } from '../../external/JSXElementType'; +import {TYPE_FORWARD_REF, TYPE_MEMO} from '../../external/JSXElementType'; export function forwardRef(render: Function) { - return { - vtype: TYPE_FORWARD_REF, - $$typeof: TYPE_FORWARD_REF, // 规避三方件hoist-non-react-statics中,通过$$typeof获取类型,但获取不到导致的错误 + const forwardRefJSXElement = { + $$typeof: TYPE_FORWARD_REF, // 规避三方件hoist-non-react-statics中,通过$$typeof获取类型,但获取不到,导致render被覆盖 render, }; + + // 控制vtype不能修改,规避三方件hoist-non-react-statics修改vtype导致问题 + Object.defineProperty(forwardRefJSXElement, 'vtype', { + configurable: false, + writable: false, + value: TYPE_FORWARD_REF, + }); + + return forwardRefJSXElement; } diff --git a/libs/horizon/src/renderer/components/Memo.ts b/libs/horizon/src/renderer/components/Memo.ts index f8889dd2..0ced1af7 100644 --- a/libs/horizon/src/renderer/components/Memo.ts +++ b/libs/horizon/src/renderer/components/Memo.ts @@ -16,9 +16,18 @@ import { TYPE_MEMO } from '../../external/JSXElementType'; export function memo(type, compare?: (oldProps: Props, newProps: Props) => boolean) { - return { - vtype: TYPE_MEMO, + const memoJSXElement = { + $$typeof: TYPE_MEMO, // 规避三方件hoist-non-react-statics中,通过$$typeof获取类型,但获取不到,导致type被覆盖 type: type, compare: compare === undefined ? null : compare, }; + + // 控制vtype不能修改,规避三方件hoist-non-react-statics修改vtype导致问题 + Object.defineProperty(memoJSXElement, 'vtype', { + configurable: false, + writable: false, + value: TYPE_MEMO, + }); + + return memoJSXElement; } From 639a797ef1c57945de30b6f1dc9cb6ea788587df Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 24 Mar 2023 17:22:30 +0800 Subject: [PATCH 43/74] Match-id-a0e9ecd2323caf055755c2e5a907dffd3a6881a5 --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- libs/horizon/src/renderer/components/ForwardRef.ts | 2 +- libs/horizon/src/renderer/components/Memo.ts | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9f1b53f..580aa79f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.42 (2023-03-24) +- **core**: 解决直接通过defineProperty赋值vtype,enumerable为false,导致vtype为空问题 + ## 0.0.41 (2023-03-15) - **core**: #105 redux + forwardRef组合使用场景会报错,redux获取组件类型不对 - **core**: 增加jsx-dev-runtime文件,给vite使用 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index c28cbac2..0273c7f8 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.41", + "version": "0.0.42", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/renderer/components/ForwardRef.ts b/libs/horizon/src/renderer/components/ForwardRef.ts index 77107760..9db9cec3 100644 --- a/libs/horizon/src/renderer/components/ForwardRef.ts +++ b/libs/horizon/src/renderer/components/ForwardRef.ts @@ -17,6 +17,7 @@ import {TYPE_FORWARD_REF, TYPE_MEMO} from '../../external/JSXElementType'; export function forwardRef(render: Function) { const forwardRefJSXElement = { + vtype: TYPE_FORWARD_REF, $$typeof: TYPE_FORWARD_REF, // 规避三方件hoist-non-react-statics中,通过$$typeof获取类型,但获取不到,导致render被覆盖 render, }; @@ -25,7 +26,6 @@ export function forwardRef(render: Function) { Object.defineProperty(forwardRefJSXElement, 'vtype', { configurable: false, writable: false, - value: TYPE_FORWARD_REF, }); return forwardRefJSXElement; diff --git a/libs/horizon/src/renderer/components/Memo.ts b/libs/horizon/src/renderer/components/Memo.ts index 0ced1af7..6ae03105 100644 --- a/libs/horizon/src/renderer/components/Memo.ts +++ b/libs/horizon/src/renderer/components/Memo.ts @@ -17,6 +17,7 @@ import { TYPE_MEMO } from '../../external/JSXElementType'; export function memo(type, compare?: (oldProps: Props, newProps: Props) => boolean) { const memoJSXElement = { + vtype: TYPE_MEMO, $$typeof: TYPE_MEMO, // 规避三方件hoist-non-react-statics中,通过$$typeof获取类型,但获取不到,导致type被覆盖 type: type, compare: compare === undefined ? null : compare, @@ -26,7 +27,6 @@ export function memo(type, compare?: (oldProps: Props, newProps: Props) = Object.defineProperty(memoJSXElement, 'vtype', { configurable: false, writable: false, - value: TYPE_MEMO, }); return memoJSXElement; From e8a1e5951a006fc57ddef8c21e4eff6342ad82f5 Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 28 Mar 2023 19:45:58 +0800 Subject: [PATCH 44/74] Match-id-094caf9a8c8fd7bbc214a6ea1eee473a5bb4f8bf --- libs/horizon/src/external/TestUtil.ts | 15 ++++-- .../src/renderer/submit/HookEffectHandler.ts | 5 ++ scripts/__tests__/ActTest/act.test.js | 53 +++++++++++++++++++ .../__tests__/ComponentTest/ClassRefs.test.js | 51 ++++++++++++++++++ 4 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 scripts/__tests__/ActTest/act.test.js create mode 100644 scripts/__tests__/ComponentTest/ClassRefs.test.js diff --git a/libs/horizon/src/external/TestUtil.ts b/libs/horizon/src/external/TestUtil.ts index 26d87660..dc9e3dbd 100644 --- a/libs/horizon/src/external/TestUtil.ts +++ b/libs/horizon/src/external/TestUtil.ts @@ -16,7 +16,7 @@ import {asyncUpdates} from '../renderer/TreeBuilder'; import {callRenderQueueImmediate} from '../renderer/taskExecutor/RenderQueue'; -import {runAsyncEffects} from '../renderer/submit/HookEffectHandler'; +import {hasAsyncEffects, runAsyncEffects} from '../renderer/submit/HookEffectHandler'; import {isPromise} from '../renderer/ErrorHandler'; interface Thenable { @@ -62,11 +62,18 @@ function act(fun: () => void | Thenable): Thenable { } } +// 防止死循环 +const LOOPING_LIMIT = 50; +let loopingCount = 0; function callRenderQueue() { callRenderQueueImmediate(); - runAsyncEffects(); - // effects可能产生刷新任务,这里再执行一次 - callRenderQueueImmediate(); + + while (hasAsyncEffects() && loopingCount < LOOPING_LIMIT) { + loopingCount++; + runAsyncEffects(); + // effects可能产生刷新任务,这里再执行一次 + callRenderQueueImmediate(); + } } export { diff --git a/libs/horizon/src/renderer/submit/HookEffectHandler.ts b/libs/horizon/src/renderer/submit/HookEffectHandler.ts index 140eceef..c7db9c32 100644 --- a/libs/horizon/src/renderer/submit/HookEffectHandler.ts +++ b/libs/horizon/src/renderer/submit/HookEffectHandler.ts @@ -25,6 +25,11 @@ import { EffectConstant } from '../hooks/EffectConstant'; let hookEffects: Array = []; let hookRemoveEffects: Array = []; + +export function hasAsyncEffects() { + return hookEffects.length > 0 || hookRemoveEffects.length > 0; +} + // 是否正在异步调度effects let isScheduling = false; diff --git a/scripts/__tests__/ActTest/act.test.js b/scripts/__tests__/ActTest/act.test.js new file mode 100644 index 00000000..3ee055df --- /dev/null +++ b/scripts/__tests__/ActTest/act.test.js @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import Horizon, { render, useState, act, useEffect } from '@cloudsop/horizon/index.ts'; + +describe('Horizon.act function Test', () => { + it('Parent can get Child instance by refs', function () { + const Parent = props => { + const [buttonOptions, setBtn] = useState([]); + const [checkedRows, setCheckedRows] = useState([]); + + useEffect(() => { + setBtn([1, 2, 3]); + }, [checkedRows.length]); + + return ( +
+ +
+ ); + }; + + const Child = props => { + const { buttonOptions } = props; + const [btnList, setBtnList] = useState(0); + + useEffect(() => { + setBtnList(buttonOptions.length); + }, [buttonOptions]); + + return
{btnList}
; + }; + + act(() => { + render(, container); + }); + + // act能够等待useEffect触发的update完成 + expect(container.querySelector('#childDiv').innerHTML).toEqual('3'); + }); +}); diff --git a/scripts/__tests__/ComponentTest/ClassRefs.test.js b/scripts/__tests__/ComponentTest/ClassRefs.test.js new file mode 100644 index 00000000..d5e91d29 --- /dev/null +++ b/scripts/__tests__/ComponentTest/ClassRefs.test.js @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import * as Horizon from '@cloudsop/horizon/index.ts'; + +describe('Class refs Test', () => { + it('Parent can get Child instance by refs', function () { + let pInst; + + class Parent extends Horizon.Component { + componentDidMount() { + pInst = this; + } + + render() { + return ( +
+ +
childDiv
+
+
+ ); + } + } + + class Child extends Horizon.Component { + state = { y: 0 }; + + render() { + return
{this.props.children}
; + } + } + + Horizon.render(, container); + + expect(pInst.refs['child'].state.y).toEqual(0); + expect(pInst.refs['childDiv'].innerHTML).toEqual('childDiv'); + }); +}); From 760e43ca2268a43dfb42866626704f09aba41b20 Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 30 Mar 2023 12:00:14 +0800 Subject: [PATCH 45/74] Match-id-ed630d91f14b98c6763db5bdf4aad670e8d920d6 --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- scripts/__tests__/ActTest/act.test.js | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 580aa79f..e40a7703 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.43 (2023-03-30) +- **core**: 解决act方法无法等待useEffect触发的更新完成问题 + ## 0.0.42 (2023-03-24) - **core**: 解决直接通过defineProperty赋值vtype,enumerable为false,导致vtype为空问题 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 0273c7f8..e5feca61 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.42", + "version": "0.0.43", "homepage": "", "bugs": "", "main": "index.js", diff --git a/scripts/__tests__/ActTest/act.test.js b/scripts/__tests__/ActTest/act.test.js index 3ee055df..00f719ca 100644 --- a/scripts/__tests__/ActTest/act.test.js +++ b/scripts/__tests__/ActTest/act.test.js @@ -16,7 +16,7 @@ import Horizon, { render, useState, act, useEffect } from '@cloudsop/horizon/index.ts'; describe('Horizon.act function Test', () => { - it('Parent can get Child instance by refs', function () { + it('The act can wait for the useEffect update to complete.', function () { const Parent = props => { const [buttonOptions, setBtn] = useState([]); const [checkedRows, setCheckedRows] = useState([]); From b03b89368b1c84222769b030746d65002a4fd7be Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 3 Apr 2023 13:20:12 +0800 Subject: [PATCH 46/74] Match-id-6732fcf1f87ddf39249416023500ccb13dbff4e1 --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- libs/horizon/src/horizonx/devtools/index.ts | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e40a7703..f42fe512 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.44 (2023-04-03) +- **core**: 修复horizonX-devtool Firefox75报错 + ## 0.0.43 (2023-03-30) - **core**: 解决act方法无法等待useEffect触发的更新完成问题 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index e5feca61..6eb58684 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.43", + "version": "0.0.44", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 7c14a438..23bb6000 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -151,7 +151,7 @@ function getAffectedComponents() { return { name: vnode?.type .toString() - .replace(/\{.*\}/gms, '{...}') + .replace(/\{.*\}/, '{...}') .replace('function ', ''), nodeId: window.__HORIZON_DEV_HOOK__.getVnodeId(vnode), }; From 5fa7c0497a7de33d24bccd20a86349889d238f4f Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 11 Apr 2023 11:23:03 +0800 Subject: [PATCH 47/74] Match-id-4ccb9ad044168b73b7fc920c04d9fafc9bece593 --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f42fe512..e659a986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,12 @@ - **core**: 增加jsx-dev-runtime文件,给vite使用 ## 0.0.40 (2023-03-08) -- **core**: #103 使用Memo、React.forwardRef包装组件后,defaultProps失效 +- **core**: #103 使用Memo、forwardRef包装组件后,defaultProps失效 - **core**: #104 --max-segment-num的style无法使用 - **core**: 状态管理器的优化 ## 0.0.39 (2023-02-21) -- **core**: #102 使用eview-react组件Dialog时,关闭组件horizon报错,且无法再打开弹框 +- **core**: #102 使用eview组件Dialog时,关闭组件horizon报错,且无法再打开弹框 ## 0.0.38 (2023-02-01) - **core**: 增加flushSync接口 @@ -51,7 +51,7 @@ - **core**: fix 修改IE上报Symbol错误的问题 ## 0.0.23 (2022-09-23) -- **core**: #86 兼容ReactIs API +- **core**: #86 兼容Is API ## 0.0.22 (2022-09-22) - **core**: #83 #75 #72 input支持受控 From 42410259008d352bae65b2bf43dbb088669a138e Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 11 Apr 2023 20:05:55 +0800 Subject: [PATCH 48/74] Match-id-ff40d6262153f5fe9dd2054ce2d145237a7545a0 --- libs/horizon/src/event/FormValueController.ts | 58 ++++----- libs/horizon/src/event/HorizonEventMain.ts | 2 +- libs/horizon/src/horizonx/adapters/redux.ts | 24 ++-- libs/horizon/src/renderer/ErrorHandler.ts | 2 +- libs/horizon/src/renderer/hooks/HookMain.ts | 12 +- .../src/renderer/hooks/UseEffectHook.ts | 58 ++++----- .../src/renderer/hooks/UseImperativeHook.ts | 28 ++--- .../src/renderer/hooks/UseReducerHook.ts | 76 ++++++------ .../src/renderer/render/BaseComponent.ts | 2 +- .../src/renderer/render/SuspenseComponent.ts | 2 +- .../src/renderer/submit/HookEffectHandler.ts | 44 +++---- .../src/renderer/submit/LifeCycleHandler.ts | 30 +++-- libs/horizon/src/renderer/submit/Submit.ts | 114 +++++++++--------- .../src/renderer/taskExecutor/BrowserAsync.ts | 16 +-- .../src/renderer/taskExecutor/RenderQueue.ts | 20 +-- libs/horizon/src/renderer/vnode/VNode.ts | 2 + .../src/renderer/vnode/VNodeCreator.ts | 2 + libs/horizon/src/renderer/vnode/VNodeUtils.ts | 14 +-- 18 files changed, 253 insertions(+), 253 deletions(-) diff --git a/libs/horizon/src/event/FormValueController.ts b/libs/horizon/src/event/FormValueController.ts index ac0603e8..3c63a299 100644 --- a/libs/horizon/src/event/FormValueController.ts +++ b/libs/horizon/src/event/FormValueController.ts @@ -37,35 +37,6 @@ export function shouldControlValue(): boolean { return changeEventTargets !== null && changeEventTargets.length > 0; } -// 从缓存队列中对受控组件进行赋值 -export function tryControlValue() { - if (!changeEventTargets) { - return; - } - changeEventTargets.forEach(target => { - controlValue(target); - }); - changeEventTargets = null; -} - -// 受控组件值重新赋值 -function controlValue(target: Element) { - const props = getVNodeProps(target); - if (props) { - const type = getDomTag(target); - switch (type) { - case 'input': - controlInputValue(target, props); - break; - case 'textarea': - updateTextareaValue(target, props); - break; - default: - break; - } - } -} - function controlInputValue(inputDom: HTMLInputElement, props: Props) { const { name, type } = props; @@ -87,3 +58,32 @@ function controlInputValue(inputDom: HTMLInputElement, props: Props) { updateInputValue(inputDom, props); } } + +// 受控组件值重新赋值 +function controlValue(target: Element) { + const props = getVNodeProps(target); + if (props) { + const type = getDomTag(target); + switch (type) { + case 'input': + controlInputValue(target, props); + break; + case 'textarea': + updateTextareaValue(target, props); + break; + default: + break; + } + } +} + +// 从缓存队列中对受控组件进行赋值 +export function tryControlValue() { + if (!changeEventTargets) { + return; + } + changeEventTargets.forEach(target => { + controlValue(target); + }); + changeEventTargets = null; +} diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index 5c0ae747..77f22988 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -45,7 +45,7 @@ function shouldTriggerChangeEvent(targetDom, evtName) { const { type } = targetDom; const domTag = getDomTag(targetDom); - if (domTag === 'select' || (domTag === 'input' && type === 'file')) { + if (domTag === 'select' || domTag === 'input' && type === 'file') { return evtName === 'change'; } else if (domTag === 'input' && (type === 'checkbox' || type === 'radio')) { if (evtName === 'click') { diff --git a/libs/horizon/src/horizonx/adapters/redux.ts b/libs/horizon/src/horizonx/adapters/redux.ts index 03ad253b..46d4b663 100644 --- a/libs/horizon/src/horizonx/adapters/redux.ts +++ b/libs/horizon/src/horizonx/adapters/redux.ts @@ -54,15 +54,12 @@ export type ReduxMiddleware = ( type Reducer = (state: any, action: ReduxAction) => any; function mergeData(state, data) { - console.log('merging data', { state, data }); if (!data) { - console.log('!data'); state.stateWrapper = data; return; } if (Array.isArray(data) && Array.isArray(state?.stateWrapper)) { - console.log('data is array'); state.stateWrapper.length = data.length; data.forEach((item, idx) => { if (item != state.stateWrapper[idx]) { @@ -73,9 +70,8 @@ function mergeData(state, data) { } if (typeof data === 'object' && typeof state?.stateWrapper === 'object') { - console.log('data is object'); Object.keys(state.stateWrapper).forEach(key => { - if (!data.hasOwnProperty(key)) delete state.stateWrapper[key]; + if (!Object.prototype.hasOwnProperty.call(data, key)) delete state.stateWrapper[key]; }); Object.entries(data).forEach(([key, value]) => { @@ -86,7 +82,6 @@ function mergeData(state, data) { return; } - console.log('data is primitive or type mismatch'); state.stateWrapper = data; } @@ -106,7 +101,6 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): if (result === undefined) { return; } // NOTE: reducer should never return undefined, in this case, do not change state - // mergeData(state,result); state.stateWrapper = result; }, }, @@ -115,10 +109,6 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): }, })(); - // store.$subscribe(()=>{ - // console.log('changed'); - // }); - const result = { reducer, getState: function () { @@ -157,12 +147,6 @@ export function combineReducers(reducers: { [key: string]: Reducer }): Reducer { }; } -export function applyMiddleware(...middlewares: ReduxMiddleware[]): (store: ReduxStoreHandler) => void { - return store => { - return applyMiddlewares(store, middlewares); - }; -} - function applyMiddlewares(store: ReduxStoreHandler, middlewares: ReduxMiddleware[]): void { middlewares = middlewares.slice(); middlewares.reverse(); @@ -173,6 +157,12 @@ function applyMiddlewares(store: ReduxStoreHandler, middlewares: ReduxMiddleware store.dispatch = dispatch; } +export function applyMiddleware(...middlewares: ReduxMiddleware[]): (store: ReduxStoreHandler) => void { + return store => { + return applyMiddlewares(store, middlewares); + }; +} + type ActionCreator = (...params: any[]) => ReduxAction; type ActionCreators = { [key: string]: ActionCreator }; export type BoundActionCreator = (...params: any[]) => void; diff --git a/libs/horizon/src/renderer/ErrorHandler.ts b/libs/horizon/src/renderer/ErrorHandler.ts index 7ba23cec..efcba91d 100644 --- a/libs/horizon/src/renderer/ErrorHandler.ts +++ b/libs/horizon/src/renderer/ErrorHandler.ts @@ -110,7 +110,7 @@ export function handleRenderThrowError(sourceVNode: VNode, error: any) { if ( (vNode.flags & DidCapture) === InitFlag && (typeof ctor.getDerivedStateFromError === 'function' || - (instance !== null && typeof instance.componentDidCatch === 'function')) + instance !== null && typeof instance.componentDidCatch === 'function') ) { FlagUtils.markShouldCapture(vNode); diff --git a/libs/horizon/src/renderer/hooks/HookMain.ts b/libs/horizon/src/renderer/hooks/HookMain.ts index 31a5be53..f22376f3 100644 --- a/libs/horizon/src/renderer/hooks/HookMain.ts +++ b/libs/horizon/src/renderer/hooks/HookMain.ts @@ -18,6 +18,12 @@ import type { VNode } from '../Types'; import { getLastTimeHook, setLastTimeHook, setCurrentHook, getNextHook } from './BaseHook'; import { HookStage, setHookStage } from './HookStage'; +function resetGlobalVariable() { + setHookStage(null); + setLastTimeHook(null); + setCurrentHook(null); +} + // hook对外入口 export function runFunctionWithHooks, Arg>( funcComp: (props: Props, arg: Arg) => any, @@ -57,9 +63,3 @@ export function runFunctionWithHooks, Arg>( return comp; } - -function resetGlobalVariable() { - setHookStage(null); - setLastTimeHook(null); - setCurrentHook(null); -} diff --git a/libs/horizon/src/renderer/hooks/UseEffectHook.ts b/libs/horizon/src/renderer/hooks/UseEffectHook.ts index cd8e4313..9d11c01d 100644 --- a/libs/horizon/src/renderer/hooks/UseEffectHook.ts +++ b/libs/horizon/src/renderer/hooks/UseEffectHook.ts @@ -21,27 +21,17 @@ import { getHookStage, HookStage } from './HookStage'; import { isArrayEqual } from '../utils/compare'; import { getProcessingVNode } from '../GlobalVar'; -export function useEffectImpl(effectFunc: () => (() => void) | void, deps?: Array | null): void { - // 异步触发的effect - useEffect(effectFunc, deps, EffectConstant.Effect); -} +function createEffect(effectFunc, removeFunc, deps, effectConstant): Effect { + const effect: Effect = { + effect: effectFunc, + removeEffect: removeFunc, + dependencies: deps, + effectConstant: effectConstant, + }; -export function useLayoutEffectImpl(effectFunc: () => (() => void) | void, deps?: Array | null): void { - // 同步触发的effect - useEffect(effectFunc, deps, EffectConstant.LayoutEffect); -} + getProcessingVNode().effectList.push(effect); -function useEffect(effectFunc: () => (() => void) | void, deps: Array | void | null, effectType: number): void { - const stage = getHookStage(); - if (stage === null) { - throwNotInFuncError(); - } - - if (stage === HookStage.Init) { - useEffectForInit(effectFunc, deps, effectType); - } else if (stage === HookStage.Update) { - useEffectForUpdate(effectFunc, deps, effectType); - } + return effect; } export function useEffectForInit(effectFunc, deps, effectType): void { @@ -76,15 +66,25 @@ export function useEffectForUpdate(effectFunc, deps, effectType): void { hook.state = createEffect(effectFunc, removeFunc, nextDeps, EffectConstant.DepsChange | effectType); } -function createEffect(effectFunc, removeFunc, deps, effectConstant): Effect { - const effect: Effect = { - effect: effectFunc, - removeEffect: removeFunc, - dependencies: deps, - effectConstant: effectConstant, - }; +function useEffect(effectFunc: () => (() => void) | void, deps: Array | void | null, effectType: number): void { + const stage = getHookStage(); + if (stage === null) { + throwNotInFuncError(); + } - getProcessingVNode().effectList.push(effect); - - return effect; + if (stage === HookStage.Init) { + useEffectForInit(effectFunc, deps, effectType); + } else if (stage === HookStage.Update) { + useEffectForUpdate(effectFunc, deps, effectType); + } +} + +export function useEffectImpl(effectFunc: () => (() => void) | void, deps?: Array | null): void { + // 异步触发的effect + useEffect(effectFunc, deps, EffectConstant.Effect); +} + +export function useLayoutEffectImpl(effectFunc: () => (() => void) | void, deps?: Array | null): void { + // 同步触发的effect + useEffect(effectFunc, deps, EffectConstant.LayoutEffect); } diff --git a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts index b9f6d581..3ae2feef 100644 --- a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts +++ b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts @@ -18,20 +18,6 @@ import { getHookStage } from './HookStage'; import { throwNotInFuncError } from './BaseHook'; import type { Ref } from './HookType'; -export function useImperativeHandleImpl( - ref: { current: R | null } | ((any) => any) | null | void, - func: () => R, - dependencies?: Array | null -): void { - const stage = getHookStage(); - if (stage === null) { - throwNotInFuncError(); - } - - const params = isNotNull(dependencies) ? dependencies.concat([ref]) : null; - useLayoutEffectImpl(effectFunc.bind(null, func, ref), params); -} - function isNotNull(object: any): boolean { return object !== null && object !== undefined; } @@ -52,3 +38,17 @@ function effectFunc(func: () => R, ref: Ref | ((any) => any) | null): (() }; } } + +export function useImperativeHandleImpl( + ref: { current: R | null } | ((any) => any) | null | void, + func: () => R, + dependencies?: Array | null +): void { + const stage = getHookStage(); + if (stage === null) { + throwNotInFuncError(); + } + + const params = isNotNull(dependencies) ? dependencies.concat([ref]) : null; + useLayoutEffectImpl(effectFunc.bind(null, func, ref), params); +} diff --git a/libs/horizon/src/renderer/hooks/UseReducerHook.ts b/libs/horizon/src/renderer/hooks/UseReducerHook.ts index 3637f775..86572fb7 100644 --- a/libs/horizon/src/renderer/hooks/UseReducerHook.ts +++ b/libs/horizon/src/renderer/hooks/UseReducerHook.ts @@ -22,29 +22,6 @@ import { getHookStage, HookStage } from './HookStage'; import type { VNode } from '../Types'; import { getProcessingVNode } from '../GlobalVar'; -export function useReducerImpl( - reducer: (S, A) => S, - initArg: P, - init?: (P) => S, - isUseState?: boolean -): [S, Trigger] | void { - const stage = getHookStage(); - if (stage === null) { - throwNotInFuncError(); - } - - if (stage === HookStage.Init) { - return useReducerForInit(reducer, initArg, init, isUseState); - } else if (stage === HookStage.Update) { - // 获取当前的hook - const currentHook = getCurrentHook(); - // 获取currentHook的更新数组 - const currentHookUpdates = (currentHook.state as Reducer).updates; - - return updateReducerHookState(currentHookUpdates, currentHook, reducer); - } -} - // 构造新的Update数组 function insertUpdate(action: A, hook: Hook): Update { const newUpdate: Update = { @@ -116,6 +93,25 @@ export function useReducerForInit(reducer, initArg, init, isUseState?: boo return [hook.state.stateValue, trigger]; } +// 计算stateValue值 +function calculateNewState(currentHookUpdates: Array>, currentHook, reducer: (S, A) => S) { + const reducerObj = currentHook.state; + let state = reducerObj.stateValue; + + // 循环遍历更新数组,计算新的状态值 + currentHookUpdates.forEach(update => { + // 1. didCalculated = true 说明state已经计算过; 2. 如果来自 isUseState + if (update.didCalculated && reducerObj.isUseState) { + state = update.state; + } else { + const action = update.action; + state = reducer(state, action); + } + }); + + return state; +} + // 更新hook.state function updateReducerHookState(currentHookUpdates, currentHook, reducer): [S, Trigger] { if (currentHookUpdates !== null) { @@ -135,21 +131,25 @@ function updateReducerHookState(currentHookUpdates, currentHook, reducer): return [currentHook.state.stateValue, currentHook.state.trigger]; } -// 计算stateValue值 -function calculateNewState(currentHookUpdates: Array>, currentHook, reducer: (S, A) => S) { - const reducerObj = currentHook.state; - let state = reducerObj.stateValue; +export function useReducerImpl( + reducer: (S, A) => S, + initArg: P, + init?: (P) => S, + isUseState?: boolean +): [S, Trigger] | void { + const stage = getHookStage(); + if (stage === null) { + throwNotInFuncError(); + } - // 循环遍历更新数组,计算新的状态值 - currentHookUpdates.forEach(update => { - // 1. didCalculated = true 说明state已经计算过; 2. 如果来自 isUseState - if (update.didCalculated && reducerObj.isUseState) { - state = update.state; - } else { - const action = update.action; - state = reducer(state, action); - } - }); + if (stage === HookStage.Init) { + return useReducerForInit(reducer, initArg, init, isUseState); + } else if (stage === HookStage.Update) { + // 获取当前的hook + const currentHook = getCurrentHook(); + // 获取currentHook的更新数组 + const currentHookUpdates = (currentHook.state as Reducer).updates; - return state; + return updateReducerHookState(currentHookUpdates, currentHook, reducer); + } } diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/horizon/src/renderer/render/BaseComponent.ts index 27449235..45b5be71 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/horizon/src/renderer/render/BaseComponent.ts @@ -73,7 +73,7 @@ export function captureVNode(processing: VNode): VNode | null { export function markRef(processing: VNode) { const ref = processing.ref; - if ((processing.isCreated && ref !== null) || (!processing.isCreated && processing.oldRef !== ref)) { + if (processing.isCreated && ref !== null || !processing.isCreated && processing.oldRef !== ref) { FlagUtils.markRef(processing); } } diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index 82c04dee..a73644c7 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -151,7 +151,7 @@ export function bubbleRender(processing: VNode) { const { childStatus, oldChildStatus } = processing.suspenseState; if ( childStatus === SuspenseChildStatus.ShowFallback || - (!processing.isCreated && oldChildStatus === SuspenseChildStatus.ShowFallback) + !processing.isCreated && oldChildStatus === SuspenseChildStatus.ShowFallback ) { FlagUtils.markUpdate(processing); } diff --git a/libs/horizon/src/renderer/submit/HookEffectHandler.ts b/libs/horizon/src/renderer/submit/HookEffectHandler.ts index c7db9c32..06bfd371 100644 --- a/libs/horizon/src/renderer/submit/HookEffectHandler.ts +++ b/libs/horizon/src/renderer/submit/HookEffectHandler.ts @@ -40,28 +40,6 @@ export function isSchedulingEffects() { return isScheduling; } -export function callUseEffects(vNode: VNode) { - const effectList: EffectList = vNode.effectList; - if (effectList !== null) { - effectList.forEach(effect => { - const { effectConstant } = effect; - if ( - (effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect && - (effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect - ) { - hookEffects.push(effect); - hookRemoveEffects.push(effect); - - // 异步调用 - if (!isScheduling) { - isScheduling = true; - runAsync(runAsyncEffects); - } - } - }); - } -} - export function runAsyncEffects() { const preMode = copyExecuteMode(); changeMode(InRender, true); @@ -98,6 +76,28 @@ export function runAsyncEffects() { setExecuteMode(preMode); } +export function callUseEffects(vNode: VNode) { + const effectList: EffectList = vNode.effectList; + if (effectList !== null) { + effectList.forEach(effect => { + const { effectConstant } = effect; + if ( + (effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect && + (effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect + ) { + hookEffects.push(effect); + hookRemoveEffects.push(effect); + + // 异步调用 + if (!isScheduling) { + isScheduling = true; + runAsync(runAsyncEffects); + } + } + }); + } +} + // 在销毁vNode的时候调用remove export function callEffectRemove(vNode: VNode) { const effectList: EffectList = vNode.effectList; diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index cd3461b2..ebf73031 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -154,18 +154,6 @@ function hideOrUnhideAllChildren(vNode, isHidden) { ); } -function attachRef(vNode: VNode) { - const ref = vNode.ref; - - handleRef(vNode, ref, vNode.realNode); -} - -function detachRef(vNode: VNode, isOldRef?: boolean) { - const ref = isOldRef ? vNode.oldRef : vNode.ref; - - handleRef(vNode, ref, null); -} - function handleRef(vNode: VNode, ref, val) { if (ref !== null && ref !== undefined) { const refType = typeof ref; @@ -182,6 +170,18 @@ function handleRef(vNode: VNode, ref, val) { } } +function attachRef(vNode: VNode) { + const ref = vNode.ref; + + handleRef(vNode, ref, vNode.realNode); +} + +function detachRef(vNode: VNode, isOldRef?: boolean) { + const ref = isOldRef ? vNode.oldRef : vNode.ref; + + handleRef(vNode, ref, null); +} + // 卸载一个vNode,不会递归 function unmountVNode(vNode: VNode): void { switch (vNode.tag) { @@ -217,6 +217,9 @@ function unmountVNode(vNode: VNode): void { unmountDomComponents(vNode); break; } + default: { + break; + } } } @@ -413,6 +416,9 @@ function submitUpdate(vNode: VNode): void { listenToPromise(vNode); break; } + default: { + break; + } } } diff --git a/libs/horizon/src/renderer/submit/Submit.ts b/libs/horizon/src/renderer/submit/Submit.ts index 4ea6d47c..f0e8982a 100644 --- a/libs/horizon/src/renderer/submit/Submit.ts +++ b/libs/horizon/src/renderer/submit/Submit.ts @@ -41,63 +41,6 @@ const LOOPING_UPDATE_LIMIT = 50; let loopingUpdateCount = 0; let lastRoot: VNode | null = null; -export function submitToRender(treeRoot) { - treeRoot.shouldUpdate = treeRoot.childShouldUpdate; - // 置空task,让才能加入新的render任务 - treeRoot.task = null; - - const startVNode = getStartVNode(); - - if (FlagUtils.hasAnyFlag(startVNode)) { - // 把自己加上 - if (startVNode.dirtyNodes === null) { - startVNode.dirtyNodes = [startVNode]; - } else { - startVNode.dirtyNodes.push(startVNode); - } - } - - const dirtyNodes = startVNode.dirtyNodes; - if (dirtyNodes !== null && dirtyNodes.length) { - const preMode = copyExecuteMode(); - changeMode(InRender, true); - - prepareForSubmit(); - // before submit阶段 - beforeSubmit(dirtyNodes); - - // submit阶段 - submit(dirtyNodes); - - resetAfterSubmit(); - - // after submit阶段 - afterSubmit(dirtyNodes); - - setExecuteMode(preMode); - dirtyNodes.length = 0; - startVNode.dirtyNodes = null; - } - - if (isSchedulingEffects()) { - setSchedulingEffects(false); - } - - // 统计root同步重渲染的次数,如果太多可能是无线循环 - countLoopingUpdate(treeRoot); - - // 在退出`submit` 之前始终调用此函数,以确保任何已计划在此根上执行的update被执行。 - tryRenderFromRoot(treeRoot); - - if (rootThrowError) { - const error = rootThrowError; - rootThrowError = null; - throw error; - } - - return null; -} - function beforeSubmit(dirtyNodes: Array) { let node; const nodesLength = dirtyNodes.length; @@ -214,3 +157,60 @@ export function checkLoopingUpdateLimit() { ); } } + +export function submitToRender(treeRoot) { + treeRoot.shouldUpdate = treeRoot.childShouldUpdate; + // 置空task,让才能加入新的render任务 + treeRoot.task = null; + + const startVNode = getStartVNode(); + + if (FlagUtils.hasAnyFlag(startVNode)) { + // 把自己加上 + if (startVNode.dirtyNodes === null) { + startVNode.dirtyNodes = [startVNode]; + } else { + startVNode.dirtyNodes.push(startVNode); + } + } + + const dirtyNodes = startVNode.dirtyNodes; + if (dirtyNodes !== null && dirtyNodes.length) { + const preMode = copyExecuteMode(); + changeMode(InRender, true); + + prepareForSubmit(); + // before submit阶段 + beforeSubmit(dirtyNodes); + + // submit阶段 + submit(dirtyNodes); + + resetAfterSubmit(); + + // after submit阶段 + afterSubmit(dirtyNodes); + + setExecuteMode(preMode); + dirtyNodes.length = 0; + startVNode.dirtyNodes = null; + } + + if (isSchedulingEffects()) { + setSchedulingEffects(false); + } + + // 统计root同步重渲染的次数,如果太多可能是无线循环 + countLoopingUpdate(treeRoot); + + // 在退出`submit` 之前始终调用此函数,以确保任何已计划在此根上执行的update被执行。 + tryRenderFromRoot(treeRoot); + + if (rootThrowError) { + const error = rootThrowError; + rootThrowError = null; + throw error; + } + + return null; +} diff --git a/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts b/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts index 6da28613..cccef59d 100644 --- a/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts +++ b/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts @@ -27,6 +27,14 @@ export function isOverTime() { return false; } +function asyncCall() { + if (isTestRuntime) { + setTimeout(callRenderTasks, 0); + } else { + port2.postMessage(null); + } +} + // 1、设置deadline;2、回调TaskExecutor传过来的browserCallback const callRenderTasks = () => { if (browserCallback === null) { @@ -61,14 +69,6 @@ if (typeof MessageChannel === 'function') { isTestRuntime = true; } -function asyncCall() { - if (isTestRuntime) { - setTimeout(callRenderTasks, 0); - } else { - port2.postMessage(null); - } -} - export function requestBrowserCallback(callback) { browserCallback = callback; diff --git a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts index 0fe3e533..729dfbc8 100644 --- a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts +++ b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts @@ -27,16 +27,6 @@ let callingQueueTask: any | null = null; // 防止重入 let isCallingRenderQueue = false; -export function callRenderQueueImmediate() { - if (callingQueueTask !== null) { - // 取消异步调度 - cancelTask(callingQueueTask); - callingQueueTask = null; - } - - callRenderQueue(); -} - // 执行render回调 function callRenderQueue() { if (!isCallingRenderQueue && renderQueue !== null) { @@ -58,6 +48,16 @@ function callRenderQueue() { } } +export function callRenderQueueImmediate() { + if (callingQueueTask !== null) { + // 取消异步调度 + cancelTask(callingQueueTask); + callingQueueTask = null; + } + + callRenderQueue(); +} + export function pushRenderCallback(callback: RenderCallback) { if (renderQueue === null) { renderQueue = [callback]; diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 4214e928..40ba0554 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -200,6 +200,8 @@ export class VNode { break; case Profiler: break; + default: + break; } } } diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 6b96e49e..73944854 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -194,6 +194,8 @@ export function createVNode(tag: VNodeTag | string, ...secondArg) { vNode.updates = []; break; + default: + break; } return vNode; diff --git a/libs/horizon/src/renderer/vnode/VNodeUtils.ts b/libs/horizon/src/renderer/vnode/VNodeUtils.ts index 7e55f48d..d9bb0f50 100644 --- a/libs/horizon/src/renderer/vnode/VNodeUtils.ts +++ b/libs/horizon/src/renderer/vnode/VNodeUtils.ts @@ -169,13 +169,6 @@ export function findDOMByClassInst(inst) { return domVNode !== null ? domVNode.realNode : null; } -// 判断dom树是否已经挂载 -export function isMounted(vNode: VNode) { - const rootNode = getTreeRootVNode(vNode); - // 如果根节点是 Dom 类型节点,表示已经挂载 - return rootNode.tag === TreeRoot; -} - function getTreeRootVNode(vNode) { let node = vNode; while (node.parent) { @@ -184,6 +177,13 @@ function getTreeRootVNode(vNode) { return node; } +// 判断dom树是否已经挂载 +export function isMounted(vNode: VNode) { + const rootNode = getTreeRootVNode(vNode); + // 如果根节点是 Dom 类型节点,表示已经挂载 + return rootNode.tag === TreeRoot; +} + // 找到相邻的DOM export function getSiblingDom(vNode: VNode): Element | null { let node: VNode = vNode; From 8752ba53c5b4ddf26dddc2c9dadc625201b292dc Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 18 Apr 2023 10:08:43 +0800 Subject: [PATCH 49/74] Match-id-5ec5b8bb1e9670f83af42904d6795648595acd4a --- libs/horizon/src/event/HorizonEventMain.ts | 2 +- libs/horizon/src/renderer/ErrorHandler.ts | 2 +- libs/horizon/src/renderer/render/BaseComponent.ts | 2 +- libs/horizon/src/renderer/render/SuspenseComponent.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index 77f22988..5c0ae747 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -45,7 +45,7 @@ function shouldTriggerChangeEvent(targetDom, evtName) { const { type } = targetDom; const domTag = getDomTag(targetDom); - if (domTag === 'select' || domTag === 'input' && type === 'file') { + if (domTag === 'select' || (domTag === 'input' && type === 'file')) { return evtName === 'change'; } else if (domTag === 'input' && (type === 'checkbox' || type === 'radio')) { if (evtName === 'click') { diff --git a/libs/horizon/src/renderer/ErrorHandler.ts b/libs/horizon/src/renderer/ErrorHandler.ts index efcba91d..7ba23cec 100644 --- a/libs/horizon/src/renderer/ErrorHandler.ts +++ b/libs/horizon/src/renderer/ErrorHandler.ts @@ -110,7 +110,7 @@ export function handleRenderThrowError(sourceVNode: VNode, error: any) { if ( (vNode.flags & DidCapture) === InitFlag && (typeof ctor.getDerivedStateFromError === 'function' || - instance !== null && typeof instance.componentDidCatch === 'function') + (instance !== null && typeof instance.componentDidCatch === 'function')) ) { FlagUtils.markShouldCapture(vNode); diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/horizon/src/renderer/render/BaseComponent.ts index 45b5be71..27449235 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/horizon/src/renderer/render/BaseComponent.ts @@ -73,7 +73,7 @@ export function captureVNode(processing: VNode): VNode | null { export function markRef(processing: VNode) { const ref = processing.ref; - if (processing.isCreated && ref !== null || !processing.isCreated && processing.oldRef !== ref) { + if ((processing.isCreated && ref !== null) || (!processing.isCreated && processing.oldRef !== ref)) { FlagUtils.markRef(processing); } } diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index a73644c7..82c04dee 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -151,7 +151,7 @@ export function bubbleRender(processing: VNode) { const { childStatus, oldChildStatus } = processing.suspenseState; if ( childStatus === SuspenseChildStatus.ShowFallback || - !processing.isCreated && oldChildStatus === SuspenseChildStatus.ShowFallback + (!processing.isCreated && oldChildStatus === SuspenseChildStatus.ShowFallback) ) { FlagUtils.markUpdate(processing); } From f651733b456a1ce71bf9e0815c11cae42c93246b Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 18 Apr 2023 10:30:01 +0800 Subject: [PATCH 50/74] Match-id-8498afa1809522a435c0673515cbe56bd60a7df3 --- libs/horizon/src/horizonx/adapters/redux.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/horizon/src/horizonx/adapters/redux.ts b/libs/horizon/src/horizonx/adapters/redux.ts index 46d4b663..97b7a483 100644 --- a/libs/horizon/src/horizonx/adapters/redux.ts +++ b/libs/horizon/src/horizonx/adapters/redux.ts @@ -71,7 +71,9 @@ function mergeData(state, data) { if (typeof data === 'object' && typeof state?.stateWrapper === 'object') { Object.keys(state.stateWrapper).forEach(key => { - if (!Object.prototype.hasOwnProperty.call(data, key)) delete state.stateWrapper[key]; + if (!Object.prototype.hasOwnProperty.call(data, key)) { + delete state.stateWrapper[key]; + } }); Object.entries(data).forEach(([key, value]) => { From 28f0a2b9b6560a54e09ef764bffbc89eb981c0b0 Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 18 Apr 2023 11:45:36 +0800 Subject: [PATCH 51/74] Match-id-6d0d90f21208e053a7c407bacfa5a3862d7806fd --- .../dom/DOMPropertiesHandler/StyleHandler.ts | 40 ++-- libs/horizon/src/event/EventBinding.ts | 16 +- libs/horizon/src/external/TestUtil.ts | 28 +-- libs/horizon/src/horizonx/devtools/index.ts | 31 +-- .../src/horizonx/proxy/ProxyHandler.ts | 8 +- .../proxy/handlers/ArrayProxyHandler.ts | 68 +++--- .../src/horizonx/proxy/handlers/MapProxy.ts | 136 ++++++------ .../proxy/handlers/ObjectProxyHandler.ts | 42 ++-- .../src/horizonx/proxy/handlers/SetProxy.ts | 91 ++++---- .../src/horizonx/store/StoreHandler.ts | 188 ++++++++-------- libs/horizon/src/renderer/TreeBuilder.ts | 68 +++--- .../src/renderer/diff/nodeDiffComparator.ts | 26 +-- .../src/renderer/submit/LifeCycleHandler.ts | 202 +++++++++--------- 13 files changed, 474 insertions(+), 470 deletions(-) diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts index c3954fa1..3c3a9a24 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts @@ -13,6 +13,26 @@ * See the Mulan PSL v2 for more details. */ +/** + * 不需要加长度单位的 css 属性 + */ +const noUnitCSS = [ + 'animationIterationCount', + 'columnCount', + 'columns', + 'gridArea', + 'fontWeight', + 'lineClamp', + 'lineHeight', + 'opacity', + 'order', + 'orphans', + 'tabSize', + 'widows', + 'zIndex', + 'zoom', +]; + function isNeedUnitCSS(styleName: string) { return !( noUnitCSS.includes(styleName) || @@ -62,23 +82,3 @@ export function setStyles(dom, styles) { } }); } - -/** - * 不需要加长度单位的 css 属性 - */ -const noUnitCSS = [ - 'animationIterationCount', - 'columnCount', - 'columns', - 'gridArea', - 'fontWeight', - 'lineClamp', - 'lineHeight', - 'opacity', - 'order', - 'orphans', - 'tabSize', - 'widows', - 'zIndex', - 'zoom', -]; diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index e9d88308..25c52b90 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -56,6 +56,14 @@ function listenToNativeEvent(nativeEvtName: string, delegatedElement: Element, i return listener; } +// 是否捕获事件 +function isCaptureEvent(horizonEventName) { + if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') { + return false; + } + return horizonEventName.slice(-7) === 'Capture'; +} + // 事件懒委托,当用户定义事件后,再进行委托到根节点 export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { currentRoot.delegatedEvents.add(eventName); @@ -94,14 +102,6 @@ function getNativeEvtName(horizonEventName, capture) { return nativeName.toLowerCase(); } -// 是否捕获事件 -function isCaptureEvent(horizonEventName) { - if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') { - return false; - } - return horizonEventName.slice(-7) === 'Capture'; -} - // 封装监听函数 function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) { return event => { diff --git a/libs/horizon/src/external/TestUtil.ts b/libs/horizon/src/external/TestUtil.ts index dc9e3dbd..3b5b24ca 100644 --- a/libs/horizon/src/external/TestUtil.ts +++ b/libs/horizon/src/external/TestUtil.ts @@ -23,6 +23,20 @@ interface Thenable { then(resolve: (val?: any) => void, reject: (err: any) => void): void; } +// 防止死循环 +const LOOPING_LIMIT = 50; +let loopingCount = 0; +function callRenderQueue() { + callRenderQueueImmediate(); + + while (hasAsyncEffects() && loopingCount < LOOPING_LIMIT) { + loopingCount++; + runAsyncEffects(); + // effects可能产生刷新任务,这里再执行一次 + callRenderQueueImmediate(); + } +} + // act用于测试,作用是:如果fun触发了刷新(包含了异步刷新),可以保证在act后面的代码是在刷新完成后才执行。 function act(fun: () => void | Thenable): Thenable { const funRet = asyncUpdates(fun); @@ -62,20 +76,6 @@ function act(fun: () => void | Thenable): Thenable { } } -// 防止死循环 -const LOOPING_LIMIT = 50; -let loopingCount = 0; -function callRenderQueue() { - callRenderQueueImmediate(); - - while (hasAsyncEffects() && loopingCount < LOOPING_LIMIT) { - loopingCount++; - runAsyncEffects(); - // effects可能产生刷新任务,这里再执行一次 - callRenderQueueImmediate(); - } -} - export { act }; diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 23bb6000..64a0f189 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -10,21 +10,6 @@ export function isPanelActive() { return window['__HORIZON_DEV_HOOK__']; } -// serializes store and creates expanded object with baked-in containing current computed values -function makeStoreSnapshot({ type, data }) { - const expanded = {}; - Object.keys(data.store.$c).forEach(key => { - expanded[key] = data.store[key]; - }); - data.store.expanded = expanded; - const snapshot = makeProxySnapshot({ - data, - type, - sessionId, - }); - return snapshot; -} - // safely serializes variables containing values wrapped in Proxy object function getType(value) { if (!value) return 'nullish'; @@ -39,6 +24,7 @@ function getType(value) { if (typeof value === 'object') return 'object'; return 'primitive'; } + function makeProxySnapshot(obj, visited: any[] = []) { const type = getType(obj); let clone; @@ -112,6 +98,21 @@ function makeProxySnapshot(obj, visited: any[] = []) { } } +// serializes store and creates expanded object with baked-in containing current computed values +function makeStoreSnapshot({ type, data }) { + const expanded = {}; + Object.keys(data.store.$c).forEach(key => { + expanded[key] = data.store[key]; + }); + data.store.expanded = expanded; + const snapshot = makeProxySnapshot({ + data, + type, + sessionId, + }); + return snapshot; +} + export const devtools = { // returns vNode id from horizon devtools getVNodeId: vNode => { diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index d165675e..be83bee5 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -27,6 +27,10 @@ const proxyMap = new WeakMap(); export const hookObserverMap = new WeakMap(); +export function getObserver(rawObj: any): Observer { + return rawObj[OBSERVER_KEY]; +} + export function createProxy(rawObj: any, isHookObserver = true, listener: { current: (...args) => any }): any { // 不是对象(是原始数据类型)不用代理 if (!(rawObj && isObject(rawObj))) { @@ -89,7 +93,3 @@ export function createProxy(rawObj: any, isHookObserver = true, listener: { curr return proxyObj; } - -export function getObserver(rawObj: any): Observer { - return rawObj[OBSERVER_KEY]; -} diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index 3938ecab..669f560a 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -19,6 +19,40 @@ import { resolveMutation } from '../../CommonUtils'; import { isPanelActive } from '../../devtools'; import { OBSERVER_KEY } from '../../Constants'; +function set(rawObj: any[], key: string, value: any, receiver: any) { + const oldValue = rawObj[key]; + const oldLength = rawObj.length; + const newValue = value; + + const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + + const ret = Reflect.set(rawObj, key, newValue, receiver); + + const newLength = rawObj.length; + const observer = getObserver(rawObj); + + const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : resolveMutation(null, rawObj); + + if (!isSame(newValue, oldValue)) { + // 值不一样,触发监听器 + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue, mutation); + }); + } + + // 触发属性变化 + observer.setProp(key, mutation); + } + + if (oldLength !== newLength) { + // 触发数组的大小变化 + observer.setProp('length', mutation); + } + + return ret; +} + export function createArrayProxy(rawObj: any[], listener: { current: (...args) => any }): any[] { let listeners = [] as ((...args) => void)[]; @@ -118,37 +152,3 @@ export function createArrayProxy(rawObj: any[], listener: { current: (...args) = return new Proxy(rawObj, handle); } - -function set(rawObj: any[], key: string, value: any, receiver: any) { - const oldValue = rawObj[key]; - const oldLength = rawObj.length; - const newValue = value; - - const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - - const ret = Reflect.set(rawObj, key, newValue, receiver); - - const newLength = rawObj.length; - const observer = getObserver(rawObj); - - const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : resolveMutation(null, rawObj); - - if (!isSame(newValue, oldValue)) { - // 值不一样,触发监听器 - if (observer.watchers?.[key]) { - observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, mutation); - }); - } - - // 触发属性变化 - observer.setProp(key, mutation); - } - - if (oldLength !== newLength) { - // 触发数组的大小变化 - observer.setProp('length', mutation); - } - - return ret; -} diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts index d5ed5d37..dd51f07f 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts @@ -25,48 +25,6 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: { let oldData: [any, any][] = []; let proxies = new Map(); - function get(rawObj: { size: number }, key: any, receiver: any): any { - if (key === 'size') { - return size(rawObj); - } - - if (key === 'get') { - return getFun.bind(null, rawObj); - } - - if (Object.prototype.hasOwnProperty.call(handler, key)) { - const value = Reflect.get(handler, key, receiver); - return value.bind(null, rawObj); - } - - if (key === 'watch') { - const observer = getObserver(rawObj); - - return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; - } - - if (key === 'addListener') { - return listener => { - listeners.push(listener); - }; - } - - if (key === 'removeListener') { - return listener => { - listeners = listeners.filter(item => item != listener); - }; - } - - return Reflect.get(rawObj, key, receiver); - } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function getFun(rawObj: { get: (key: any) => any; has: (key: any) => boolean }, key: any) { const keyProxy = rawObj.has(key) ? key : proxies.get(key); @@ -211,31 +169,6 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: { return false; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function size(rawObj: { size: number }) { - const observer = getObserver(rawObj); - observer.useProp(COLLECTION_CHANGE); - return rawObj.size; - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.keys(), 'keys'); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.values(), 'values'); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.entries(), 'entries'); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function forOf(rawObj: { - entries: () => { next: () => { value: any; done: boolean } }; - values: () => { next: () => { value: any; done: boolean } }; - }) { - return wrapIterator(rawObj, rawObj.entries(), 'entries'); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function forEach( rawObj: { forEach: (callback: (value: any, key: any) => void) => void }, callback: (valProxy: any, keyProxy: any, rawObj: any) => void @@ -354,6 +287,75 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: { }, }; } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function size(rawObj: { size: number }) { + const observer = getObserver(rawObj); + observer.useProp(COLLECTION_CHANGE); + return rawObj.size; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.keys(), 'keys'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.values(), 'values'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.entries(), 'entries'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function forOf(rawObj: { + entries: () => { next: () => { value: any; done: boolean } }; + values: () => { next: () => { value: any; done: boolean } }; + }) { + return wrapIterator(rawObj, rawObj.entries(), 'entries'); + } + + function get(rawObj: { size: number }, key: any, receiver: any): any { + if (key === 'size') { + return size(rawObj); + } + + if (key === 'get') { + return getFun.bind(null, rawObj); + } + + if (Object.prototype.hasOwnProperty.call(handler, key)) { + const value = Reflect.get(handler, key, receiver); + return value.bind(null, rawObj); + } + + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + + return Reflect.get(rawObj, key, receiver); + } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const handler = { get, diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index f1ac97b7..0afa6583 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -18,6 +18,27 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { OBSERVER_KEY } from '../../Constants'; import { isPanelActive } from '../../devtools'; +function set(rawObj: object, key: string, value: any, receiver: any): boolean { + const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + const observer = getObserver(rawObj); + + const oldValue = rawObj[key]; + const newValue = value; + + const ret = Reflect.set(rawObj, key, newValue, receiver); + const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj); + + if (!isSame(newValue, oldValue)) { + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue, mutation); + }); + } + observer.setProp(key, mutation); + } + return ret; +} + export function createObjectProxy( rawObj: T, singleLevel = false, @@ -99,24 +120,3 @@ export function createObjectProxy( return proxy; } - -function set(rawObj: object, key: string, value: any, receiver: any): boolean { - const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - const observer = getObserver(rawObj); - - const oldValue = rawObj[key]; - const newValue = value; - - const ret = Reflect.set(rawObj, key, newValue, receiver); - const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj); - - if (!isSame(newValue, oldValue)) { - if (observer.watchers?.[key]) { - observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, mutation); - }); - } - observer.setProp(key, mutation); - } - return ret; -} diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts index 6a4401fa..b0409ad6 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts @@ -26,43 +26,6 @@ export function createSetProxy( let listeners: ((mutation) => {})[] = []; let proxies = new WeakMap(); - function get(rawObj: { size: number }, key: any, receiver: any): any { - if (Object.prototype.hasOwnProperty.call(handler, key)) { - const value = Reflect.get(handler, key, receiver); - return value.bind(null, rawObj); - } - - if (key === 'size') { - return size(rawObj); - } - - if (key === 'addListener') { - return listener => { - listeners.push(listener); - }; - } - - if (key === 'removeListener') { - return listener => { - listeners = listeners.filter(item => item != listener); - }; - } - if (key === 'watch') { - const observer = getObserver(rawObj); - - return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; - } - return Reflect.get(rawObj, key, receiver); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Set的add方法 function add(rawObj: { add: (any) => void; has: (any) => boolean; values: () => any[] }, value: any): Object { if (!rawObj.has(proxies.get(value))) { @@ -166,17 +129,43 @@ export function createSetProxy( return rawObj.size; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.keys()); - } + function get(rawObj: { size: number }, key: any, receiver: any): any { + if (Object.prototype.hasOwnProperty.call(handler, key)) { + const value = Reflect.get(handler, key, receiver); + return value.bind(null, rawObj); + } - function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.values()); - } + if (key === 'size') { + return size(rawObj); + } - function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.entries()); + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + return Reflect.get(rawObj, key, receiver); } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }) { const observer = getObserver(rawObj); @@ -224,6 +213,18 @@ export function createSetProxy( }; } + function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.keys()); + } + + function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.values()); + } + + function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.entries()); + } + function forOf(rawObj: { entries: () => { next: () => { value: any; done: boolean } }; values: () => { next: () => { value: any; done: boolean } }; diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index 781deef4..2afd33f2 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -53,6 +53,100 @@ const idGenerator = { const storeMap = new Map>(); +// 通过该方法执行store.$queue中的action +function tryNextAction(storeObj, proxyObj, config, plannedActions) { + if (!plannedActions.length) { + if (proxyObj.$pending) { + const timestamp = Date.now(); + const duration = timestamp - proxyObj.$pending; + proxyObj.$pending = false; + devtools.emit(QUEUE_FINISHED, { + store: storeObj, + endedAt: timestamp, + duration, + }); + } + return; + } + + const nextAction = plannedActions.shift()!; + const result = config.actions + ? config.actions[nextAction.action].bind(storeObj, proxyObj)(...nextAction.payload) + : undefined; + + if (isPromise(result)) { + result.then(value => { + nextAction.resolve(value); + tryNextAction(storeObj, proxyObj, config, plannedActions); + }); + } else { + nextAction.resolve(result); + tryNextAction(storeObj, proxyObj, config, plannedActions); + } +} + +// 删除Observers中保存的这个VNode的相关数据 +export function clearVNodeObservers(vNode: VNode) { + if (!vNode.observers) { + return; + } + + vNode.observers.forEach(observer => { + observer.clearByVNode(vNode); + }); + + vNode.observers.clear(); +} + +// 注册VNode销毁时的清理动作 +function registerDestroyFunction() { + const processingVNode = getProcessingVNode(); + + // 获取不到当前运行的VNode,说明不在组件中运行,属于非法场景 + if (!processingVNode) { + return; + } + + if (!processingVNode.observers) { + processingVNode.observers = new Set(); + } + + // 函数组件 + if (processingVNode.tag === FunctionComponent) { + const vNodeRef = useRef(processingVNode); + + useEffect(() => { + return () => { + clearVNodeObservers(vNodeRef.current); + vNodeRef.current.observers = null; + }; + }, []); + } else if (processingVNode.tag === ClassComponent) { + // 类组件 + if (!processingVNode.classComponentWillUnmount) { + processingVNode.classComponentWillUnmount = vNode => { + clearVNodeObservers(vNode); + vNode.observers = null; + }; + } + } +} + +// createStore返回的是一个getStore的函数,这个函数必须要在组件(函数/类组件)里面被执行,因为要注册VNode销毁时的清理动作 +function createGetStore, C extends UserComputedValues>( + storeObj: StoreObj +): () => StoreObj { + const getStore = () => { + if (!storeObj.$config.options?.isReduxAdapter) { + registerDestroyFunction(); + } + + return storeObj; + }; + + return getStore; +} + export function createStore, C extends UserComputedValues>( config: StoreConfig ): () => StoreObj { @@ -216,100 +310,6 @@ export function createStore, C extend return createGetStore(storeObj); } -// 通过该方法执行store.$queue中的action -function tryNextAction(storeObj, proxyObj, config, plannedActions) { - if (!plannedActions.length) { - if (proxyObj.$pending) { - const timestamp = Date.now(); - const duration = timestamp - proxyObj.$pending; - proxyObj.$pending = false; - devtools.emit(QUEUE_FINISHED, { - store: storeObj, - endedAt: timestamp, - duration, - }); - } - return; - } - - const nextAction = plannedActions.shift()!; - const result = config.actions - ? config.actions[nextAction.action].bind(storeObj, proxyObj)(...nextAction.payload) - : undefined; - - if (isPromise(result)) { - result.then(value => { - nextAction.resolve(value); - tryNextAction(storeObj, proxyObj, config, plannedActions); - }); - } else { - nextAction.resolve(result); - tryNextAction(storeObj, proxyObj, config, plannedActions); - } -} - -// createStore返回的是一个getStore的函数,这个函数必须要在组件(函数/类组件)里面被执行,因为要注册VNode销毁时的清理动作 -function createGetStore, C extends UserComputedValues>( - storeObj: StoreObj -): () => StoreObj { - const getStore = () => { - if (!storeObj.$config.options?.isReduxAdapter) { - registerDestroyFunction(); - } - - return storeObj; - }; - - return getStore; -} - -// 删除Observers中保存的这个VNode的相关数据 -export function clearVNodeObservers(vNode: VNode) { - if (!vNode.observers) { - return; - } - - vNode.observers.forEach(observer => { - observer.clearByVNode(vNode); - }); - - vNode.observers.clear(); -} - -// 注册VNode销毁时的清理动作 -function registerDestroyFunction() { - const processingVNode = getProcessingVNode(); - - // 获取不到当前运行的VNode,说明不在组件中运行,属于非法场景 - if (!processingVNode) { - return; - } - - if (!processingVNode.observers) { - processingVNode.observers = new Set(); - } - - // 函数组件 - if (processingVNode.tag === FunctionComponent) { - const vNodeRef = useRef(processingVNode); - - useEffect(() => { - return () => { - clearVNodeObservers(vNodeRef.current); - vNodeRef.current.observers = null; - }; - }, []); - } else if (processingVNode.tag === ClassComponent) { - // 类组件 - if (!processingVNode.classComponentWillUnmount) { - processingVNode.classComponentWillUnmount = vNode => { - clearVNodeObservers(vNode); - vNode.observers = null; - }; - } - } -} - // 函数组件中使用的hook export function useStore, C extends UserComputedValues>( id: string diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 7d37de2f..eb20b0ef 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -228,6 +228,40 @@ export function calcStartUpdateVNode(treeRoot: VNode) { return node; } +// 在局部更新时,从上到下恢复父节点的context和PortalStack +function recoverTreeContext(vNode: VNode) { + const contextProviders: VNode[] = []; + let parent = vNode.parent; + while (parent !== null) { + if (parent.tag === ContextProvider) { + contextProviders.unshift(parent); + } + if (parent.tag === DomPortal) { + pushCurrentRoot(parent); + } + parent = parent.parent; + } + + contextProviders.forEach(node => { + setContext(node, node.props.value); + }); +} + +// 在局部更新时,从下到上重置父节点的context +function resetTreeContext(vNode: VNode) { + let parent = vNode.parent; + + while (parent !== null) { + if (parent.tag === ContextProvider) { + resetContext(parent); + } + if (parent.tag === DomPortal) { + popCurrentRoot(); + } + parent = parent.parent; + } +} + // ============================== 深度遍历 ============================== function buildVNodeTree(treeRoot: VNode) { const preMode = copyExecuteMode(); @@ -299,40 +333,6 @@ function buildVNodeTree(treeRoot: VNode) { setExecuteMode(preMode); } -// 在局部更新时,从上到下恢复父节点的context和PortalStack -function recoverTreeContext(vNode: VNode) { - const contextProviders: VNode[] = []; - let parent = vNode.parent; - while (parent !== null) { - if (parent.tag === ContextProvider) { - contextProviders.unshift(parent); - } - if (parent.tag === DomPortal) { - pushCurrentRoot(parent); - } - parent = parent.parent; - } - - contextProviders.forEach(node => { - setContext(node, node.props.value); - }); -} - -// 在局部更新时,从下到上重置父节点的context -function resetTreeContext(vNode: VNode) { - let parent = vNode.parent; - - while (parent !== null) { - if (parent.tag === ContextProvider) { - resetContext(parent); - } - if (parent.tag === DomPortal) { - popCurrentRoot(); - } - parent = parent.parent; - } -} - // 总体任务入口 function renderFromRoot(treeRoot) { runAsyncEffects(); diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index 56753063..61706bea 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -231,6 +231,19 @@ function getOldNodeFromMap(nodeMap: Map, newIdx: number, return null; } +// 设置vNode中的cIndex属性,cIndex是节点在children中的位置 +function setVNodesCIndex(startChild: VNode | null, startIdx: number) { + let node: VNode | null = startChild; + let idx = startIdx; + + while (node !== null) { + node.cIndex = idx; + markVNodePath(node); + node = node.next; + idx++; + } +} + // diff数组类型的节点,核心算法 function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newChildren: Array): VNode | null { let resultingFirstChild: VNode | null = null; @@ -478,19 +491,6 @@ function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newC return resultingFirstChild; } -// 设置vNode中的cIndex属性,cIndex是节点在children中的位置 -function setVNodesCIndex(startChild: VNode | null, startIdx: number) { - let node: VNode | null = startChild; - let idx = startIdx; - - while (node !== null) { - node.cIndex = idx; - markVNodePath(node); - node = node.next; - idx++; - } -} - // 新节点是迭代器类型 function diffIteratorNodesHandler( parentNode: VNode, diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index ebf73031..056f18f9 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -182,47 +182,6 @@ function detachRef(vNode: VNode, isOldRef?: boolean) { handleRef(vNode, ref, null); } -// 卸载一个vNode,不会递归 -function unmountVNode(vNode: VNode): void { - switch (vNode.tag) { - case FunctionComponent: - case ForwardRef: - case MemoComponent: { - callEffectRemove(vNode); - break; - } - case ClassComponent: { - detachRef(vNode); - - const instance = vNode.realNode; - // 当constructor中抛出异常时,instance会是null,这里判断一下instance是否为空 - // suspense打断时不需要触发WillUnmount - if (instance && typeof instance.componentWillUnmount === 'function' && !vNode.isSuspended) { - callComponentWillUnmount(vNode, instance); - } - - // HorizonX会在classComponentWillUnmount中清除对VNode的引入用 - if (vNode.classComponentWillUnmount) { - vNode.classComponentWillUnmount(vNode); - vNode.classComponentWillUnmount = null; - } - break; - } - case DomComponent: { - detachRef(vNode); - break; - } - case DomPortal: { - // 这里会递归 - unmountDomComponents(vNode); - break; - } - default: { - break; - } - } -} - // 卸载vNode,递归遍历子vNode function unmountNestedVNodes(vNode: VNode): void { travelVNodeTree( @@ -238,59 +197,6 @@ function unmountNestedVNodes(vNode: VNode): void { ); } -function submitAddition(vNode: VNode): void { - let parent = vNode.parent; - let parentDom; - let tag; - while (parent !== null) { - tag = parent.tag; - if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) { - parentDom = parent.realNode; - break; - } - parent = parent.parent; - } - - if ((parent.flags & ResetText) === ResetText) { - // 在insert之前先reset - clearText(parentDom); - FlagUtils.removeFlag(parent, ResetText); - } - - if ((vNode.flags & DirectAddition) === DirectAddition) { - insertOrAppendPlacementNode(vNode, null, parentDom); - FlagUtils.removeFlag(vNode, DirectAddition); - return; - } - const before = getSiblingDom(vNode); - insertOrAppendPlacementNode(vNode, before, parentDom); -} - -function insertOrAppendPlacementNode(node: VNode, beforeDom: Element | null, parent: Element | Container): void { - const { tag, realNode } = node; - - if (isDomVNode(node)) { - insertDom(parent, realNode, beforeDom); - } else if (tag === DomPortal) { - // 这里不做处理,直接在portal中处理 - } else { - // 插入子节点们 - let child = node.child; - while (child !== null) { - insertOrAppendPlacementNode(child, beforeDom, parent); - child = child.next; - } - } -} - -function insertDom(parent, realNode, beforeDom) { - if (beforeDom) { - insertDomBefore(parent, realNode, beforeDom); - } else { - appendChildElement(parent, realNode); - } -} - // 遍历所有子节点:删除dom节点,detach ref 和 调用componentWillUnmount() function unmountDomComponents(vNode: VNode): void { let currentParentIsValid = false; @@ -342,6 +248,100 @@ function unmountDomComponents(vNode: VNode): void { ); } +// 卸载一个vNode,不会递归 +function unmountVNode(vNode: VNode): void { + switch (vNode.tag) { + case FunctionComponent: + case ForwardRef: + case MemoComponent: { + callEffectRemove(vNode); + break; + } + case ClassComponent: { + detachRef(vNode); + + const instance = vNode.realNode; + // 当constructor中抛出异常时,instance会是null,这里判断一下instance是否为空 + // suspense打断时不需要触发WillUnmount + if (instance && typeof instance.componentWillUnmount === 'function' && !vNode.isSuspended) { + callComponentWillUnmount(vNode, instance); + } + + // HorizonX会在classComponentWillUnmount中清除对VNode的引入用 + if (vNode.classComponentWillUnmount) { + vNode.classComponentWillUnmount(vNode); + vNode.classComponentWillUnmount = null; + } + break; + } + case DomComponent: { + detachRef(vNode); + break; + } + case DomPortal: { + // 这里会递归 + unmountDomComponents(vNode); + break; + } + default: { + break; + } + } +} + +function insertDom(parent, realNode, beforeDom) { + if (beforeDom) { + insertDomBefore(parent, realNode, beforeDom); + } else { + appendChildElement(parent, realNode); + } +} + +function insertOrAppendPlacementNode(node: VNode, beforeDom: Element | null, parent: Element | Container): void { + const { tag, realNode } = node; + + if (isDomVNode(node)) { + insertDom(parent, realNode, beforeDom); + } else if (tag === DomPortal) { + // 这里不做处理,直接在portal中处理 + } else { + // 插入子节点们 + let child = node.child; + while (child !== null) { + insertOrAppendPlacementNode(child, beforeDom, parent); + child = child.next; + } + } +} + +function submitAddition(vNode: VNode): void { + let parent = vNode.parent; + let parentDom; + let tag; + while (parent !== null) { + tag = parent.tag; + if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) { + parentDom = parent.realNode; + break; + } + parent = parent.parent; + } + + if ((parent.flags & ResetText) === ResetText) { + // 在insert之前先reset + clearText(parentDom); + FlagUtils.removeFlag(parent, ResetText); + } + + if ((vNode.flags & DirectAddition) === DirectAddition) { + insertOrAppendPlacementNode(vNode, null, parentDom); + FlagUtils.removeFlag(vNode, DirectAddition); + return; + } + const before = getSiblingDom(vNode); + insertOrAppendPlacementNode(vNode, before, parentDom); +} + function submitClear(vNode: VNode): void { const realNode = vNode.realNode; const cloneDom = realNode.cloneNode(false); // 复制节点后horizon添加给dom的属性未能复制 @@ -397,6 +397,13 @@ function submitDeletion(vNode: VNode): void { clearVNode(vNode); } +function submitSuspenseComponent(vNode: VNode) { + const { childStatus } = vNode.suspenseState; + if (childStatus !== SuspenseChildStatus.Init) { + hideOrUnhideAllChildren(vNode.child, childStatus === SuspenseChildStatus.ShowFallback); + } +} + function submitUpdate(vNode: VNode): void { switch (vNode.tag) { case FunctionComponent: @@ -422,13 +429,6 @@ function submitUpdate(vNode: VNode): void { } } -function submitSuspenseComponent(vNode: VNode) { - const { childStatus } = vNode.suspenseState; - if (childStatus !== SuspenseChildStatus.Init) { - hideOrUnhideAllChildren(vNode.child, childStatus === SuspenseChildStatus.ShowFallback); - } -} - function submitResetTextContent(vNode: VNode) { clearText(vNode.realNode); } From e7c29a632a900a1c40eda85e5654e3a9c490f3f7 Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 20 Apr 2023 18:47:51 +0800 Subject: [PATCH 52/74] Match-id-01940cf2f09900b8f170152db9efd6b4de21f6a6 --- .cloudbuild/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.cloudbuild/build.yml b/.cloudbuild/build.yml index 905877bf..3d8cd444 100644 --- a/.cloudbuild/build.yml +++ b/.cloudbuild/build.yml @@ -40,3 +40,8 @@ steps: auto: true buildcheck: true mode: sync + POST_BUILD: + - compile_report: + rules: + - warning /.**/ + - error /.**/ From 3d42a535d662ee7475d2716706b63bb1b713873c Mon Sep 17 00:00:00 2001 From: * <*> Date: Sat, 6 May 2023 16:14:22 +0800 Subject: [PATCH 53/74] Match-id-ad7c692603a33470196a2c41cdb3ff21136732d9 --- .../DOMPropertiesHandler.ts | 4 +- libs/horizon/src/event/EventBinding.ts | 3 +- libs/horizon/src/external/ChildrenUtil.ts | 3 +- libs/horizon/src/external/JSXElement.ts | 36 +++++--------- libs/horizon/src/renderer/RootStack.ts | 13 +++-- libs/horizon/src/renderer/Types.ts | 4 +- .../src/renderer/diff/nodeDiffComparator.ts | 9 ++-- .../src/renderer/submit/LifeCycleHandler.ts | 5 +- libs/horizon/src/renderer/vnode/VNode.ts | 4 +- libs/horizon/src/renderer/vnode/VNodeUtils.ts | 4 +- .../ComponentTest/JsxElement.test.js | 48 +++++++++++++++++++ 11 files changed, 90 insertions(+), 43 deletions(-) create mode 100644 scripts/__tests__/ComponentTest/JsxElement.test.js diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts index a0ab84b3..5c0773c1 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts @@ -48,7 +48,7 @@ export function setDomProps(dom: Element, props: Object, isNativeTag: boolean, i } } else if (propName === 'dangerouslySetInnerHTML') { dom.innerHTML = propVal.__html; - } else if (!isInit || (isInit && propVal != null)) { + } else if (!isInit || propVal != null) { updateCommonProp(dom, propName, propVal, isNativeTag); } } @@ -70,7 +70,7 @@ export function compareProps(oldProps: Object, newProps: Object): Object { for (let i = 0; i < oldPropsLength; i++) { propName = keysOfOldProps[i]; // 新属性中包含该属性或者该属性为空值的属性不需要处理 - if (keysOfNewProps.includes(propName) || oldProps[propName] == null) { + if ( oldProps[propName] == null || keysOfNewProps.includes(propName)) { continue; } diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index 25c52b90..fd7a4d0c 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -82,8 +82,7 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { } if (!events[nativeFullName]) { - const listener = listenToNativeEvent(nativeEvent, currentRoot.realNode, isCapture); - events[nativeFullName] = listener; + events[nativeFullName] = listenToNativeEvent(nativeEvent, currentRoot.realNode, isCapture); } }); } diff --git a/libs/horizon/src/external/ChildrenUtil.ts b/libs/horizon/src/external/ChildrenUtil.ts index 39c2e96c..3624a736 100644 --- a/libs/horizon/src/external/ChildrenUtil.ts +++ b/libs/horizon/src/external/ChildrenUtil.ts @@ -17,6 +17,7 @@ import { throwIfTrue } from '../renderer/utils/throwIfTrue'; import { TYPE_COMMON_ELEMENT, TYPE_PORTAL } from './JSXElementType'; import { isValidElement, JSXElement } from './JSXElement'; +import { BELONG_CLASS_VNODE_KEY } from '../renderer/vnode/VNode'; // 生成key function getItemKey(item: any, index: number): string { @@ -83,7 +84,7 @@ function callMapFun(children: any, arr: Array, prefix: string, callback: Fu mappedChild.type, newKey, mappedChild.ref, - mappedChild.belongClassVNode, + mappedChild[BELONG_CLASS_VNODE_KEY], mappedChild.props, mappedChild.src ); diff --git a/libs/horizon/src/external/JSXElement.ts b/libs/horizon/src/external/JSXElement.ts index 3de212b3..ccf43079 100644 --- a/libs/horizon/src/external/JSXElement.ts +++ b/libs/horizon/src/external/JSXElement.ts @@ -16,6 +16,7 @@ import { TYPE_COMMON_ELEMENT } from './JSXElementType'; import { getProcessingClassVNode } from '../renderer/GlobalVar'; import { Source } from '../renderer/Types'; +import { BELONG_CLASS_VNODE_KEY } from '../renderer/vnode/VNode'; /** * vtype 节点的类型,这里固定是element @@ -36,17 +37,9 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null) ref: ref, props: props, - // 所属的class组件 - belongClassVNode: null, + // 所属的class组件,clonedeep jsxElement时需要防止无限循环 + [BELONG_CLASS_VNODE_KEY]: vNode, }; - - // 在 cloneDeep JSXElement 的时候会出现死循环,需要设置belongClassVNode的enumerable为false - Object.defineProperty(ele, 'belongClassVNode', { - configurable: false, - enumerable: false, - value: vNode, - }); - if (isDev) { // 为了test判断两个 JSXElement 对象是否相等时忽略src属性,需要设置src的enumerable为false Object.defineProperty(ele, 'src', { @@ -60,11 +53,6 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null) return ele; } -function isValidKey(key) { - const keyArray = ['key', 'ref', '__source', '__self']; - return !keyArray.includes(key); -} - function mergeDefault(sourceObj, defaultObj) { Object.keys(defaultObj).forEach(key => { if (sourceObj[key] === undefined) { @@ -73,19 +61,20 @@ function mergeDefault(sourceObj, defaultObj) { }); } +// ['key', 'ref', '__source', '__self']属性不从setting获取 +const keyArray = ['key', 'ref', '__source', '__self']; + function buildElement(isClone, type, setting, children) { // setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null - const key = setting && setting.key !== undefined ? String(setting.key) : isClone ? type.key : null; - const ref = setting && setting.ref !== undefined ? setting.ref : isClone ? type.ref : null; + const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null); + const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null); const props = isClone ? { ...type.props } : {}; - let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode(); + let vNode = isClone ? type[BELONG_CLASS_VNODE_KEY] : getProcessingClassVNode(); if (setting !== null && setting !== undefined) { - const keys = Object.keys(setting); - const keyLength = keys.length; - for (let i = 0; i < keyLength; i++) { - const k = keys[i]; - if (isValidKey(k)) { + + for (const k in setting) { + if (!keyArray.includes(k)) { props[k] = setting[k]; } } @@ -109,7 +98,6 @@ function buildElement(isClone, type, setting, children) { lineNumber: setting.__source.lineNumber, }; } - return JSXElement(element, key, ref, vNode, props, src); } diff --git a/libs/horizon/src/renderer/RootStack.ts b/libs/horizon/src/renderer/RootStack.ts index 6a793a0f..814ca840 100644 --- a/libs/horizon/src/renderer/RootStack.ts +++ b/libs/horizon/src/renderer/RootStack.ts @@ -15,15 +15,20 @@ import { VNode } from './vnode/VNode'; -const currentRootStack: VNode[] = []; +const currentRootStack: (VNode | undefined)[] = []; +let index = -1; export function getCurrentRoot() { - return currentRootStack[currentRootStack.length - 1]; + return currentRootStack[index]; } export function pushCurrentRoot(root: VNode) { - return currentRootStack.push(root); + index++; + currentRootStack[index] = root; } export function popCurrentRoot() { - return currentRootStack.pop(); + const target = currentRootStack[index]; + currentRootStack[index] = undefined; + index--; + return target; } diff --git a/libs/horizon/src/renderer/Types.ts b/libs/horizon/src/renderer/Types.ts index 91b6bfbd..9598855e 100644 --- a/libs/horizon/src/renderer/Types.ts +++ b/libs/horizon/src/renderer/Types.ts @@ -13,6 +13,8 @@ * See the Mulan PSL v2 for more details. */ +import { BELONG_CLASS_VNODE_KEY } from './vnode/VNode'; + export { VNode } from './vnode/VNode'; type Trigger = (A) => void; @@ -32,7 +34,7 @@ export type JSXElement = { key: any; ref: any; props: any; - belongClassVNode: any; + [BELONG_CLASS_VNODE_KEY]: any; }; export type ProviderType = { diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index 61706bea..42f30b53 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -27,6 +27,7 @@ import { import { isSameType, getIteratorFn, isTextType, isIteratorType, isObjectType } from './DiffTools'; import { travelChildren } from '../vnode/VNodeUtils'; import { markVNodePath } from '../utils/vNodePath'; +import { BELONG_CLASS_VNODE_KEY } from '../vnode/VNode'; enum DiffCategory { TEXT_NODE = 'TEXT_NODE', @@ -166,11 +167,11 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) { if (oldNode === null || !isSameType(oldNode, newChild)) { resultNode = createVNodeFromElement(newChild); resultNode.ref = newChild.ref; - resultNode.belongClassVNode = newChild.belongClassVNode; + resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY]; } else { resultNode = updateVNode(oldNode, newChild.props); resultNode.ref = newChild.ref; - resultNode.belongClassVNode = newChild.belongClassVNode; + resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY]; } break; } else if (newChild.vtype === TYPE_PORTAL) { @@ -570,7 +571,7 @@ function diffObjectNodeHandler( } else if (isSameType(canReuseNode, newChild)) { resultNode = updateVNode(canReuseNode, newChild.props); resultNode.ref = newChild.ref; - resultNode.belongClassVNode = newChild.belongClassVNode; + resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY]; startDelVNode = resultNode.next; resultNode.next = null; } @@ -583,7 +584,7 @@ function diffObjectNodeHandler( } else { resultNode = createVNodeFromElement(newChild); resultNode.ref = newChild.ref; - resultNode.belongClassVNode = newChild.belongClassVNode; + resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY]; } } } else if (newChild.vtype === TYPE_PORTAL) { diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 056f18f9..5d02368e 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -52,6 +52,7 @@ import { import { handleSubmitError } from '../ErrorHandler'; import { travelVNodeTree, clearVNode, isDomVNode, getSiblingDom } from '../vnode/VNodeUtils'; import { shouldAutoFocus } from '../../dom/utils/Common'; +import { BELONG_CLASS_VNODE_KEY } from '../vnode/VNode'; function callComponentWillUnmount(vNode: VNode, instance: any) { try { @@ -163,8 +164,8 @@ function handleRef(vNode: VNode, ref, val) { } else if (refType === 'object') { (ref).current = val; } else { - if (vNode.belongClassVNode && vNode.belongClassVNode.realNode) { - vNode.belongClassVNode.realNode.refs[String(ref)] = val; + if (vNode[BELONG_CLASS_VNODE_KEY] && vNode[BELONG_CLASS_VNODE_KEY].realNode) { + vNode[BELONG_CLASS_VNODE_KEY].realNode.refs[String(ref)] = val; } } } diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 40ba0554..4911f887 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -38,6 +38,8 @@ import type { Hook } from '../hooks/HookType'; import { InitFlag } from './VNodeFlags'; import { Observer } from '../../horizonx/proxy/Observer'; +export const BELONG_CLASS_VNODE_KEY = Symbol('belongClassVNode'); + export class VNode { tag: VNodeTag; key: string | null; // 唯一标识符 @@ -97,7 +99,7 @@ export class VNode { toUpdateNodes: Set | null; // 保存要更新的节点 delegatedEvents: Set; - belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用 + [BELONG_CLASS_VNODE_KEY]: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用 // 状态管理器HorizonX使用 isStoreChange: boolean; diff --git a/libs/horizon/src/renderer/vnode/VNodeUtils.ts b/libs/horizon/src/renderer/vnode/VNodeUtils.ts index d9bb0f50..b195f886 100644 --- a/libs/horizon/src/renderer/vnode/VNodeUtils.ts +++ b/libs/horizon/src/renderer/vnode/VNodeUtils.ts @@ -20,9 +20,9 @@ import type {VNode} from '../Types'; import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags'; -import {isComment} from '../../dom/utils/Common'; import {getNearestVNode} from '../../dom/DOMInternalKeys'; import {Addition, InitFlag} from './VNodeFlags'; +import { BELONG_CLASS_VNODE_KEY } from './VNode'; export function travelChildren( beginVNode: VNode | null, @@ -124,7 +124,7 @@ export function clearVNode(vNode: VNode) { vNode.toUpdateNodes = null; - vNode.belongClassVNode = null; + vNode[BELONG_CLASS_VNODE_KEY] = null; if (window.__HORIZON_DEV_HOOK__) { const hook = window.__HORIZON_DEV_HOOK__; hook.deleteVNode(vNode); diff --git a/scripts/__tests__/ComponentTest/JsxElement.test.js b/scripts/__tests__/ComponentTest/JsxElement.test.js new file mode 100644 index 00000000..8794fbbf --- /dev/null +++ b/scripts/__tests__/ComponentTest/JsxElement.test.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import * as Horizon from '@cloudsop/horizon/index.ts'; + +describe('JSX Element test', () => { + it('symbol attribute prevent cloneDeep unlimited loop', function () { + + function cloneDeep(obj) { + const result = {}; + Object.keys(obj).forEach(key => { + if (obj[key] && typeof obj[key] === 'object') { + result[key] = cloneDeep(obj[key]); + } else { + result[key] = obj[key]; + } + }) + return result; + } + class Demo extends Horizon.Component { + render() { + return ( +
+ hello +
+ ); + } + } + + const ele = Horizon.createElement(Demo); + const copy = cloneDeep(ele); + expect(copy.vtype).toEqual(ele.vtype); + expect(Object.getOwnPropertySymbols(copy).length).toEqual(0); + }); +}); + From c2b21d887fab5d012e2c9aa1e9217cda3d453bb6 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 10 May 2023 10:36:46 +0800 Subject: [PATCH 54/74] Match-id-d5ebb7ce3bcb7378a88b90977da5b6329e0a5990 --- CHANGELOG.md | 4 ++++ libs/horizon/package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f42fe512..d019e752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.45 (2023-05-10) +- **core**: 修改belongClassVNode属性为Symbol提升性能 +- **core**: 优化内部循环实现,提升性能 + ## 0.0.44 (2023-04-03) - **core**: 修复horizonX-devtool Firefox75报错 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 6eb58684..5cf8027c 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.44", + "version": "0.0.45", "homepage": "", "bugs": "", "main": "index.js", From 914a0ce6e57de68156dff908a8e789edc6b1c691 Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 16 May 2023 20:58:14 +0800 Subject: [PATCH 55/74] Match-id-b191193225d35dcd637d7ef1902db54e05f62f6f --- libs/horizon/src/dom/DOMExternal.ts | 4 +- libs/horizon/src/event/EventBinding.ts | 13 +- libs/horizon/src/event/EventHub.ts | 5 + libs/horizon/src/event/EventWrapper.ts | 3 + libs/horizon/src/event/HorizonEventMain.ts | 23 +- libs/horizon/src/event/ListenerGetter.ts | 90 ++++++ libs/horizon/src/event/MouseEvent.ts | 109 +++++++ libs/horizon/src/event/utils.ts | 26 +- libs/horizon/src/renderer/render/DomPortal.ts | 4 +- libs/horizon/src/renderer/vnode/VNodeFlags.ts | 7 + .../EventTest/MouseEnterEvent.test.js | 301 ++++++++++++++++++ 11 files changed, 571 insertions(+), 14 deletions(-) create mode 100644 libs/horizon/src/event/MouseEvent.ts create mode 100644 scripts/__tests__/EventTest/MouseEnterEvent.test.js diff --git a/libs/horizon/src/dom/DOMExternal.ts b/libs/horizon/src/dom/DOMExternal.ts index 556f5952..8a04a66a 100644 --- a/libs/horizon/src/dom/DOMExternal.ts +++ b/libs/horizon/src/dom/DOMExternal.ts @@ -18,7 +18,8 @@ import { createPortal } from '../renderer/components/CreatePortal'; import type { Container } from './DOMOperator'; import { isElement } from './utils/Common'; import { findDOMByClassInst } from '../renderer/vnode/VNodeUtils'; -import { Callback } from '../renderer/UpdateHandler'; +import {Callback} from '../renderer/UpdateHandler'; +import {listenSimulatedDelegatedEvents} from '../event/EventBinding'; function createRoot(children: any, container: Container, callback?: Callback) { // 清空容器 @@ -31,6 +32,7 @@ function createRoot(children: any, container: Container, callback?: Callback) { // 调度器创建根节点,并给容器dom赋vNode结构体 const treeRoot = createTreeRootVNode(container); container._treeRoot = treeRoot; + listenSimulatedDelegatedEvents(treeRoot); // 执行回调 if (typeof callback === 'function') { diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index fd7a4d0c..ddfb016c 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -16,16 +16,14 @@ /** * 事件绑定实现,分为绑定委托事件和非委托事件 */ -import { allDelegatedHorizonEvents, allDelegatedNativeEvents } from './EventHub'; -import { isDocument } from '../dom/utils/Common'; +import {allDelegatedHorizonEvents, simulatedDelegatedEvents} from './EventHub'; +import {isDocument} from '../dom/utils/Common'; import { getNearestVNode, getNonDelegatedListenerMap } from '../dom/DOMInternalKeys'; import { asyncUpdates, runDiscreteUpdates } from '../renderer/TreeBuilder'; import { handleEventMain } from './HorizonEventMain'; import { decorateNativeEvent } from './EventWrapper'; import { VNode } from '../renderer/vnode/VNode'; -const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4); - // 触发委托事件 function triggerDelegatedEvent( nativeEvtName: string, @@ -64,6 +62,13 @@ function isCaptureEvent(horizonEventName) { return horizonEventName.slice(-7) === 'Capture'; } +// 利用冒泡事件模拟不冒泡事件,需要直接在根节点绑定 +export function listenSimulatedDelegatedEvents(root: VNode) { + for (let i = 0; i < simulatedDelegatedEvents.length; i++) { + lazyDelegateOnRoot(root, simulatedDelegatedEvents[i]); + } +} + // 事件懒委托,当用户定义事件后,再进行委托到根节点 export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { currentRoot.delegatedEvents.add(eventName); diff --git a/libs/horizon/src/event/EventHub.ts b/libs/horizon/src/event/EventHub.ts index 35ad13ac..e1a80969 100644 --- a/libs/horizon/src/event/EventHub.ts +++ b/libs/horizon/src/event/EventHub.ts @@ -15,6 +15,9 @@ // 需要委托的horizon事件和原生事件对应关系 export const allDelegatedHorizonEvents = new Map(); + +// 模拟委托事件,事件本事不冒泡,需要其他事件来触发冒泡过程 +export const simulatedDelegatedEvents = ['onMouseEnter', 'onMouseLeave']; // 所有委托的原生事件集合 export const allDelegatedNativeEvents = new Set(); @@ -49,6 +52,8 @@ export const horizonEventToNativeMap = new Map([ ['onCompositionUpdate', ['compositionupdate']], ['onChange', ['change', 'click', 'focusout', 'input']], ['onSelect', ['select']], + ['onMouseEnter', ['mouseout', 'mouseover']], + ['onMouseLeave', ['mouseout', 'mouseover']], ['onAnimationEnd', ['animationend']], ['onAnimationIteration', ['animationiteration']], diff --git a/libs/horizon/src/event/EventWrapper.ts b/libs/horizon/src/event/EventWrapper.ts index 6ed15a19..98906694 100644 --- a/libs/horizon/src/event/EventWrapper.ts +++ b/libs/horizon/src/event/EventWrapper.ts @@ -37,6 +37,9 @@ export class WrappedEvent { key: string; currentTarget: EventTarget | null = null; + target: HTMLElement; + relatedTarget: HTMLElement; + stopPropagation: () => void; preventDefault: () => void; diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index 5c0ae747..ad0e8326 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -30,7 +30,8 @@ import { import { getDomTag } from '../dom/utils/Common'; import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler'; import { getDom } from '../dom/DOMInternalKeys'; -import { recordChangeEventTargets, shouldControlValue, tryControlValue } from './FormValueController'; +import {recordChangeEventTargets, shouldControlValue, tryControlValue} from './FormValueController'; +import {getMouseEnterListeners} from './MouseEvent'; // web规范,鼠标右键key值 const RIGHT_MOUSE_BUTTON = 2; @@ -141,18 +142,26 @@ function triggerHorizonEvents( const target = nativeEvent.target || nativeEvent.srcElement!; // 触发普通委托事件 - let listenerList: ListenerUnitList = getCommonListeners(nativeEvtName, vNode, nativeEvent, target, isCapture); + const listenerList: ListenerUnitList = getCommonListeners(nativeEvtName, vNode, nativeEvent, target, isCapture); + let mouseEnterListeners: ListenerUnitList = []; + if (horizonEventToNativeMap.get('onMouseEnter')!.includes(nativeEvtName)) { + mouseEnterListeners = getMouseEnterListeners( + nativeEvtName, + vNode, + nativeEvent, + target, + ); + } + + let changeEvents: ListenerUnitList = []; // 触发特殊handler委托事件 if (!isCapture && horizonEventToNativeMap.get('onChange')!.includes(nativeEvtName)) { - const changeListeners = getChangeListeners(nativeEvtName, nativeEvent, vNode, target); - if (changeListeners.length) { - listenerList = listenerList.concat(changeListeners); - } + changeEvents = getChangeListeners(nativeEvtName, nativeEvent, vNode, target); } // 处理触发的事件队列 - processListeners(listenerList); + processListeners([...listenerList, ...mouseEnterListeners, ...changeEvents]); } // 其他事件正在执行中标记 diff --git a/libs/horizon/src/event/ListenerGetter.ts b/libs/horizon/src/event/ListenerGetter.ts index efd363fb..cda9c3e1 100644 --- a/libs/horizon/src/event/ListenerGetter.ts +++ b/libs/horizon/src/event/ListenerGetter.ts @@ -86,3 +86,93 @@ export function getListenersFromTree( return listeners; } + +// 获取enter和leave事件队列 +export function collectMouseListeners( + leaveEvent: null | WrappedEvent, + enterEvent: null | WrappedEvent, + from: VNode | null, + to: VNode | null, +): ListenerUnitList { + // 确定公共父节点,作为在树上遍历的终点 + const commonParent = from && to ? getCommonAncestor(from, to) : null; + let leaveEventList: ListenerUnitList = []; + if (from && leaveEvent) { + // 遍历树,获取绑定的leave事件 + leaveEventList = getMouseListenersFromTree( + leaveEvent, + from, + commonParent, + ); + } + let enterEventList: ListenerUnitList = []; + if (to && enterEvent) { + // 先触发父节点enter事件,所以需要逆序 + enterEventList = getMouseListenersFromTree( + enterEvent, + to, + commonParent, + ).reverse(); + } + return [...leaveEventList, ...enterEventList]; +} + +function getMouseListenersFromTree( + event: WrappedEvent, + target: VNode, + commonParent: VNode | null, +): ListenerUnitList { + const registrationName = event.customEventName; + const listeners: ListenerUnitList = []; + + let vNode = target; + while (vNode !== null) { + // commonParent作为终点 + if (vNode === commonParent) { + break; + } + const {realNode, tag} = vNode; + if (tag === DomComponent && realNode !== null) { + const currentTarget = realNode; + const listener = getListenerFromVNode(vNode, registrationName); + if (listener) { + listeners.push({ + vNode, + listener, + currentTarget, + event, + }); + } + } + vNode = vNode.parent; + } + return listeners; +} + +// 寻找两个节点的共同最近祖先,如果没有则返回null +function getCommonAncestor(instA: VNode, instB: VNode): VNode | null { + const parentsSet = new Set(); + for (let tempA: VNode | null = instA; tempA; tempA = getParent(tempA)) { + parentsSet.add(tempA); + } + for (let tempB: VNode | null = instB; tempB; tempB = getParent(tempB)) { + if (parentsSet.has(tempB)) { + return tempB; + } + } + return null; +} + +// 获取父节点 +function getParent(inst: VNode | null): VNode | null { + if (inst === null) { + return null; + } + do { + inst = inst.parent; + } while (inst && inst.tag !== DomComponent); + if (inst) { + return inst; + } + return null; +} diff --git a/libs/horizon/src/event/MouseEvent.ts b/libs/horizon/src/event/MouseEvent.ts new file mode 100644 index 00000000..e947a98f --- /dev/null +++ b/libs/horizon/src/event/MouseEvent.ts @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import {getNearestVNode} from '../dom/DOMInternalKeys'; +import {WrappedEvent} from './EventWrapper'; +import {VNode} from '../renderer/vnode/VNode'; +import {AnyNativeEvent, ListenerUnitList} from './Types'; +import {DomComponent, DomText} from '../renderer/vnode/VNodeTags'; +import {collectMouseListeners} from './ListenerGetter'; +import {getNearestMountedVNode} from './utils'; + +/** + * 背景: mouseEnter和mouseLeave事件不冒泡,所以无法直接委托给根节点进行代理 + * 实现方案:利用mouseout、mouseover事件的,找到事件触发的起点和终点,判断出鼠标移动轨迹,在轨迹中的节点触发mouseEnter和mouseLeave事件 + * 步骤: + * 1. 根节点绑定mouseout和mouseover事件 + * 2. 事件触发后找到事件的起点和终点 + * 3. 封装装enter和leave事件 + * 4. 根据起止点找到公共父节点,作为事件冒泡的终点 + * 5. 遍历treeNode,找到每个节点绑定的mouseEnter和mouseLeave监听方法 + * 例如: mouseOut事件由D->C, A节点作为公共父节点,将触发 D、B的mouseLeave事件和C节点的mouseEnter事件 + * A + * / \ + * B C + * / \ + * D E + * + */ + +function getWrapperEvents(nativeEventTarget, fromInst, toInst, nativeEvent, targetInst): (WrappedEvent | null)[] { + const vWindow = nativeEventTarget.window === nativeEventTarget ? nativeEventTarget : nativeEventTarget.ownerDocument.defaultView; + + // 起点或者终点为空的话默认值为所在window + const fromNode = fromInst?.realNode || vWindow; + const toNode = toInst?.realNode || vWindow; + let leave: WrappedEvent | null = null; + let enter: WrappedEvent | null = null; + const nativeTargetInst = getNearestVNode(nativeEventTarget); + + // 在Mounted的dom节点上render一个子组件,系统中存在两个根节点,子节点的mouseout事件触发两次,取离target近的根节点生效 + if (nativeTargetInst === targetInst) { + leave = new WrappedEvent('onMouseLeave', 'mouseleave', nativeEvent); + leave.target = fromNode; + leave.relatedTarget = toNode; + + enter = new WrappedEvent('onMouseEnter', 'mouseenter', nativeEvent); + enter.target = toNode; + enter.relatedTarget = fromNode; + } + return [leave, enter]; +} + +function getEndpointVNode( + domEventName: string, + targetInst: null | VNode, + nativeEvent: AnyNativeEvent, +): (VNode | null)[] { + let fromVNode; + let toVNode; + if (domEventName === 'mouseover') { + fromVNode = null; + toVNode = targetInst; + } else { + const related = nativeEvent.relatedTarget || nativeEvent.toElement; + fromVNode = targetInst; + toVNode = related ? getNearestVNode(related) : null; + if (toVNode !== null) { + const nearestMounted = getNearestMountedVNode(toVNode); + if (toVNode !== nearestMounted || (toVNode.tag !== DomComponent && toVNode.tag !== DomText)) { + toVNode = null; + } + } + } + return [fromVNode, toVNode]; +} + +export function getMouseEnterListeners( + domEventName: string, + targetInst: null | VNode, + nativeEvent: AnyNativeEvent, + nativeEventTarget: null | EventTarget, +): ListenerUnitList { + + // 获取起点和终点的VNode + const [fromVNode, toVNode] = getEndpointVNode(domEventName, targetInst, nativeEvent); + if (fromVNode === toVNode) { + return []; + } + + // 获取包装后的leave和enter事件 + const [leave, enter] = getWrapperEvents(nativeEventTarget, fromVNode, toVNode, nativeEvent, targetInst); + + // 收集事件的监听方法 + return collectMouseListeners(leave, enter, fromVNode, toVNode); +} + + diff --git a/libs/horizon/src/event/utils.ts b/libs/horizon/src/event/utils.ts index 6f21e52e..484d98b4 100644 --- a/libs/horizon/src/event/utils.ts +++ b/libs/horizon/src/event/utils.ts @@ -13,6 +13,10 @@ * See the Mulan PSL v2 for more details. */ +import {VNode} from '../renderer/vnode/VNode'; +import {Addition, FlagUtils} from '../renderer/vnode/VNodeFlags'; +import {TreeRoot} from '../renderer/vnode/VNodeTags'; + export function isInputElement(dom?: HTMLElement): boolean { return dom instanceof HTMLInputElement || dom instanceof HTMLTextAreaElement; } @@ -20,6 +24,26 @@ export function isInputElement(dom?: HTMLElement): boolean { export function setPropertyWritable(obj, propName) { const desc = Object.getOwnPropertyDescriptor(obj, propName); if (!desc || !desc.writable) { - Object.defineProperty(obj, propName, { writable: true }); + Object.defineProperty(obj, propName, {writable: true}); } } + +// 获取离 vNode 最近的已挂载 vNode,包含它自己 +export function getNearestMountedVNode(vNode: VNode): null | VNode { + let node = vNode; + let target = vNode; + // 如果没有alternate,说明是可能是未插入的新树,需要处理插入的副作用。 + while (node.parent) { + // 存在更新,节点未挂载,查找父节点,但是父节点也可能未挂载,需要继续往上查找无更新节点 + if (FlagUtils.hasFlag(node, Addition)) { + target = node.parent; + } + node = node.parent; + } + // 如果根节点是 Dom 类型节点,表示已经挂载 + if (node.tag === TreeRoot) { + return target; + } + // 如果没有找到根节点,意味着Tree已经卸载或者未挂载 + return null; +} diff --git a/libs/horizon/src/renderer/render/DomPortal.ts b/libs/horizon/src/renderer/render/DomPortal.ts index 4ef690fe..65188f0a 100644 --- a/libs/horizon/src/renderer/render/DomPortal.ts +++ b/libs/horizon/src/renderer/render/DomPortal.ts @@ -16,10 +16,12 @@ import type { VNode } from '../Types'; import { resetNamespaceCtx, setNamespaceCtx } from '../ContextSaver'; import { createChildrenByDiff } from '../diff/nodeDiffComparator'; -import { popCurrentRoot, pushCurrentRoot } from '../RootStack'; +import {popCurrentRoot, pushCurrentRoot} from '../RootStack'; +import {listenSimulatedDelegatedEvents} from '../../event/EventBinding'; export function bubbleRender(processing: VNode) { resetNamespaceCtx(processing); + listenSimulatedDelegatedEvents(processing); popCurrentRoot(); } diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index d1971106..31e40aa7 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -40,14 +40,20 @@ export class FlagUtils { static removeFlag(node: VNode, flag: number) { node.flags &= ~flag; } + static removeLifecycleEffectFlags(node) { node.flags &= ~LifecycleEffectArr; } + static hasAnyFlag(node: VNode) { // 有标志位 return node.flags !== InitFlag; } + static hasFlag(node: VNode, flag) { + return (node.flags & flag) !== 0; + } + static setNoFlags(node: VNode) { node.flags = InitFlag; } @@ -55,6 +61,7 @@ export class FlagUtils { static markAddition(node: VNode) { node.flags |= Addition; } + static setAddition(node: VNode) { node.flags = Addition; } diff --git a/scripts/__tests__/EventTest/MouseEnterEvent.test.js b/scripts/__tests__/EventTest/MouseEnterEvent.test.js new file mode 100644 index 00000000..e0f831c8 --- /dev/null +++ b/scripts/__tests__/EventTest/MouseEnterEvent.test.js @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Renderer from '../../../libs/horizon/src/renderer/Renderer'; +import { doc } from 'prettier'; + +describe('EnterLeaveEventPlugin', () => { + let container; + + beforeEach(() => { + jest.resetModules(); + // The container has to be attached for events to fire. + container = document.createElement('div'); + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); + container = null; + }); + + it('should set onMouseLeave relatedTarget properly in iframe', () => { + const iframe = document.createElement('iframe'); + container.appendChild(iframe); + const iframeDocument = iframe.contentDocument; + iframeDocument.write( + '
', + ); + iframeDocument.close(); + + const leaveEvents = []; + const node = Horizon.render( +
{ + e.persist(); + leaveEvents.push(e); + }} + />, + iframeDocument.body.getElementsByTagName('div')[0], + ); + + node.dispatchEvent( + new MouseEvent('mouseout', { + bubbles: true, + cancelable: true, + relatedTarget: iframe.contentWindow, + }), + ); + + expect(leaveEvents.length).toBe(1); + expect(leaveEvents[0].target).toBe(node); + expect(leaveEvents[0].relatedTarget).toBe(iframe.contentWindow); + }); + + it('should set onMouseEnter relatedTarget properly in iframe', () => { + const iframe = document.createElement('iframe'); + container.appendChild(iframe); + const iframeDocument = iframe.contentDocument; + iframeDocument.write( + '
', + ); + iframeDocument.close(); + + const enterEvents = []; + const node = Horizon.render( +
{ + e.persist(); + enterEvents.push(e); + }} + />, + iframeDocument.body.getElementsByTagName('div')[0], + ); + + node.dispatchEvent( + new MouseEvent('mouseover', { + bubbles: true, + cancelable: true, + relatedTarget: null, + }), + ); + + expect(enterEvents.length).toBe(1); + expect(enterEvents[0].target).toBe(node); + expect(enterEvents[0].relatedTarget).toBe(iframe.contentWindow); + }); + + // Regression test for https://github.com/facebook/Horizon/issues/10906. + it('should find the common parent after updates', () => { + let parentEnterCalls = 0; + let childEnterCalls = 0; + let parent = null; + + class Parent extends Horizon.Component { + render() { + return ( +
parentEnterCalls++} + ref={node => (parent = node)}> + {this.props.showChild && ( +
childEnterCalls++}/> + )} +
+ ); + } + } + + Horizon.render(, container); + // The issue only reproduced on insertion during the first update. + Horizon.render(, container); + + // Enter from parent into the child. + parent.dispatchEvent( + new MouseEvent('mouseout', { + bubbles: true, + cancelable: true, + relatedTarget: parent.firstChild, + }), + ); + + // Entering a child should fire on the child, not on the parent. + expect(childEnterCalls).toBe(1); + expect(parentEnterCalls).toBe(0); + }); + + it('should call mouseEnter once from sibling rendered inside a rendered component', done => { + const mockFn1 = jest.fn(); + const mockFn2 = jest.fn(); + const mockFn3 = jest.fn(); + + class Parent extends Horizon.Component { + constructor(props) { + super(props); + this.parentEl = Horizon.createRef(); + } + + componentDidMount() { + Horizon.render(, this.parentEl.current); + } + + render() { + return
; + } + } + + class MouseEnterDetect extends Horizon.Component { + constructor(props) { + super(props); + this.firstEl = Horizon.createRef(); + this.siblingEl = Horizon.createRef(); + } + + componentDidMount() { + this.siblingEl.current.dispatchEvent( + new MouseEvent('mouseout', { + bubbles: true, + cancelable: true, + relatedTarget: this.firstEl.current, + }), + ); + expect(mockFn1.mock.calls.length).toBe(1); + expect(mockFn2.mock.calls.length).toBe(1); + expect(mockFn3.mock.calls.length).toBe(0); + done(); + } + + render() { + return ( + +
+
+ + ); + } + } + + Horizon.render(, container); + }); + + it('should call mouseEnter when pressing a non tracked Horizon node', done => { + const mockFn = jest.fn(); + + class Parent extends Horizon.Component { + constructor(props) { + super(props); + this.parentEl = Horizon.createRef(); + } + + componentDidMount() { + Horizon.render(, this.parentEl.current); + } + + render() { + return
; + } + } + + class MouseEnterDetect extends Horizon.Component { + constructor(props) { + super(props); + this.divRef = Horizon.createRef(); + this.siblingEl = Horizon.createRef(); + } + + componentDidMount() { + const attachedNode = document.createElement('div'); + this.divRef.current.appendChild(attachedNode); + attachedNode.dispatchEvent( + new MouseEvent('mouseout', { + bubbles: true, + cancelable: true, + relatedTarget: this.siblingEl.current, + }), + ); + expect(mockFn.mock.calls.length).toBe(1); + done(); + } + + render() { + return ( +
+
+
+ ); + } + } + + Horizon.render(, container); + }); + + it('should work with portals outside of the root that has onMouseLeave', () => { + const divRef = Horizon.createRef(); + const onMouseLeave = jest.fn(); + + function Component() { + return ( +
+ {Horizon.createPortal(
, document.body)} +
+ ); + } + + Horizon.render(, container); + + // Leave from the portal div + divRef.current.dispatchEvent( + new MouseEvent('mouseout', { + bubbles: true, + cancelable: true, + relatedTarget: document.body, + }), + ); + + expect(onMouseLeave).toHaveBeenCalledTimes(1); + }); + + it('should work with portals that have onMouseEnter outside of the root ', () => { + const divRef = Horizon.createRef(); + const otherDivRef = Horizon.createRef(); + const onMouseEnter = jest.fn(); + + function Component() { + return ( +
+ {Horizon.createPortal( +
, + document.body, + )} +
+ ); + } + + Horizon.render(, container); + + // Leave from the portal div + divRef.current.dispatchEvent( + new MouseEvent('mouseout', { + bubbles: true, + cancelable: true, + relatedTarget: otherDivRef.current, + }), + ); + + expect(onMouseEnter).toHaveBeenCalledTimes(1); + }); +}); + + From ef6fd2888ac71c0f274fb25552ae5ef221f7a342 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 17 May 2023 15:39:26 +0800 Subject: [PATCH 56/74] Match-id-dc2c8508a5e1dec65fe87e6d8a44d2e02af52459 --- libs/horizon/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 5cf8027c..b4640a73 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.45", + "version": "0.0.47", "homepage": "", "bugs": "", "main": "index.js", From 8a9fdd01a63dcfd2ba508b6b581aa78d1a3d1bb5 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 17 May 2023 21:30:53 +0800 Subject: [PATCH 57/74] Match-id-bf925df05da50203ca4baf87357f2a25d21ca829 --- .cloudbuild/test.yml | 2 +- .../src/dom/DOMPropertiesHandler/StyleHandler.ts | 12 ++++++++++++ scripts/__tests__/DomTest/Attribute.test.js | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.cloudbuild/test.yml b/.cloudbuild/test.yml index a5c7242f..10f27393 100644 --- a/.cloudbuild/test.yml +++ b/.cloudbuild/test.yml @@ -13,7 +13,7 @@ env: resource: type: docker image: kweecr04.his.huawei.com:80/ecr-build-arm-gzkunpeng/euleros_v2r7spc522_x64_opmt_cs5.0_sz:v5.0 - class: 4U8G + class: 8U16G mode: toolbox cache: - type: workspace diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts index 3c3a9a24..e2fca141 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts @@ -33,6 +33,17 @@ const noUnitCSS = [ 'zoom', ]; +const length = noUnitCSS.length; +for (let i = 0; i < length; i++) { + const cssKey = noUnitCSS[i]; + const attributeKey = cssKey.charAt(0).toUpperCase() + cssKey.slice(1); + + // css 兼容性前缀 webkit: chrome, mo: IE或者Edge, Moz: 火狐 + noUnitCSS.push('Webkit' + attributeKey); + noUnitCSS.push('mo' + attributeKey); + noUnitCSS.push('Moz' + attributeKey); +} + function isNeedUnitCSS(styleName: string) { return !( noUnitCSS.includes(styleName) || @@ -78,6 +89,7 @@ export function setStyles(dom, styles) { if (name.indexOf('--') === 0) { style.setProperty(name, styleVal); } else { + // 使用这种赋值方式,浏览器可以将'WebkitLineClamp', 'backgroundColor'分别识别为'-webkit-line-clamp'和'backgroud-color' style[name] = adjustStyleValue(name, styleVal); } }); diff --git a/scripts/__tests__/DomTest/Attribute.test.js b/scripts/__tests__/DomTest/Attribute.test.js index 90823780..56004668 100755 --- a/scripts/__tests__/DomTest/Attribute.test.js +++ b/scripts/__tests__/DomTest/Attribute.test.js @@ -83,6 +83,12 @@ describe('Dom Attribute', () => { expect(window.getComputedStyle(div).getPropertyValue('height')).toBe('20px'); }); + it('WebkitLineClamp和lineClamp样式不会把数字转换成字符串或者追加"px"', () => { + Horizon.render(
, container); + // 浏览器可以将WebkitLineClamp识别为-webkit-line-clamp,测试框架不可以 + expect(container.querySelector('div').style.WebkitLineClamp).toBe(2); + }); + it('空字符串做属性名', () => { const emptyStringProps = { '': '' }; expect(() => { From 58e1de39576649f8e42a0ffef1c35f34e8af004b Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 18 May 2023 21:46:59 +0800 Subject: [PATCH 58/74] Match-id-40ac0e7e6e1927b826730b3937fe6f00c7a4b394 --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d019e752..bffc742f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.48 (2023-05-18) +- **core**: 解决style中属性WebkitLineClamp值被转换成字符串问题 + ## 0.0.45 (2023-05-10) - **core**: 修改belongClassVNode属性为Symbol提升性能 - **core**: 优化内部循环实现,提升性能 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index b4640a73..71fad88d 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.47", + "version": "0.0.48", "homepage": "", "bugs": "", "main": "index.js", From 630cc2ca262661adac31b21f44fc885b0df906ab Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 19 May 2023 10:34:15 +0800 Subject: [PATCH 59/74] Match-id-4b079f1ecf2a5cb384aed61f56d8e4485b301702 --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- libs/horizon/src/dom/DOMOperator.ts | 2 +- libs/horizon/src/dom/validators/ValidateProps.ts | 2 +- libs/horizon/src/renderer/TreeBuilder.ts | 5 +++++ libs/horizon/src/renderer/components/BaseClassComponent.ts | 2 +- 6 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0cf79c9..baecec7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.49 (2023-05-19) +- **core**: 解决当组件被销毁,业务若异步(定时器)调用setState修改状态,可能出现路径错误问题。 + ## 0.0.48 (2023-05-18) - **core**: 解决style中属性WebkitLineClamp值被转换成字符串问题 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 71fad88d..35b506ef 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.48", + "version": "0.0.49", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index a7abe22d..f83893e5 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/horizon/src/dom/DOMOperator.ts @@ -141,7 +141,7 @@ export function newTextDom(text: string, processing: VNode): Text { return textNode; } -// 提交vNode的类型为Component或者Text的更新 +// 提交vNode的类型为DomComponent或者DomText的更新 export function submitDomUpdate(tag: string, vNode: VNode) { const newProps = vNode.props; const element: Element | null = vNode.realNode; diff --git a/libs/horizon/src/dom/validators/ValidateProps.ts b/libs/horizon/src/dom/validators/ValidateProps.ts index d6629b29..f025ce63 100644 --- a/libs/horizon/src/dom/validators/ValidateProps.ts +++ b/libs/horizon/src/dom/validators/ValidateProps.ts @@ -98,7 +98,7 @@ export function validateProps(type, props) { return; } - // 非内置的变迁 + // 非内置的元素 if (!isNativeElement(type, props)) { return; } diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index eb20b0ef..9798bb8a 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -179,6 +179,11 @@ function isEqualByIndex(idx: number, pathArrays: string[][]) { function getChildByIndex(vNode: VNode, idx: number) { let node = vNode.child; for (let i = 0; i < idx; i++) { + // 场景:当组件被销毁,业务若异步(定时器)调用setState修改状态,可能出现路径错误,此处进行保护。 + if (node == null) { + return null; + } + node = node.next; } return node; diff --git a/libs/horizon/src/renderer/components/BaseClassComponent.ts b/libs/horizon/src/renderer/components/BaseClassComponent.ts index de4ff351..70b09ee4 100644 --- a/libs/horizon/src/renderer/components/BaseClassComponent.ts +++ b/libs/horizon/src/renderer/components/BaseClassComponent.ts @@ -33,7 +33,7 @@ class Component { setState(state: S, callback?: Callback) { if (isDev) { - console.error('Cant not call `this.setState` in the constructor of class component, it will do nothing'); + console.error('Can not call `this.setState` in the constructor of class component, it will do nothing'); } } } From a4671aa06f21581a23ea294581a1261b96cd03fa Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 23 May 2023 21:02:58 +0800 Subject: [PATCH 60/74] Match-id-9ffb3423d9cbe56ad4f381a2e50b90f7caa403ff --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- libs/horizon/src/external/JSXElement.ts | 8 ++++++++ libs/horizon/src/renderer/vnode/VNode.ts | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index baecec7b..b9010423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.50 (2023-05-23) +- **core**: 解决IE11不兼容Symbol问题 + ## 0.0.49 (2023-05-19) - **core**: 解决当组件被销毁,业务若异步(定时器)调用setState修改状态,可能出现路径错误问题。 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 35b506ef..286222ac 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.49", + "version": "0.0.50", "homepage": "", "bugs": "", "main": "index.js", diff --git a/libs/horizon/src/external/JSXElement.ts b/libs/horizon/src/external/JSXElement.ts index ccf43079..5ff3db84 100644 --- a/libs/horizon/src/external/JSXElement.ts +++ b/libs/horizon/src/external/JSXElement.ts @@ -40,6 +40,14 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null) // 所属的class组件,clonedeep jsxElement时需要防止无限循环 [BELONG_CLASS_VNODE_KEY]: vNode, }; + // 兼容IE11不支持Symbol + if (typeof BELONG_CLASS_VNODE_KEY === 'string') { + Object.defineProperty(ele, BELONG_CLASS_VNODE_KEY, { + configurable: false, + enumerable: false, + value: vNode, + }); + } if (isDev) { // 为了test判断两个 JSXElement 对象是否相等时忽略src属性,需要设置src的enumerable为false Object.defineProperty(ele, 'src', { diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 4911f887..bc56dee5 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -38,7 +38,7 @@ import type { Hook } from '../hooks/HookType'; import { InitFlag } from './VNodeFlags'; import { Observer } from '../../horizonx/proxy/Observer'; -export const BELONG_CLASS_VNODE_KEY = Symbol('belongClassVNode'); +export const BELONG_CLASS_VNODE_KEY = typeof Symbol === 'function' ? Symbol('belongClassVNode') : 'belongClassVNode'; export class VNode { tag: VNodeTag; From 43023ca26afb48263eae3bf78ce0913aca1fdfe9 Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 25 May 2023 09:40:45 +0800 Subject: [PATCH 61/74] Match-id-315cebc9891bb15a1e6cf5d419fd71f880796f3a --- libs/horizon/src/dom/DOMExternal.ts | 4 ++-- libs/horizon/src/event/EventBinding.ts | 4 ++-- libs/horizon/src/event/EventHub.ts | 2 +- libs/horizon/src/event/HorizonEventMain.ts | 4 ++-- libs/horizon/src/event/ListenerGetter.ts | 5 +---- libs/horizon/src/event/MouseEvent.ts | 14 +++++++------- libs/horizon/src/event/utils.ts | 6 +++--- libs/horizon/src/renderer/render/DomPortal.ts | 4 ++-- 8 files changed, 20 insertions(+), 23 deletions(-) diff --git a/libs/horizon/src/dom/DOMExternal.ts b/libs/horizon/src/dom/DOMExternal.ts index 8a04a66a..dbd78da4 100644 --- a/libs/horizon/src/dom/DOMExternal.ts +++ b/libs/horizon/src/dom/DOMExternal.ts @@ -18,8 +18,8 @@ import { createPortal } from '../renderer/components/CreatePortal'; import type { Container } from './DOMOperator'; import { isElement } from './utils/Common'; import { findDOMByClassInst } from '../renderer/vnode/VNodeUtils'; -import {Callback} from '../renderer/UpdateHandler'; -import {listenSimulatedDelegatedEvents} from '../event/EventBinding'; +import { Callback } from '../renderer/UpdateHandler'; +import { listenSimulatedDelegatedEvents } from '../event/EventBinding'; function createRoot(children: any, container: Container, callback?: Callback) { // 清空容器 diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index ddfb016c..e4e0f59d 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -16,8 +16,8 @@ /** * 事件绑定实现,分为绑定委托事件和非委托事件 */ -import {allDelegatedHorizonEvents, simulatedDelegatedEvents} from './EventHub'; -import {isDocument} from '../dom/utils/Common'; +import { allDelegatedHorizonEvents, simulatedDelegatedEvents } from './EventHub'; +import { isDocument } from '../dom/utils/Common'; import { getNearestVNode, getNonDelegatedListenerMap } from '../dom/DOMInternalKeys'; import { asyncUpdates, runDiscreteUpdates } from '../renderer/TreeBuilder'; import { handleEventMain } from './HorizonEventMain'; diff --git a/libs/horizon/src/event/EventHub.ts b/libs/horizon/src/event/EventHub.ts index e1a80969..51d9ced6 100644 --- a/libs/horizon/src/event/EventHub.ts +++ b/libs/horizon/src/event/EventHub.ts @@ -16,7 +16,7 @@ // 需要委托的horizon事件和原生事件对应关系 export const allDelegatedHorizonEvents = new Map(); -// 模拟委托事件,事件本事不冒泡,需要其他事件来触发冒泡过程 +// 模拟委托事件,不冒泡事件需要利用其他事件来触发冒泡过程 export const simulatedDelegatedEvents = ['onMouseEnter', 'onMouseLeave']; // 所有委托的原生事件集合 export const allDelegatedNativeEvents = new Set(); diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index ad0e8326..a2a20d5e 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -30,8 +30,8 @@ import { import { getDomTag } from '../dom/utils/Common'; import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler'; import { getDom } from '../dom/DOMInternalKeys'; -import {recordChangeEventTargets, shouldControlValue, tryControlValue} from './FormValueController'; -import {getMouseEnterListeners} from './MouseEvent'; +import { recordChangeEventTargets, shouldControlValue, tryControlValue } from './FormValueController'; +import { getMouseEnterListeners } from './MouseEvent'; // web规范,鼠标右键key值 const RIGHT_MOUSE_BUTTON = 2; diff --git a/libs/horizon/src/event/ListenerGetter.ts b/libs/horizon/src/event/ListenerGetter.ts index cda9c3e1..3b19cf2a 100644 --- a/libs/horizon/src/event/ListenerGetter.ts +++ b/libs/horizon/src/event/ListenerGetter.ts @@ -171,8 +171,5 @@ function getParent(inst: VNode | null): VNode | null { do { inst = inst.parent; } while (inst && inst.tag !== DomComponent); - if (inst) { - return inst; - } - return null; + return inst || null; } diff --git a/libs/horizon/src/event/MouseEvent.ts b/libs/horizon/src/event/MouseEvent.ts index e947a98f..3eb38123 100644 --- a/libs/horizon/src/event/MouseEvent.ts +++ b/libs/horizon/src/event/MouseEvent.ts @@ -13,13 +13,13 @@ * See the Mulan PSL v2 for more details. */ -import {getNearestVNode} from '../dom/DOMInternalKeys'; -import {WrappedEvent} from './EventWrapper'; -import {VNode} from '../renderer/vnode/VNode'; -import {AnyNativeEvent, ListenerUnitList} from './Types'; -import {DomComponent, DomText} from '../renderer/vnode/VNodeTags'; -import {collectMouseListeners} from './ListenerGetter'; -import {getNearestMountedVNode} from './utils'; +import { getNearestVNode } from '../dom/DOMInternalKeys'; +import { WrappedEvent } from './EventWrapper'; +import { VNode } from '../renderer/vnode/VNode'; +import { AnyNativeEvent, ListenerUnitList } from './Types'; +import { DomComponent, DomText } from '../renderer/vnode/VNodeTags'; +import { collectMouseListeners } from './ListenerGetter'; +import { getNearestMountedVNode } from './utils'; /** * 背景: mouseEnter和mouseLeave事件不冒泡,所以无法直接委托给根节点进行代理 diff --git a/libs/horizon/src/event/utils.ts b/libs/horizon/src/event/utils.ts index 484d98b4..46feae8a 100644 --- a/libs/horizon/src/event/utils.ts +++ b/libs/horizon/src/event/utils.ts @@ -13,9 +13,9 @@ * See the Mulan PSL v2 for more details. */ -import {VNode} from '../renderer/vnode/VNode'; -import {Addition, FlagUtils} from '../renderer/vnode/VNodeFlags'; -import {TreeRoot} from '../renderer/vnode/VNodeTags'; +import { VNode } from '../renderer/vnode/VNode'; +import { Addition, FlagUtils } from '../renderer/vnode/VNodeFlags'; +import { TreeRoot } from '../renderer/vnode/VNodeTags'; export function isInputElement(dom?: HTMLElement): boolean { return dom instanceof HTMLInputElement || dom instanceof HTMLTextAreaElement; diff --git a/libs/horizon/src/renderer/render/DomPortal.ts b/libs/horizon/src/renderer/render/DomPortal.ts index 65188f0a..df0e3e30 100644 --- a/libs/horizon/src/renderer/render/DomPortal.ts +++ b/libs/horizon/src/renderer/render/DomPortal.ts @@ -16,8 +16,8 @@ import type { VNode } from '../Types'; import { resetNamespaceCtx, setNamespaceCtx } from '../ContextSaver'; import { createChildrenByDiff } from '../diff/nodeDiffComparator'; -import {popCurrentRoot, pushCurrentRoot} from '../RootStack'; -import {listenSimulatedDelegatedEvents} from '../../event/EventBinding'; +import { popCurrentRoot, pushCurrentRoot } from '../RootStack'; +import { listenSimulatedDelegatedEvents } from '../../event/EventBinding'; export function bubbleRender(processing: VNode) { resetNamespaceCtx(processing); From 60cf2e32ce7642376bfbdb286c21d81a33c3e743 Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 29 May 2023 14:59:17 +0800 Subject: [PATCH 62/74] Match-id-785473ead33f3451a1dddc93c6cea6efae7c6563 --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- package.json | 3 +++ .../EventTest/MouseEnterEvent.test.js | 26 ++++++------------- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9010423..a49e9d42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.51 (2023-05-29) +- **core**: 增加mouseenter和mouseleave事件代理机制 + ## 0.0.50 (2023-05-23) - **core**: 解决IE11不兼容Symbol问题 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 286222ac..f2ed85ce 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.50", + "version": "0.0.51", "homepage": "", "bugs": "", "main": "index.js", diff --git a/package.json b/package.json index d122cacb..94d6ac45 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,7 @@ { + "name": "@cloudsop/horizon", + "description": "Horizon is a JavaScript framework library.", + "version": "0.0.51", "private": true, "workspaces": [ "libs/*" diff --git a/scripts/__tests__/EventTest/MouseEnterEvent.test.js b/scripts/__tests__/EventTest/MouseEnterEvent.test.js index e0f831c8..6256062f 100644 --- a/scripts/__tests__/EventTest/MouseEnterEvent.test.js +++ b/scripts/__tests__/EventTest/MouseEnterEvent.test.js @@ -14,15 +14,12 @@ */ import * as Horizon from '@cloudsop/horizon/index.ts'; -import * as Renderer from '../../../libs/horizon/src/renderer/Renderer'; -import { doc } from 'prettier'; -describe('EnterLeaveEventPlugin', () => { +describe('mouseenter和mouseleave事件测试', () => { let container; beforeEach(() => { jest.resetModules(); - // The container has to be attached for events to fire. container = document.createElement('div'); document.body.appendChild(container); }); @@ -32,7 +29,7 @@ describe('EnterLeaveEventPlugin', () => { container = null; }); - it('should set onMouseLeave relatedTarget properly in iframe', () => { + it('在iframe中mouseleave事件的relateTarget属性', () => { const iframe = document.createElement('iframe'); container.appendChild(iframe); const iframeDocument = iframe.contentDocument; @@ -65,7 +62,7 @@ describe('EnterLeaveEventPlugin', () => { expect(leaveEvents[0].relatedTarget).toBe(iframe.contentWindow); }); - it('should set onMouseEnter relatedTarget properly in iframe', () => { + it('在iframe中mouseenter事件的relateTarget属性', () => { const iframe = document.createElement('iframe'); container.appendChild(iframe); const iframeDocument = iframe.contentDocument; @@ -98,8 +95,7 @@ describe('EnterLeaveEventPlugin', () => { expect(enterEvents[0].relatedTarget).toBe(iframe.contentWindow); }); - // Regression test for https://github.com/facebook/Horizon/issues/10906. - it('should find the common parent after updates', () => { + it('从新渲染的子组件触发mouseout事件,子组件响应mouseenter事件,父节点不响应', () => { let parentEnterCalls = 0; let childEnterCalls = 0; let parent = null; @@ -119,10 +115,8 @@ describe('EnterLeaveEventPlugin', () => { } Horizon.render(, container); - // The issue only reproduced on insertion during the first update. Horizon.render(, container); - // Enter from parent into the child. parent.dispatchEvent( new MouseEvent('mouseout', { bubbles: true, @@ -130,13 +124,11 @@ describe('EnterLeaveEventPlugin', () => { relatedTarget: parent.firstChild, }), ); - - // Entering a child should fire on the child, not on the parent. expect(childEnterCalls).toBe(1); expect(parentEnterCalls).toBe(0); }); - it('should call mouseEnter once from sibling rendered inside a rendered component', done => { + it('render一个新组件,兄弟节点触发mouseout事件,mouseenter事件响应一次', done => { const mockFn1 = jest.fn(); const mockFn2 = jest.fn(); const mockFn3 = jest.fn(); @@ -190,7 +182,7 @@ describe('EnterLeaveEventPlugin', () => { Horizon.render(, container); }); - it('should call mouseEnter when pressing a non tracked Horizon node', done => { + it('未被horizon管理的节点触发mouseout事件,mouseenter事件也能正常触发', done => { const mockFn = jest.fn(); class Parent extends Horizon.Component { @@ -241,7 +233,7 @@ describe('EnterLeaveEventPlugin', () => { Horizon.render(, container); }); - it('should work with portals outside of the root that has onMouseLeave', () => { + it('外部portal节点触发的mouseout事件,根节点的mouseleave事件也能响应', () => { const divRef = Horizon.createRef(); const onMouseLeave = jest.fn(); @@ -255,7 +247,6 @@ describe('EnterLeaveEventPlugin', () => { Horizon.render(, container); - // Leave from the portal div divRef.current.dispatchEvent( new MouseEvent('mouseout', { bubbles: true, @@ -267,7 +258,7 @@ describe('EnterLeaveEventPlugin', () => { expect(onMouseLeave).toHaveBeenCalledTimes(1); }); - it('should work with portals that have onMouseEnter outside of the root ', () => { + it('外部portal节点触发的mouseout事件,根节点的mouseEnter事件也能响应', () => { const divRef = Horizon.createRef(); const otherDivRef = Horizon.createRef(); const onMouseEnter = jest.fn(); @@ -285,7 +276,6 @@ describe('EnterLeaveEventPlugin', () => { Horizon.render(, container); - // Leave from the portal div divRef.current.dispatchEvent( new MouseEvent('mouseout', { bubbles: true, From f9163eb932b26748d2ea4d40176b703614ed7fde Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 29 May 2023 19:26:32 +0800 Subject: [PATCH 63/74] Match-id-b3bb6926bb4d0b41babdaea8b4963c69795b6fe9 --- CHANGELOG.md | 3 +++ libs/horizon/package.json | 2 +- package.json | 9 ++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a49e9d42..a5ebd7a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.52 (2023-05-29) +- **build**: ejs属于构建依赖 + ## 0.0.51 (2023-05-29) - **core**: 增加mouseenter和mouseleave事件代理机制 diff --git a/libs/horizon/package.json b/libs/horizon/package.json index f2ed85ce..f64cd277 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "0.0.51", + "version": "0.0.52", "homepage": "", "bugs": "", "main": "index.js", diff --git a/package.json b/package.json index 94d6ac45..22d01c6d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "@cloudsop/horizon", + "name": "horizon-core", "description": "Horizon is a JavaScript framework library.", - "version": "0.0.51", + "version": "0.0.52", "private": true, "workspaces": [ "libs/*" @@ -51,6 +51,7 @@ "@typescript-eslint/eslint-plugin": "4.8.0", "@typescript-eslint/parser": "4.8.0", "babel-jest": "^27.5.1", + "ejs": "^3.1.8", "eslint": "7.13.0", "eslint-config-prettier": "^6.9.0", "eslint-plugin-jest": "^22.15.0", @@ -69,7 +70,5 @@ "node": ">=10.x", "npm": ">=7.x" }, - "dependencies": { - "ejs": "^3.1.8" - } + "dependencies": {} } From a72fb10ef3fc66d224239810d56d9f120c68f5f9 Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 2 Jun 2023 16:44:47 +0800 Subject: [PATCH 64/74] Match-id-c3d530104af803ccd0d7aef09548027b4c8d60f7 --- libs/horizon/src/dom/SelectionRangeHandler.ts | 4 +- libs/horizon/src/dom/utils/Common.ts | 4 + .../src/dom/validators/ValidateProps.ts | 5 +- .../src/dom/valueHandler/InputValueHandler.ts | 9 +- .../dom/valueHandler/SelectValueHandler.ts | 7 +- .../dom/valueHandler/TextareaValueHandler.ts | 5 +- libs/horizon/src/event/EventBinding.ts | 21 ++-- libs/horizon/src/event/FormValueController.ts | 6 +- libs/horizon/src/event/ListenerGetter.ts | 95 ++++++++++--------- .../src/renderer/diff/nodeDiffComparator.ts | 10 +- 10 files changed, 88 insertions(+), 78 deletions(-) diff --git a/libs/horizon/src/dom/SelectionRangeHandler.ts b/libs/horizon/src/dom/SelectionRangeHandler.ts index e09e0dad..a8f643c1 100644 --- a/libs/horizon/src/dom/SelectionRangeHandler.ts +++ b/libs/horizon/src/dom/SelectionRangeHandler.ts @@ -17,7 +17,7 @@ * 处理文本框、输入框中框选范围内的数据 */ -import { getIFrameFocusedDom, isText } from './utils/Common'; +import { getIFrameFocusedDom, isNull, isText } from './utils/Common'; import { isElement } from './utils/Common'; @@ -30,7 +30,7 @@ function setSelectionRange(dom: HTMLInputElement | HTMLTextAreaElement, range) { const { start, end } = range; let realEnd = end; - if (realEnd == null) { + if (isNull(realEnd)) { realEnd = start; } diff --git a/libs/horizon/src/dom/utils/Common.ts b/libs/horizon/src/dom/utils/Common.ts index 30c172cf..001a32f1 100644 --- a/libs/horizon/src/dom/utils/Common.ts +++ b/libs/horizon/src/dom/utils/Common.ts @@ -84,3 +84,7 @@ const types = ['button', 'input', 'select', 'textarea']; export function shouldAutoFocus(tagName: string, props: Props): boolean { return types.includes(tagName) ? Boolean(props.autoFocus) : false; } + +export function isNull(val) { + return val === null || val === undefined; +} diff --git a/libs/horizon/src/dom/validators/ValidateProps.ts b/libs/horizon/src/dom/validators/ValidateProps.ts index f025ce63..9eabcc09 100644 --- a/libs/horizon/src/dom/validators/ValidateProps.ts +++ b/libs/horizon/src/dom/validators/ValidateProps.ts @@ -14,6 +14,7 @@ */ import { getPropDetails, PROPERTY_TYPE, PropDetails } from './PropertiesData'; +import { isNull } from '../utils/Common'; const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/; @@ -73,7 +74,7 @@ export function isInvalidValue( propDetails: PropDetails | null, isNativeTag: boolean ): boolean { - if (value == null) { + if (isNull(value)) { return true; } @@ -104,7 +105,7 @@ export function validateProps(type, props) { } // style属性必须是对象 - if (props.style != null && typeof props.style !== 'object') { + if (!isNull(props.style) && typeof props.style !== 'object') { throw new Error('style should be a object.'); } diff --git a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts index 824d57dd..749e85b3 100644 --- a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts @@ -15,6 +15,7 @@ import { updateCommonProp } from '../DOMPropertiesHandler/UpdateCommonProp'; import { Props } from '../utils/Interface'; +import { isNull } from '../utils/Common'; function getInitValue(dom: HTMLInputElement, props: Props) { const { value, defaultValue, checked, defaultChecked } = props; @@ -29,7 +30,7 @@ function getInitValue(dom: HTMLInputElement, props: Props) { export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) { // checked属于必填属性,无法置 let { checked } = props; - if (checked == null) { + if (isNull(checked)) { checked = getInitValue(dom, props).initChecked; } @@ -45,12 +46,12 @@ export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) { export function updateInputValue(dom: HTMLInputElement, props: Props) { const { value, checked } = props; - if (value != null) { + if (!isNull(value)) { // 处理 dom.value 逻辑 if (dom.value !== String(value)) { dom.value = String(value); } - } else if (checked != null) { + } else if (!isNull(checked)) { updateCommonProp(dom, 'checked', checked, true); } } @@ -60,7 +61,7 @@ export function setInitInputValue(dom: HTMLInputElement, props: Props) { const { value, defaultValue } = props; const { initValue, initChecked } = getInitValue(dom, props); - if (value != null || defaultValue != null) { + if (!isNull(value) || !isNull(defaultValue)) { // value 的使用优先级 value 属性 > defaultValue 属性 > 空字符串 const initValueStr = String(initValue); diff --git a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts index fb9f859e..86297315 100644 --- a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts @@ -14,6 +14,7 @@ */ import { HorizonSelect, Props } from '../utils/Interface'; +import { isNull } from '../utils/Common'; function updateMultipleValue(options, newValues) { const newValueSet = new Set(); @@ -69,18 +70,18 @@ export function updateSelectValue(dom: HorizonSelect, props: Props, isInit = fal dom._multiple = newMultiple; // 设置了 value 属性 - if (value != null) { + if (!isNull(value)) { updateValue(dom.options, value, newMultiple); } else if (oldMultiple !== newMultiple) { // 修改了 multiple 属性 // 切换 multiple 之后,如果设置了 defaultValue 需要重新应用 - if (defaultValue != null) { + if (!isNull(defaultValue)) { updateValue(dom.options, defaultValue, newMultiple); } else { // 恢复到未选定状态 updateValue(dom.options, newMultiple ? [] : '', newMultiple); } - } else if (isInit && defaultValue != null) { + } else if (isInit && !isNull(defaultValue)) { // 设置了 defaultValue 属性 updateValue(dom.options, defaultValue, newMultiple); } diff --git a/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts b/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts index 82b2203e..e852cd1f 100644 --- a/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts @@ -14,12 +14,13 @@ */ import { Props } from '../utils/Interface'; +import { isNull } from '../utils/Common'; // 值的优先级 value > children > defaultValue function getInitValue(props: Props) { const { value } = props; - if (value == null) { + if (isNull(value)) { const { defaultValue, children } = props; let initValue = defaultValue; @@ -30,7 +31,7 @@ function getInitValue(props: Props) { } // defaultValue 属性未配置,置为空字符串 - initValue = initValue != null ? initValue : ''; + initValue = initValue ?? ''; return initValue; } else { return value; diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index e4e0f59d..56e1567c 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -62,13 +62,6 @@ function isCaptureEvent(horizonEventName) { return horizonEventName.slice(-7) === 'Capture'; } -// 利用冒泡事件模拟不冒泡事件,需要直接在根节点绑定 -export function listenSimulatedDelegatedEvents(root: VNode) { - for (let i = 0; i < simulatedDelegatedEvents.length; i++) { - lazyDelegateOnRoot(root, simulatedDelegatedEvents[i]); - } -} - // 事件懒委托,当用户定义事件后,再进行委托到根节点 export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { currentRoot.delegatedEvents.add(eventName); @@ -80,11 +73,8 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { const nativeFullName = isCapture ? nativeEvent + 'capture' : nativeEvent; // 事件存储在DOM节点属性,避免多个VNode(root和portal)对应同一个DOM, 造成事件重复监听 - let events = currentRoot.realNode.$EV; - - if (!events) { - events = (currentRoot.realNode as any).$EV = {}; - } + currentRoot.realNode.$EV = currentRoot.realNode.$EV || {}; + const events = currentRoot.realNode.$EV; if (!events[nativeFullName]) { events[nativeFullName] = listenToNativeEvent(nativeEvent, currentRoot.realNode, isCapture); @@ -92,6 +82,13 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { }); } +// 利用冒泡事件模拟不冒泡事件,需要直接在根节点绑定 +export function listenSimulatedDelegatedEvents(root: VNode) { + for (let i = 0; i < simulatedDelegatedEvents.length; i++) { + lazyDelegateOnRoot(root, simulatedDelegatedEvents[i]); + } +} + // 通过horizon事件名获取到native事件名 function getNativeEvtName(horizonEventName, capture) { let nativeName; diff --git a/libs/horizon/src/event/FormValueController.ts b/libs/horizon/src/event/FormValueController.ts index 3c63a299..2e296e6d 100644 --- a/libs/horizon/src/event/FormValueController.ts +++ b/libs/horizon/src/event/FormValueController.ts @@ -14,7 +14,7 @@ */ import { getVNodeProps } from '../dom/DOMInternalKeys'; -import { getDomTag } from '../dom/utils/Common'; +import { getDomTag, isNull } from '../dom/utils/Common'; import { Props } from '../dom/utils/Interface'; import { updateTextareaValue } from '../dom/valueHandler/TextareaValueHandler'; import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler'; @@ -41,14 +41,14 @@ function controlInputValue(inputDom: HTMLInputElement, props: Props) { const { name, type } = props; // 如果是 radio,找出同一form内,name相同的Radio,更新它们Handler的Value - if (type === 'radio' && name != null) { + if (type === 'radio' && !isNull(name)) { const radioList = document.querySelectorAll(`input[type="radio"][name="${name}"]`); for (let i = 0; i < radioList.length; i++) { const radio = radioList[i]; if (radio === inputDom) { continue; } - if (radio.form != null && inputDom.form != null && radio.form !== inputDom.form) { + if (!isNull(radio.form) && !isNull(inputDom.form) && radio.form !== inputDom.form) { continue; } diff --git a/libs/horizon/src/event/ListenerGetter.ts b/libs/horizon/src/event/ListenerGetter.ts index 3b19cf2a..02feee6f 100644 --- a/libs/horizon/src/event/ListenerGetter.ts +++ b/libs/horizon/src/event/ListenerGetter.ts @@ -87,34 +87,30 @@ export function getListenersFromTree( return listeners; } -// 获取enter和leave事件队列 -export function collectMouseListeners( - leaveEvent: null | WrappedEvent, - enterEvent: null | WrappedEvent, - from: VNode | null, - to: VNode | null, -): ListenerUnitList { - // 确定公共父节点,作为在树上遍历的终点 - const commonParent = from && to ? getCommonAncestor(from, to) : null; - let leaveEventList: ListenerUnitList = []; - if (from && leaveEvent) { - // 遍历树,获取绑定的leave事件 - leaveEventList = getMouseListenersFromTree( - leaveEvent, - from, - commonParent, - ); + +// 获取父节点 +function getParent(inst: VNode | null): VNode | null { + if (inst === null) { + return null; } - let enterEventList: ListenerUnitList = []; - if (to && enterEvent) { - // 先触发父节点enter事件,所以需要逆序 - enterEventList = getMouseListenersFromTree( - enterEvent, - to, - commonParent, - ).reverse(); + do { + inst = inst.parent; + } while (inst && inst.tag !== DomComponent); + return inst || null; +} + +// 寻找两个节点的共同最近祖先,如果没有则返回null +function getCommonAncestor(instA: VNode, instB: VNode): VNode | null { + const parentsSet = new Set(); + for (let tempA: VNode | null = instA; tempA; tempA = getParent(tempA)) { + parentsSet.add(tempA); } - return [...leaveEventList, ...enterEventList]; + for (let tempB: VNode | null = instB; tempB; tempB = getParent(tempB)) { + if (parentsSet.has(tempB)) { + return tempB; + } + } + return null; } function getMouseListenersFromTree( @@ -149,27 +145,32 @@ function getMouseListenersFromTree( return listeners; } -// 寻找两个节点的共同最近祖先,如果没有则返回null -function getCommonAncestor(instA: VNode, instB: VNode): VNode | null { - const parentsSet = new Set(); - for (let tempA: VNode | null = instA; tempA; tempA = getParent(tempA)) { - parentsSet.add(tempA); +// 获取enter和leave事件队列 +export function collectMouseListeners( + leaveEvent: null | WrappedEvent, + enterEvent: null | WrappedEvent, + from: VNode | null, + to: VNode | null, +): ListenerUnitList { + // 确定公共父节点,作为在树上遍历的终点 + const commonParent = from && to ? getCommonAncestor(from, to) : null; + let leaveEventList: ListenerUnitList = []; + if (from && leaveEvent) { + // 遍历树,获取绑定的leave事件 + leaveEventList = getMouseListenersFromTree( + leaveEvent, + from, + commonParent, + ); } - for (let tempB: VNode | null = instB; tempB; tempB = getParent(tempB)) { - if (parentsSet.has(tempB)) { - return tempB; - } + let enterEventList: ListenerUnitList = []; + if (to && enterEvent) { + // 先触发父节点enter事件,所以需要逆序 + enterEventList = getMouseListenersFromTree( + enterEvent, + to, + commonParent, + ).reverse(); } - return null; -} - -// 获取父节点 -function getParent(inst: VNode | null): VNode | null { - if (inst === null) { - return null; - } - do { - inst = inst.parent; - } while (inst && inst.tag !== DomComponent); - return inst || null; + return [...leaveEventList, ...enterEventList]; } diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index 42f30b53..83798c68 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -182,6 +182,10 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) { } break; } + break; + } + default: { + break; } } @@ -374,7 +378,7 @@ function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newC // 4. 新节点还有一部分,但是老节点已经没有了 if (oldNode === null) { let isDirectAdd = false; - // TODO: 是否可以扩大至非dom类型节点 + // 是否可以扩大至非dom类型节点待确认 // 如果dom节点在上次添加前没有节点,说明本次添加时,可以直接添加到最后,不需要通过 getSiblingDom 函数找到 before 节点 if ( parentNode.tag === DomComponent && @@ -513,7 +517,7 @@ function diffIteratorNodesHandler( } // 新节点是字符串类型 -function diffStringNodeHandler(parentNode: VNode, newChild: any, firstChildVNode: VNode, isComparing: boolean) { +function diffStringNodeHandler(parentNode: VNode, newChild: any, firstChildVNode: VNode | null, isComparing: boolean) { let newTextNode: VNode | null = null; // 第一个vNode是Text,则复用 @@ -560,7 +564,7 @@ function diffObjectNodeHandler( } let resultNode: VNode | null = null; - let startDelVNode = firstChildVNode; + let startDelVNode: VNode | null = firstChildVNode; if (newChild.vtype === TYPE_COMMON_ELEMENT) { if (canReuseNode) { // 可以复用 From c6857c5bc523201103c624761744fe651b2675fa Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 5 Jun 2023 18:53:22 +0800 Subject: [PATCH 65/74] Match-id-b88442c85aaf06a604c65a19c68060ba9cb9e44e --- libs/horizon/src/dom/DOMExternal.ts | 4 +- libs/horizon/src/dom/DOMOperator.ts | 18 +++-- .../DOMPropertiesHandler.ts | 12 ++-- .../dom/DOMPropertiesHandler/StyleHandler.ts | 2 +- libs/horizon/src/dom/SelectionRangeHandler.ts | 2 +- libs/horizon/src/external/ChildrenUtil.ts | 64 +++++++++--------- libs/horizon/src/external/JSXElement.ts | 9 ++- libs/horizon/src/horizonx/adapters/redux.ts | 3 +- libs/horizon/src/horizonx/devtools/index.ts | 4 +- .../src/horizonx/proxy/HooklessObserver.ts | 3 - libs/horizon/src/horizonx/proxy/Observer.ts | 2 +- .../src/horizonx/proxy/ProxyHandler.ts | 6 +- .../proxy/handlers/CollectionProxyHandler.ts | 4 +- .../src/horizonx/proxy/handlers/MapProxy.ts | 31 ++++----- .../proxy/handlers/ObjectProxyHandler.ts | 3 +- .../src/horizonx/proxy/handlers/SetProxy.ts | 31 ++++----- .../horizonx/proxy/handlers/WeakMapProxy.ts | 65 ++++++++++--------- .../horizonx/proxy/handlers/WeakSetProxy.ts | 17 ++--- 18 files changed, 152 insertions(+), 128 deletions(-) diff --git a/libs/horizon/src/dom/DOMExternal.ts b/libs/horizon/src/dom/DOMExternal.ts index dbd78da4..6c459952 100644 --- a/libs/horizon/src/dom/DOMExternal.ts +++ b/libs/horizon/src/dom/DOMExternal.ts @@ -73,7 +73,7 @@ function executeRender(children: any, container: Container, callback?: Callback) } function findDOMNode(domOrEle?: Element): null | Element | Text { - if (domOrEle == null) { + if (domOrEle === null || domOrEle === undefined) { return null; } @@ -103,7 +103,7 @@ function removeRootEventLister(container: Container) { // 卸载入口 function destroy(container: Container): boolean { - if (container && container._treeRoot) { + if (container._treeRoot) { syncUpdates(() => { executeRender(null, container, () => { removeRootEventLister(container); diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index f83893e5..55c218ae 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/horizon/src/dom/DOMOperator.ts @@ -46,7 +46,7 @@ function getChildNS(parentNS: string | null, tagName: string): string { return NSS.html; } - if (parentNS == null || parentNS === NSS.html) { + if (parentNS === null || parentNS === NSS.html) { // 没有父命名空间,或父命名空间为xhtml return NSS[tagName] ?? NSS.html; } @@ -130,7 +130,8 @@ export function isTextChild(type: string, props: Props): boolean { return ( props.dangerouslySetInnerHTML && typeof props.dangerouslySetInnerHTML === 'object' && - props.dangerouslySetInnerHTML.__html != null + props.dangerouslySetInnerHTML.__html !== null && + props.dangerouslySetInnerHTML.__html !== undefined ); } } @@ -148,7 +149,7 @@ export function submitDomUpdate(tag: string, vNode: VNode) { if (tag === DomComponent) { // DomComponent类型 - if (element != null) { + if (element !== null || element !== undefined) { const type = vNode.type; const changeList = vNode.changeList; vNode.changeList = null; @@ -158,7 +159,14 @@ export function submitDomUpdate(tag: string, vNode: VNode) { updateVNodeProps(element, newProps); // 应用diff更新Properties. // 当一个选中的radio改变名称,浏览器使另一个radio的复选框为false. - if (type === 'input' && newProps.type === 'radio' && newProps.name != null && newProps.checked != null) { + if ( + type === 'input' + && newProps.type === 'radio' + && newProps.name !== null + && newProps.name !== undefined + && newProps.checked !== null + && newProps.checked !== undefined + ) { updateCommonProp(element, 'checked', newProps.checked, true); } const isNativeTag = isNativeElement(type, newProps); @@ -204,7 +212,7 @@ export function hideDom(tag: string, dom: Element | Text) { // 不隐藏元素 export function unHideDom(tag: string, dom: Element | Text, props: Props) { if (tag === DomComponent) { - dom.style.display = adjustStyleValue('display', props?.style?.display ?? ''); + dom.style.display = adjustStyleValue('display', props.style?.display ?? ''); } else if (tag === DomText) { dom.textContent = props; } diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts index 5c0773c1..ede38907 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts @@ -48,7 +48,7 @@ export function setDomProps(dom: Element, props: Object, isNativeTag: boolean, i } } else if (propName === 'dangerouslySetInnerHTML') { dom.innerHTML = propVal.__html; - } else if (!isInit || propVal != null) { + } else if (!isInit || propVal !== null || propVal !== undefined) { updateCommonProp(dom, propName, propVal, isNativeTag); } } @@ -70,7 +70,7 @@ export function compareProps(oldProps: Object, newProps: Object): Object { for (let i = 0; i < oldPropsLength; i++) { propName = keysOfOldProps[i]; // 新属性中包含该属性或者该属性为空值的属性不需要处理 - if ( oldProps[propName] == null || keysOfNewProps.includes(propName)) { + if ( oldProps[propName] === null || oldProps[propName] === undefined || keysOfNewProps.includes(propName)) { continue; } @@ -103,9 +103,13 @@ export function compareProps(oldProps: Object, newProps: Object): Object { for (let i = 0; i < keysOfNewProps.length; i++) { propName = keysOfNewProps[i]; newPropValue = newProps[propName]; - oldPropValue = oldProps != null ? oldProps[propName] : null; + oldPropValue = oldProps !== null ? oldProps[propName] : null; - if (newPropValue === oldPropValue || (newPropValue == null && oldPropValue == null)) { + if ( + newPropValue === oldPropValue + || ((newPropValue === null || newPropValue === undefined) + && (oldPropValue === null || oldPropValue === undefined)) + ) { // 新旧属性值未发生变化,或者新旧属性皆为空值,不需要进行处理 continue; } diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts index e2fca141..522fa956 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts @@ -67,7 +67,7 @@ export function adjustStyleValue(name, value) { if (typeof value === 'number' && value !== 0 && isNeedUnitCSS(name)) { validValue = `${value}px`; - } else if (value === '' || value == null || typeof value === 'boolean') { + } else if (value === '' || value === null || value === undefined || typeof value === 'boolean') { validValue = ''; } diff --git a/libs/horizon/src/dom/SelectionRangeHandler.ts b/libs/horizon/src/dom/SelectionRangeHandler.ts index e09e0dad..f9488f9b 100644 --- a/libs/horizon/src/dom/SelectionRangeHandler.ts +++ b/libs/horizon/src/dom/SelectionRangeHandler.ts @@ -30,7 +30,7 @@ function setSelectionRange(dom: HTMLInputElement | HTMLTextAreaElement, range) { const { start, end } = range; let realEnd = end; - if (realEnd == null) { + if (realEnd === null || realEnd === undefined) { realEnd = start; } diff --git a/libs/horizon/src/external/ChildrenUtil.ts b/libs/horizon/src/external/ChildrenUtil.ts index 3624a736..55f66e3c 100644 --- a/libs/horizon/src/external/ChildrenUtil.ts +++ b/libs/horizon/src/external/ChildrenUtil.ts @@ -21,44 +21,13 @@ import { BELONG_CLASS_VNODE_KEY } from '../renderer/vnode/VNode'; // 生成key function getItemKey(item: any, index: number): string { - if (typeof item === 'object' && item !== null && item.key != null) { + if (typeof item === 'object' && item !== null && item.key !== null && item.key !== undefined) { return '.$' + item.key; } // 使用36进制减少生成字符串的长度以节省空间 return '.' + index.toString(36); } -function mapChildrenToArray(children: any, arr: Array, prefix: string, callback?: Function): number | void { - const type = typeof children; - switch (type) { - // 继承原有规格,undefined和boolean类型按照null处理 - case 'undefined': - case 'boolean': - callMapFun(null, arr, prefix, callback); - return; - case 'number': - case 'string': - callMapFun(children, arr, prefix, callback); - return; - case 'object': - if (children === null) { - callMapFun(null, arr, prefix, callback); - return; - } - const vtype = children.vtype; - if (vtype === TYPE_COMMON_ELEMENT || vtype === TYPE_PORTAL) { - callMapFun(children, arr, prefix, callback); - return; - } - if (Array.isArray(children)) { - processArrayChildren(children, arr, prefix, callback); - return; - } - throw new Error('Object is invalid as a Horizon child. '); - // No Default - } -} - function processArrayChildren(children: any, arr: Array, prefix: string, callback: Function) { for (let i = 0; i < children.length; i++) { const childItem = children[i]; @@ -93,6 +62,37 @@ function callMapFun(children: any, arr: Array, prefix: string, callback: Fu } } +function mapChildrenToArray(children: any, arr: Array, prefix: string, callback?: Function): number | void { + const type = typeof children; + switch (type) { + // 继承原有规格,undefined和boolean类型按照null处理 + case 'undefined': + case 'boolean': + callMapFun(null, arr, prefix, callback); + return; + case 'number': + case 'string': + callMapFun(children, arr, prefix, callback); + return; + case 'object': + if (children === null) { + callMapFun(null, arr, prefix, callback); + return; + } + const vtype = children.vtype; + if (vtype === TYPE_COMMON_ELEMENT || vtype === TYPE_PORTAL) { + callMapFun(children, arr, prefix, callback); + return; + } + if (Array.isArray(children)) { + processArrayChildren(children, arr, prefix, callback); + return; + } + throw new Error('Object is invalid as a Horizon child. '); + // No Default + } +} + // 在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg function mapChildren(children: any, func: Function, context?: any): Array { if (children === null || children === undefined) { diff --git a/libs/horizon/src/external/JSXElement.ts b/libs/horizon/src/external/JSXElement.ts index 5ff3db84..e54e2054 100644 --- a/libs/horizon/src/external/JSXElement.ts +++ b/libs/horizon/src/external/JSXElement.ts @@ -72,10 +72,15 @@ function mergeDefault(sourceObj, defaultObj) { // ['key', 'ref', '__source', '__self']属性不从setting获取 const keyArray = ['key', 'ref', '__source', '__self']; +function getSettingArgs(setting, isClone, type) { + const key = setting && setting.key !== undefined ? String(setting.key) : (isClone ? type.key : null); + const ref = setting && setting.ref !== undefined ? setting.ref : (isClone ? type.ref : null); + return { key, ref }; +} + function buildElement(isClone, type, setting, children) { // setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null - const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null); - const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null); + const { key, ref } = getSettingArgs(setting, isClone, type); const props = isClone ? { ...type.props } : {}; let vNode = isClone ? type[BELONG_CLASS_VNODE_KEY] : getProcessingClassVNode(); diff --git a/libs/horizon/src/horizonx/adapters/redux.ts b/libs/horizon/src/horizonx/adapters/redux.ts index 97b7a483..ffffc59f 100644 --- a/libs/horizon/src/horizonx/adapters/redux.ts +++ b/libs/horizon/src/horizonx/adapters/redux.ts @@ -140,7 +140,8 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): } export function combineReducers(reducers: { [key: string]: Reducer }): Reducer { - return (state = {}, action) => { + return (state, action) => { + state = state || {}; const newState = {}; Object.entries(reducers).forEach(([key, reducer]) => { newState[key] = reducer(state[key], action); diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 64a0f189..eda6d696 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -145,7 +145,7 @@ function getAffectedComponents() { const process = Array.from(allStores[key].$config.state._horizonObserver.keyVNodes.values()); while (process.length) { let pivot = process.shift() as { tag: 'string' }; - if (pivot?.tag) subRes.add(pivot); + if (pivot.tag) subRes.add(pivot); if (pivot?.toString() === '[object Set]') Array.from(pivot).forEach(item => process.push(item)); } res[key] = Array.from(subRes).map(vnode => { @@ -192,7 +192,7 @@ window.addEventListener('message', messageEvent => { const store = getStore(data.storeId); if (!store?.[data.action]) return; - const action = store.$queue?.[data.action]; + const action = store.$queue[data.action]; const params = data.params; action(...params); } diff --git a/libs/horizon/src/horizonx/proxy/HooklessObserver.ts b/libs/horizon/src/horizonx/proxy/HooklessObserver.ts index a47ad752..c823ef0e 100644 --- a/libs/horizon/src/horizonx/proxy/HooklessObserver.ts +++ b/libs/horizon/src/horizonx/proxy/HooklessObserver.ts @@ -41,9 +41,6 @@ export class HooklessObserver implements IObserver { triggerChangeListeners(mutation: any): void { this.listeners.forEach(listener => { - if (!listener) { - return; - } listener(mutation); }); } diff --git a/libs/horizon/src/horizonx/proxy/Observer.ts b/libs/horizon/src/horizonx/proxy/Observer.ts index b1aa43d7..de009283 100644 --- a/libs/horizon/src/horizonx/proxy/Observer.ts +++ b/libs/horizon/src/horizonx/proxy/Observer.ts @@ -81,7 +81,7 @@ export class Observer implements IObserver { const vNodes = this.keyVNodes.get(key); //NOTE: using Set directly can lead to deadlock const vNodeArray = Array.from(vNodes || []); - vNodeArray?.forEach((vNode: VNode) => { + vNodeArray.forEach((vNode: VNode) => { if (vNode.isStoreChange) { // VNode已经被触发过,不再重复触发 return; diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index be83bee5..b20a6dfb 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -31,7 +31,9 @@ export function getObserver(rawObj: any): Observer { return rawObj[OBSERVER_KEY]; } -export function createProxy(rawObj: any, isHookObserver = true, listener: { current: (...args) => any }): any { +export function createProxy(rawObj: any, isHookObserver: boolean, listener: { current: (...args) => any }): any { + isHookObserver = isHookObserver || true; + // 不是对象(是原始数据类型)不用代理 if (!(rawObj && isObject(rawObj))) { return rawObj; @@ -83,7 +85,7 @@ export function createProxy(rawObj: any, isHookObserver = true, listener: { curr // 原生对象 或 函数 proxyObj = createObjectProxy(rawObj, false, { current: change => { - listener?.current(change); + listener.current(change); }, }); } diff --git a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts index 1b0f0d37..e6d4309c 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts @@ -21,9 +21,11 @@ import { createMapProxy } from './MapProxy'; export function createCollectionProxy( rawObj: Object, - hookObserver = true, + hookObserver: boolean, listener: { current: (...args) => any } ): Object { + hookObserver = hookObserver || true; + if (isWeakSet(rawObj)) { return createWeakSetProxy(rawObj, hookObserver, listener); } diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts index dd51f07f..0921cf7a 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts @@ -20,7 +20,8 @@ import { isPanelActive } from '../../devtools'; const COLLECTION_CHANGE = '_collectionChange'; -export function createMapProxy(rawObj: Object, hookObserver = true, listener: { current: (...args) => any }): Object { +export function createMapProxy(rawObj: Object, hookObserver: boolean, listener: { current: (...args) => any }): Object { + hookObserver = hookObserver || true; let listeners: ((mutation) => {})[] = []; let oldData: [any, any][] = []; let proxies = new Map(); @@ -314,6 +315,20 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: { return wrapIterator(rawObj, rawObj.entries(), 'entries'); } + const handler = { + get, + set, + delete: deleteFun, + clear, + has, + entries, + forEach, + keys, + values, + // 判断Symbol类型,兼容IE + [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf, + }; + function get(rawObj: { size: number }, key: any, receiver: any): any { if (key === 'size') { return size(rawObj); @@ -357,20 +372,6 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: { return Reflect.get(rawObj, key, receiver); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const handler = { - get, - set, - delete: deleteFun, - clear, - has, - entries, - forEach, - keys, - values, - // 判断Symbol类型,兼容IE - [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf, - }; - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const boundHandler = {}; Object.entries(handler).forEach(([id, val]) => { boundHandler[id] = (...args: any[]) => { diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 0afa6583..28aeb1f2 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -41,9 +41,10 @@ function set(rawObj: object, key: string, value: any, receiver: any): boolean { export function createObjectProxy( rawObj: T, - singleLevel = false, + singleLevel: boolean, listener: { current: (...args) => any } ): ProxyHandler { + singleLevel = singleLevel || false; let listeners = [] as ((...args) => void)[]; function get(rawObj: object, key: string | symbol, receiver: any): any { diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts index b0409ad6..e5efb07b 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts @@ -20,9 +20,10 @@ const COLLECTION_CHANGE = '_collectionChange'; export function createSetProxy( rawObj: T, - hookObserver = true, + hookObserver: boolean, listener: { current: (...args) => any } ): ProxyHandler { + hookObserver = hookObserver || true; let listeners: ((mutation) => {})[] = []; let proxies = new WeakMap(); @@ -129,6 +130,20 @@ export function createSetProxy( return rawObj.size; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const handler = { + get, + add, + delete: deleteFun, + has, + clear, + forEach, + forOf, + entries, + keys, + values, + [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf, + }; + function get(rawObj: { size: number }, key: any, receiver: any): any { if (Object.prototype.hasOwnProperty.call(handler, key)) { const value = Reflect.get(handler, key, receiver); @@ -267,20 +282,6 @@ export function createSetProxy( }); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const handler = { - get, - add, - delete: deleteFun, - has, - clear, - forEach, - forOf, - entries, - keys, - values, - [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf, - }; - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// getObserver(rawObj).addListener(change => { if (!change.parents) change.parents = []; change.parents.push(rawObj); diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts index d5f87ff3..3792dc4d 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts @@ -22,11 +22,43 @@ const COLLECTION_CHANGE = '_collectionChange'; export function createWeakMapProxy( rawObj: Object, - hookObserver = true, + hookObserver: boolean, listener: { current: (...args) => any } ): Object { + hookObserver = hookObserver ||true; let listeners: ((mutation) => {})[] = []; + const handler = { + get, + set, + add, + delete: deleteFun, + clear, + has, + }; + + function getFun(rawObj: { get: (key: any) => any }, key: any) { + const observer = getObserver(rawObj); + observer.useProp(key); + + const value = rawObj.get(key); + // 对于value也需要进一步代理 + const valProxy = createProxy(value, hookObserverMap.get(rawObj), { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [key]: change.mutation.from }, + { ...rawObj, [key]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, + }); + + return valProxy; + } + function get(rawObj: { size: number }, key: any, receiver: any): any { if (key === 'get') { return getFun.bind(null, rawObj); @@ -66,28 +98,6 @@ export function createWeakMapProxy( return Reflect.get(rawObj, key, receiver); } - function getFun(rawObj: { get: (key: any) => any }, key: any) { - const observer = getObserver(rawObj); - observer.useProp(key); - - const value = rawObj.get(key); - // 对于value也需要进一步代理 - const valProxy = createProxy(value, hookObserverMap.get(rawObj), { - current: change => { - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, [key]: change.mutation.from }, - { ...rawObj, [key]: change.mutation.to } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); - }, - }); - - return valProxy; - } - // Map的set方法 function set( rawObj: { get: (key: any) => any; set: (key: any, value: any) => any; has: (key: any) => boolean }, @@ -171,15 +181,6 @@ export function createWeakMapProxy( return false; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const handler = { - get, - set, - add, - delete: deleteFun, - clear, - has, - }; - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// getObserver(rawObj).addListener(change => { if (!change.parents) change.parents = []; change.parents.push(rawObj); diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts index 27becbe9..4fd6b788 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts @@ -18,12 +18,20 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; export function createWeakSetProxy( rawObj: T, - hookObserver = true, + hookObserver: boolean, listener: { current: (...args) => any } ): ProxyHandler { + hookObserver = hookObserver || true; let listeners: ((mutation) => {})[] = []; let proxies = new WeakMap(); + const handler = { + get, + add, + delete: deleteFun, + has, + }; + function get(rawObj: { size: number }, key: any, receiver: any): any { if (Object.prototype.hasOwnProperty.call(handler, key)) { const value = Reflect.get(handler, key, receiver); @@ -109,13 +117,6 @@ export function createWeakSetProxy( return false; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const handler = { - get, - add, - delete: deleteFun, - has, - }; - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// getObserver(rawObj).addListener(change => { if (!change.parents) change.parents = []; change.parents.push(rawObj); From 6175b4e30889d9092a1c9c52fa3b741f4b3c1052 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 7 Jun 2023 10:24:02 +0800 Subject: [PATCH 66/74] Match-id-e0350c199ac3e8354d02617e9f692d807c56383a --- libs/horizon/src/renderer/TreeBuilder.ts | 7 +- libs/horizon/src/renderer/hooks/BaseHook.ts | 2 +- .../src/renderer/hooks/UseImperativeHook.ts | 3 +- .../src/renderer/render/ContextProvider.ts | 2 +- .../src/renderer/render/DomComponent.ts | 3 +- libs/horizon/src/renderer/render/DomText.ts | 3 +- .../src/renderer/render/SuspenseComponent.ts | 2 +- .../render/class/ClassLifeCycleProcessor.ts | 2 +- .../src/renderer/taskExecutor/RenderQueue.ts | 2 +- .../src/renderer/taskExecutor/TaskQueue.ts | 4 +- .../src/renderer/vnode/VNodeCreator.ts | 5 +- libs/horizon/src/renderer/vnode/VNodeFlags.ts | 76 +++++++++---------- 12 files changed, 58 insertions(+), 53 deletions(-) diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 9798bb8a..cc99e968 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -54,6 +54,7 @@ import { import { getPathArr } from './utils/vNodePath'; import { injectUpdater } from '../external/devtools'; import { popCurrentRoot, pushCurrentRoot } from './RootStack'; +import { isNull } from '../dom/utils/Common'; // 不可恢复错误 let unrecoverableErrorDuringBuild: any = null; @@ -139,7 +140,7 @@ function bubbleVNode(vNode: VNode): void { node = parent; // 更新processing,抛出异常时可以使用 processing = node; - } while (node !== null); + } while (!isNull(node)); // 修改结果 if (getBuildResult() === BuildInComplete) { @@ -180,7 +181,7 @@ function getChildByIndex(vNode: VNode, idx: number) { let node = vNode.child; for (let i = 0; i < idx; i++) { // 场景:当组件被销毁,业务若异步(定时器)调用setState修改状态,可能出现路径错误,此处进行保护。 - if (node == null) { + if (isNull(node)) { return null; } @@ -225,7 +226,7 @@ export function calcStartUpdateVNode(treeRoot: VNode) { const pathIndex = Number(startNodePath[i]); node = getChildByIndex(node, pathIndex)!; // 路径错误时,回退到从根更新 - if (node == null) { + if (isNull(node)) { return treeRoot; } } diff --git a/libs/horizon/src/renderer/hooks/BaseHook.ts b/libs/horizon/src/renderer/hooks/BaseHook.ts index 3ffcaf62..0109bf62 100644 --- a/libs/horizon/src/renderer/hooks/BaseHook.ts +++ b/libs/horizon/src/renderer/hooks/BaseHook.ts @@ -52,7 +52,7 @@ export function createHook(state: any = null): Hook { return currentHook; } -export function getNextHook(hook: Hook, hooks: Array>) { +export function getNextHook(hook: Hook, hooks: Array>): Hook | null { return hooks[hook.hIndex + 1] || null; } diff --git a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts index 3ae2feef..94d69743 100644 --- a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts +++ b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts @@ -22,7 +22,7 @@ function isNotNull(object: any): boolean { return object !== null && object !== undefined; } -function effectFunc(func: () => R, ref: Ref | ((any) => any) | null): (() => void) | void { +function effectFunc(func: () => R, ref: Ref | ((any) => any) | null): (() => void) | null { if (typeof ref === 'function') { const value = func(); ref(value); @@ -37,6 +37,7 @@ function effectFunc(func: () => R, ref: Ref | ((any) => any) | null): (() ref.current = null; }; } + return null; } export function useImperativeHandleImpl( diff --git a/libs/horizon/src/renderer/render/ContextProvider.ts b/libs/horizon/src/renderer/render/ContextProvider.ts index 1eadd6cc..6a8f7810 100644 --- a/libs/horizon/src/renderer/render/ContextProvider.ts +++ b/libs/horizon/src/renderer/render/ContextProvider.ts @@ -64,7 +64,7 @@ function handleContextChange(processing: VNode, context: ContextType): void node => { const depContexts = node.depContexts; if (depContexts && depContexts.length) { - isMatch = matchDependencies(depContexts, context, node) ?? isMatch; + isMatch = matchDependencies(depContexts, context, node) || isMatch; } }, node => diff --git a/libs/horizon/src/renderer/render/DomComponent.ts b/libs/horizon/src/renderer/render/DomComponent.ts index 2eaef98b..247986ad 100644 --- a/libs/horizon/src/renderer/render/DomComponent.ts +++ b/libs/horizon/src/renderer/render/DomComponent.ts @@ -23,6 +23,7 @@ import { markRef } from './BaseComponent'; import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags'; import { travelVNodeTree } from '../vnode/VNodeUtils'; import { createChildrenByDiff } from '../diff/nodeDiffComparator'; +import { isNull } from '../../dom/utils/Common'; function updateDom(processing: VNode, type: any, newProps: Props) { // 如果oldProps !== newProps,意味着存在更新,并且需要处理其相关的副作用 @@ -54,7 +55,7 @@ export function bubbleRender(processing: VNode) { const type = processing.type; const newProps = processing.props; - if (!processing.isCreated && processing.realNode != null) { + if (!processing.isCreated && !isNull(processing.realNode)) { // 更新dom属性 updateDom(processing, type, newProps); diff --git a/libs/horizon/src/renderer/render/DomText.ts b/libs/horizon/src/renderer/render/DomText.ts index 6f46901c..2f8cec01 100644 --- a/libs/horizon/src/renderer/render/DomText.ts +++ b/libs/horizon/src/renderer/render/DomText.ts @@ -18,6 +18,7 @@ import type { VNode } from '../Types'; import { throwIfTrue } from '../utils/throwIfTrue'; import { newTextDom } from '../../dom/DOMOperator'; import { FlagUtils } from '../vnode/VNodeFlags'; +import { isNull } from '../../dom/utils/Common'; export function captureRender(): VNode | null { return null; @@ -26,7 +27,7 @@ export function captureRender(): VNode | null { export function bubbleRender(processing: VNode) { const newText = processing.props; - if (!processing.isCreated && processing.realNode != null) { + if (!processing.isCreated && !isNull(processing.realNode)) { // 更新 const oldText = processing.oldProps; // 如果文本不同,将其标记为更新 diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index 82c04dee..4bc1a3b6 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -165,7 +165,7 @@ function canCapturePromise(vNode: VNode | null): boolean { // 处理Suspense子组件抛出的promise export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, promise: PromiseType): boolean { - let vNode = parent; + let vNode: VNode | null = parent; // 向上找到最近的不在fallback状态的Suspense,并触发重新渲染 do { diff --git a/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts b/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts index f1fe2d1d..bf3c9c14 100644 --- a/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts +++ b/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts @@ -35,7 +35,7 @@ export function callDerivedStateFromProps( const newState = getDerivedStateFromProps(nextProps, oldState); // 组件未返回state,需要返回旧的preState - processing.state = newState === null || newState === undefined ? oldState : { ...oldState, ...newState }; + processing.state = newState ? { ...oldState, ...newState } : oldState; } } diff --git a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts index 729dfbc8..bec90454 100644 --- a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts +++ b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts @@ -35,7 +35,7 @@ function callRenderQueue() { try { let callback; - while ((callback = renderQueue.shift())) { + while (callback = renderQueue.shift()) { callback(); } diff --git a/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts b/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts index 3c0bfcfe..84fa08bd 100644 --- a/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts +++ b/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts @@ -63,12 +63,12 @@ export function add(node: Node): void { export function first(): Node | null { const val: Node | null | undefined = taskQueue[0]; - return val !== undefined ? val : null; + return val ?? null; } export function shift(): Node | null { const val = taskQueue.shift(); - return val !== undefined ? val : null; + return val ?? null; } export function remove(node: Node) { diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 73944854..27e08e99 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -44,6 +44,7 @@ import { import { VNode } from './VNode'; import { JSXElement, Source } from '../Types'; import { markVNodePath } from '../utils/vNodePath'; +import { isNull } from '../../dom/utils/Common'; const typeLazyMap = { [TYPE_FORWARD_REF]: ForwardRef, @@ -136,7 +137,7 @@ export function createUndeterminedVNode(type, key, props, source: Source | null) vNodeTag = typeMap[type.vtype]; isLazy = type.vtype === TYPE_LAZY; } else { - throw Error(`Component type is invalid, got: ${type == null ? type : componentType}`); + throw Error(`Component type is invalid, got: ${isNull(type) ? type : componentType}`); } const vNode = newVirtualNode(vNodeTag, key, props); @@ -183,7 +184,7 @@ export function createTreeRootVNode(container) { return vNode; } -// TODO: 暂时保留给测试用例使用,后续修改测试用例 +// 暂时保留给测试用例使用,后续修改测试用例 export function createVNode(tag: VNodeTag | string, ...secondArg) { let vNode = null; switch (tag) { diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index 31e40aa7..7ca52c55 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -36,71 +36,71 @@ export const ForceUpdate = /** */ 1 << 12; // For suspense export const Clear = /** */ 1 << 13; const LifecycleEffectArr = Update | Callback | Ref | Snapshot; -export class FlagUtils { - static removeFlag(node: VNode, flag: number) { +export const FlagUtils = { + removeFlag(node: VNode, flag: number) { node.flags &= ~flag; - } + }, - static removeLifecycleEffectFlags(node) { + removeLifecycleEffectFlags(node) { node.flags &= ~LifecycleEffectArr; - } + }, - static hasAnyFlag(node: VNode) { + hasAnyFlag(node: VNode) { // 有标志位 return node.flags !== InitFlag; - } + }, - static hasFlag(node: VNode, flag) { + hasFlag(node: VNode, flag) { return (node.flags & flag) !== 0; - } + }, - static setNoFlags(node: VNode) { + setNoFlags(node: VNode) { node.flags = InitFlag; - } + }, - static markAddition(node: VNode) { + markAddition(node: VNode) { node.flags |= Addition; - } + }, - static setAddition(node: VNode) { + setAddition(node: VNode) { node.flags = Addition; - } + }, - static markDirectAddition(node: VNode) { + markDirectAddition(node: VNode) { node.flags |= DirectAddition; - } - static markUpdate(node: VNode) { + }, + markUpdate(node: VNode) { node.flags |= Update; - } - static setDeletion(node: VNode) { + }, + setDeletion(node: VNode) { node.flags = Deletion; - } - static markContentReset(node: VNode) { + }, + markContentReset(node: VNode) { node.flags |= ResetText; - } - static markCallback(node: VNode) { + }, + markCallback(node: VNode) { node.flags |= Callback; - } - static markDidCapture(node: VNode) { + }, + markDidCapture(node: VNode) { node.flags |= DidCapture; - } - static markShouldCapture(node: VNode) { + }, + markShouldCapture(node: VNode) { node.flags |= ShouldCapture; - } - static markRef(node: VNode) { + }, + markRef(node: VNode) { node.flags |= Ref; - } - static markSnapshot(node: VNode) { + }, + markSnapshot(node: VNode) { node.flags |= Snapshot; - } - static markInterrupted(node: VNode) { + }, + markInterrupted(node: VNode) { node.flags |= Interrupted; - } - static markForceUpdate(node: VNode) { + }, + markForceUpdate(node: VNode) { node.flags |= ForceUpdate; - } + }, - static markClear(node: VNode) { + markClear(node: VNode) { node.flags |= Clear; } } From dd7b65c45ac8cda1313f70f1614b6e4c20e86ddb Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 7 Jun 2023 14:50:36 +0800 Subject: [PATCH 67/74] Match-id-b3271adc745fbcd1091dfc0f70385d726500f682 --- libs/horizon/src/dom/DOMOperator.ts | 6 ++-- .../DOMPropertiesHandler.ts | 6 ++-- libs/horizon/src/external/JSXElement.ts | 9 ++---- libs/horizon/src/horizonx/CommonUtils.ts | 3 +- libs/horizon/src/horizonx/devtools/index.ts | 29 +++++++++++-------- libs/horizon/src/horizonx/proxy/Observer.ts | 4 --- .../src/horizonx/proxy/ProxyHandler.ts | 27 +++++++++-------- .../proxy/handlers/ArrayProxyHandler.ts | 2 +- .../proxy/handlers/CollectionProxyHandler.ts | 14 ++++----- .../src/horizonx/proxy/handlers/MapProxy.ts | 13 +++++---- .../proxy/handlers/ObjectProxyHandler.ts | 7 ++--- .../src/horizonx/proxy/handlers/SetProxy.ts | 5 ++-- .../horizonx/proxy/handlers/WeakMapProxy.ts | 7 ++--- .../horizonx/proxy/handlers/WeakSetProxy.ts | 5 ++-- 14 files changed, 67 insertions(+), 70 deletions(-) diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index 55c218ae..809a0ae8 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/horizon/src/dom/DOMOperator.ts @@ -149,7 +149,7 @@ export function submitDomUpdate(tag: string, vNode: VNode) { if (tag === DomComponent) { // DomComponent类型 - if (element !== null || element !== undefined) { + if (element !== null && element !== undefined) { const type = vNode.type; const changeList = vNode.changeList; vNode.changeList = null; @@ -210,9 +210,9 @@ export function hideDom(tag: string, dom: Element | Text) { } // 不隐藏元素 -export function unHideDom(tag: string, dom: Element | Text, props: Props) { +export function unHideDom(tag: string, dom: Element | Text, props?: Props) { if (tag === DomComponent) { - dom.style.display = adjustStyleValue('display', props.style?.display ?? ''); + dom.style.display = adjustStyleValue('display', props?.style?.display ?? ''); } else if (tag === DomText) { dom.textContent = props; } diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts index ede38907..bdf59af7 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts @@ -48,7 +48,7 @@ export function setDomProps(dom: Element, props: Object, isNativeTag: boolean, i } } else if (propName === 'dangerouslySetInnerHTML') { dom.innerHTML = propVal.__html; - } else if (!isInit || propVal !== null || propVal !== undefined) { + } else if (!isInit || (propVal !== null && propVal !== undefined)) { updateCommonProp(dom, propName, propVal, isNativeTag); } } @@ -103,7 +103,7 @@ export function compareProps(oldProps: Object, newProps: Object): Object { for (let i = 0; i < keysOfNewProps.length; i++) { propName = keysOfNewProps[i]; newPropValue = newProps[propName]; - oldPropValue = oldProps !== null ? oldProps[propName] : null; + oldPropValue = oldProps !== null && oldProps !== undefined ? oldProps[propName] : null; if ( newPropValue === oldPropValue @@ -144,7 +144,7 @@ export function compareProps(oldProps: Object, newProps: Object): Object { } else if (propName === 'dangerouslySetInnerHTML') { newHTML = newPropValue ? newPropValue.__html : undefined; oldHTML = oldPropValue ? oldPropValue.__html : undefined; - if (newHTML != null) { + if (newHTML !== null && newHTML !== undefined) { if (oldHTML !== newHTML) { toUpdateProps[propName] = newPropValue; } diff --git a/libs/horizon/src/external/JSXElement.ts b/libs/horizon/src/external/JSXElement.ts index e54e2054..5ff3db84 100644 --- a/libs/horizon/src/external/JSXElement.ts +++ b/libs/horizon/src/external/JSXElement.ts @@ -72,15 +72,10 @@ function mergeDefault(sourceObj, defaultObj) { // ['key', 'ref', '__source', '__self']属性不从setting获取 const keyArray = ['key', 'ref', '__source', '__self']; -function getSettingArgs(setting, isClone, type) { - const key = setting && setting.key !== undefined ? String(setting.key) : (isClone ? type.key : null); - const ref = setting && setting.ref !== undefined ? setting.ref : (isClone ? type.ref : null); - return { key, ref }; -} - function buildElement(isClone, type, setting, children) { // setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null - const { key, ref } = getSettingArgs(setting, isClone, type); + const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null); + const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null); const props = isClone ? { ...type.props } : {}; let vNode = isClone ? type[BELONG_CLASS_VNODE_KEY] : getProcessingClassVNode(); diff --git a/libs/horizon/src/horizonx/CommonUtils.ts b/libs/horizon/src/horizonx/CommonUtils.ts index cf2c2bb8..b42ff3be 100644 --- a/libs/horizon/src/horizonx/CommonUtils.ts +++ b/libs/horizon/src/horizonx/CommonUtils.ts @@ -115,7 +115,8 @@ export function resolveMutation(from, to) { if (res[i].mutation) found = true; } } - // TODO: resolve shifts + + // need to resolve shifts return { mutation: found, items: res, from, to }; } diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index eda6d696..711c0d62 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -116,18 +116,22 @@ function makeStoreSnapshot({ type, data }) { export const devtools = { // returns vNode id from horizon devtools getVNodeId: vNode => { - if (!isPanelActive()) return; + if (!isPanelActive()) { + return null; + } window['__HORIZON_DEV_HOOK__'].send(); // update list first return window['__HORIZON_DEV_HOOK__'].getVnodeId(vNode); }, // sends horizonx devtool message to extension emit: (type, data) => { - if (!isPanelActive()) return; + if (!isPanelActive()) { + return; + } window.postMessage({ type: 'HORIZON_DEV_TOOLS', payload: makeStoreSnapshot({ type, data }), from: 'dev tool hook', - }); + }, ''); }, }; @@ -135,7 +139,7 @@ export const devtools = { function getAffectedComponents() { const allStores = getAllStores(); const keys = Object.keys(allStores); - let res = {}; + const res = {}; keys.forEach(key => { if (!allStores[key].$config.state._horizonObserver.keyVNodes) { res[key] = []; @@ -144,17 +148,17 @@ function getAffectedComponents() { const subRes = new Set(); const process = Array.from(allStores[key].$config.state._horizonObserver.keyVNodes.values()); while (process.length) { - let pivot = process.shift() as { tag: 'string' }; + const pivot = process.shift() as { tag: 'string' }; if (pivot.tag) subRes.add(pivot); - if (pivot?.toString() === '[object Set]') Array.from(pivot).forEach(item => process.push(item)); + if (pivot.toString() === '[object Set]') Array.from(pivot).forEach(item => process.push(item)); } - res[key] = Array.from(subRes).map(vnode => { + res[key] = Array.from(subRes).map(vNode => { return { - name: vnode?.type + name: vNode?.type .toString() .replace(/\{.*\}/, '{...}') .replace('function ', ''), - nodeId: window.__HORIZON_DEV_HOOK__.getVnodeId(vnode), + nodeId: window.__HORIZON_DEV_HOOK__.getVnodeId(vNode), }; }); }); @@ -163,7 +167,7 @@ function getAffectedComponents() { } // listens to messages from background -window.addEventListener('message', messageEvent => { +window.addEventListener('message', (messageEvent?) => { if (messageEvent?.data?.payload?.type === 'horizonx request observed components') { // get observed components setTimeout(() => { @@ -171,7 +175,7 @@ window.addEventListener('message', messageEvent => { type: 'HORIZON_DEV_TOOLS', payload: { type: OBSERVED_COMPONENTS, data: getAffectedComponents() }, from: 'dev tool hook', - }); + }, ''); }, 100); } @@ -216,6 +220,7 @@ window.addEventListener('message', messageEvent => { console.error(err); } } - // TODO:implement add and delete element + + // need to implement add and delete element } }); diff --git a/libs/horizon/src/horizonx/proxy/Observer.ts b/libs/horizon/src/horizonx/proxy/Observer.ts index de009283..bb5dae3d 100644 --- a/libs/horizon/src/horizonx/proxy/Observer.ts +++ b/libs/horizon/src/horizonx/proxy/Observer.ts @@ -97,10 +97,6 @@ export class Observer implements IObserver { } triggerUpdate(vNode: VNode): void { - if (!vNode) { - return; - } - // 触发VNode更新 launchUpdateFromVNode(vNode); } diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index b20a6dfb..b2ff5cdd 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -62,11 +62,12 @@ export function createProxy(rawObj: any, isHookObserver: boolean, listener: { cu // 创建Proxy let proxyObj; if (!isHookObserver) { - proxyObj = createObjectProxy(rawObj, true, { - current: change => { - listener.current(change); + proxyObj = createObjectProxy(rawObj, { + current: change => { + listener.current(change); + }, }, - }); + true); } else if (isArray(rawObj)) { // 数组 proxyObj = createArrayProxy(rawObj as [], { @@ -76,18 +77,20 @@ export function createProxy(rawObj: any, isHookObserver: boolean, listener: { cu }); } else if (isCollection(rawObj)) { // 集合 - proxyObj = createCollectionProxy(rawObj, true, { - current: change => { - listener.current(change); + proxyObj = createCollectionProxy(rawObj, { + current: change => { + listener.current(change); + }, }, - }); + true); } else { // 原生对象 或 函数 - proxyObj = createObjectProxy(rawObj, false, { - current: change => { - listener.current(change); + proxyObj = createObjectProxy(rawObj, { + current: change => { + listener.current(change); + }, }, - }); + false); } proxyMap.set(rawObj, proxyObj); diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index 669f560a..630abd53 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -35,7 +35,7 @@ function set(rawObj: any[], key: string, value: any, receiver: any) { if (!isSame(newValue, oldValue)) { // 值不一样,触发监听器 - if (observer.watchers?.[key]) { + if (observer.watchers[key]) { observer.watchers[key].forEach(cb => { cb(key, oldValue, newValue, mutation); }); diff --git a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts index e6d4309c..e5b4407e 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts @@ -21,19 +21,17 @@ import { createMapProxy } from './MapProxy'; export function createCollectionProxy( rawObj: Object, - hookObserver: boolean, - listener: { current: (...args) => any } + listener: { current: (...args) => any }, + hookObserver = true ): Object { - hookObserver = hookObserver || true; - if (isWeakSet(rawObj)) { - return createWeakSetProxy(rawObj, hookObserver, listener); + return createWeakSetProxy(rawObj, listener, hookObserver); } if (isSet(rawObj)) { - return createSetProxy(rawObj, hookObserver, listener); + return createSetProxy(rawObj, listener, hookObserver); } if (isWeakMap(rawObj)) { - return createWeakMapProxy(rawObj, hookObserver, listener); + return createWeakMapProxy(rawObj, listener, hookObserver); } - return createMapProxy(rawObj, hookObserver, listener); + return createMapProxy(rawObj, listener, hookObserver); } diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts index 0921cf7a..6fd0b03d 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts @@ -20,14 +20,17 @@ import { isPanelActive } from '../../devtools'; const COLLECTION_CHANGE = '_collectionChange'; -export function createMapProxy(rawObj: Object, hookObserver: boolean, listener: { current: (...args) => any }): Object { - hookObserver = hookObserver || true; +export function createMapProxy( + rawObj: Object, + listener: { current: (...args) => any }, + hookObserver = true +): Object { let listeners: ((mutation) => {})[] = []; let oldData: [any, any][] = []; let proxies = new Map(); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function getFun(rawObj: { get: (key: any) => any; has: (key: any) => boolean }, key: any) { + function getFun(rawObj: { get: (key: any) => any; has: (key: any) => boolean }, key: any): any { const keyProxy = rawObj.has(key) ? key : proxies.get(key); if (!keyProxy) return; const observer = getObserver(rawObj); @@ -61,7 +64,7 @@ export function createMapProxy(rawObj: Object, hookObserver: boolean, listener: }, key: any, value: any - ) { + ): any { if (rawObj.has(key) || rawObj.has(proxies.get(key))) { // VALUE CHANGE (whole value for selected key is changed) const oldValue = rawObj.get(proxies.get(key)); @@ -71,7 +74,7 @@ export function createMapProxy(rawObj: Object, hookObserver: boolean, listener: const observer = getObserver(rawObj); observer.setProp(COLLECTION_CHANGE, mutation); - if (observer.watchers?.[key]) { + if (observer.watchers[key]) { observer.watchers[key].forEach(cb => { cb(key, oldValue, value, mutation); }); diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 28aeb1f2..4f7448ab 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -29,7 +29,7 @@ function set(rawObj: object, key: string, value: any, receiver: any): boolean { const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj); if (!isSame(newValue, oldValue)) { - if (observer.watchers?.[key]) { + if (observer.watchers[key]) { observer.watchers[key].forEach(cb => { cb(key, oldValue, newValue, mutation); }); @@ -41,10 +41,9 @@ function set(rawObj: object, key: string, value: any, receiver: any): boolean { export function createObjectProxy( rawObj: T, - singleLevel: boolean, - listener: { current: (...args) => any } + listener: { current: (...args) => any }, + singleLevel = false ): ProxyHandler { - singleLevel = singleLevel || false; let listeners = [] as ((...args) => void)[]; function get(rawObj: object, key: string | symbol, receiver: any): any { diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts index e5efb07b..3eeeebd0 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts @@ -20,10 +20,9 @@ const COLLECTION_CHANGE = '_collectionChange'; export function createSetProxy( rawObj: T, - hookObserver: boolean, - listener: { current: (...args) => any } + listener: { current: (...args) => any }, + hookObserver = true ): ProxyHandler { - hookObserver = hookObserver || true; let listeners: ((mutation) => {})[] = []; let proxies = new WeakMap(); diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts index 3792dc4d..30c32fd8 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts @@ -22,10 +22,9 @@ const COLLECTION_CHANGE = '_collectionChange'; export function createWeakMapProxy( rawObj: Object, - hookObserver: boolean, - listener: { current: (...args) => any } + listener: { current: (...args) => any }, + hookObserver = true ): Object { - hookObserver = hookObserver ||true; let listeners: ((mutation) => {})[] = []; const handler = { @@ -117,7 +116,7 @@ export function createWeakMapProxy( } if (valChange) { - if (observer.watchers?.[key]) { + if (observer.watchers[key]) { observer.watchers[key].forEach(cb => { cb(key, oldValue, newValue, mutation); }); diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts index 4fd6b788..a9914be9 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts @@ -18,10 +18,9 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; export function createWeakSetProxy( rawObj: T, - hookObserver: boolean, - listener: { current: (...args) => any } + listener: { current: (...args) => any }, + hookObserver = true, ): ProxyHandler { - hookObserver = hookObserver || true; let listeners: ((mutation) => {})[] = []; let proxies = new WeakMap(); From 04181677ea2a74f91799dd8a6090f32ec5c73298 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 7 Jun 2023 15:12:51 +0800 Subject: [PATCH 68/74] Match-id-376100c85662cffb23b28ed1f31fb28473ed51a5 --- libs/horizon/src/dom/SelectionRangeHandler.ts | 4 ++-- libs/horizon/src/dom/utils/Common.ts | 4 ++-- libs/horizon/src/dom/validators/ValidateProps.ts | 5 ++--- libs/horizon/src/dom/valueHandler/InputValueHandler.ts | 9 ++++----- libs/horizon/src/dom/valueHandler/SelectValueHandler.ts | 7 +++---- .../horizon/src/dom/valueHandler/TextareaValueHandler.ts | 3 +-- libs/horizon/src/event/EventBinding.ts | 2 +- libs/horizon/src/event/FormValueController.ts | 6 +++--- libs/horizon/src/renderer/TreeBuilder.ts | 7 +++---- libs/horizon/src/renderer/hooks/UseImperativeHook.ts | 5 +---- libs/horizon/src/renderer/render/DomComponent.ts | 3 +-- libs/horizon/src/renderer/render/DomText.ts | 2 +- libs/horizon/src/renderer/vnode/VNodeCreator.ts | 3 +-- 13 files changed, 25 insertions(+), 35 deletions(-) diff --git a/libs/horizon/src/dom/SelectionRangeHandler.ts b/libs/horizon/src/dom/SelectionRangeHandler.ts index a8f643c1..f9488f9b 100644 --- a/libs/horizon/src/dom/SelectionRangeHandler.ts +++ b/libs/horizon/src/dom/SelectionRangeHandler.ts @@ -17,7 +17,7 @@ * 处理文本框、输入框中框选范围内的数据 */ -import { getIFrameFocusedDom, isNull, isText } from './utils/Common'; +import { getIFrameFocusedDom, isText } from './utils/Common'; import { isElement } from './utils/Common'; @@ -30,7 +30,7 @@ function setSelectionRange(dom: HTMLInputElement | HTMLTextAreaElement, range) { const { start, end } = range; let realEnd = end; - if (isNull(realEnd)) { + if (realEnd === null || realEnd === undefined) { realEnd = start; } diff --git a/libs/horizon/src/dom/utils/Common.ts b/libs/horizon/src/dom/utils/Common.ts index 001a32f1..dde99489 100644 --- a/libs/horizon/src/dom/utils/Common.ts +++ b/libs/horizon/src/dom/utils/Common.ts @@ -85,6 +85,6 @@ export function shouldAutoFocus(tagName: string, props: Props): boolean { return types.includes(tagName) ? Boolean(props.autoFocus) : false; } -export function isNull(val) { - return val === null || val === undefined; +export function isNotNull(object: any): boolean { + return object !== null && object !== undefined; } diff --git a/libs/horizon/src/dom/validators/ValidateProps.ts b/libs/horizon/src/dom/validators/ValidateProps.ts index 9eabcc09..ed1f0132 100644 --- a/libs/horizon/src/dom/validators/ValidateProps.ts +++ b/libs/horizon/src/dom/validators/ValidateProps.ts @@ -14,7 +14,6 @@ */ import { getPropDetails, PROPERTY_TYPE, PropDetails } from './PropertiesData'; -import { isNull } from '../utils/Common'; const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/; @@ -74,7 +73,7 @@ export function isInvalidValue( propDetails: PropDetails | null, isNativeTag: boolean ): boolean { - if (isNull(value)) { + if (value === null || value === undefined) { return true; } @@ -105,7 +104,7 @@ export function validateProps(type, props) { } // style属性必须是对象 - if (!isNull(props.style) && typeof props.style !== 'object') { + if (props.style !== null && props.style !== undefined && typeof props.style !== 'object') { throw new Error('style should be a object.'); } diff --git a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts index 749e85b3..16908947 100644 --- a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts @@ -15,7 +15,6 @@ import { updateCommonProp } from '../DOMPropertiesHandler/UpdateCommonProp'; import { Props } from '../utils/Interface'; -import { isNull } from '../utils/Common'; function getInitValue(dom: HTMLInputElement, props: Props) { const { value, defaultValue, checked, defaultChecked } = props; @@ -30,7 +29,7 @@ function getInitValue(dom: HTMLInputElement, props: Props) { export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) { // checked属于必填属性,无法置 let { checked } = props; - if (isNull(checked)) { + if (checked === undefined) { checked = getInitValue(dom, props).initChecked; } @@ -46,12 +45,12 @@ export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) { export function updateInputValue(dom: HTMLInputElement, props: Props) { const { value, checked } = props; - if (!isNull(value)) { + if (value !== undefined) { // 处理 dom.value 逻辑 if (dom.value !== String(value)) { dom.value = String(value); } - } else if (!isNull(checked)) { + } else if (checked !== undefined) { updateCommonProp(dom, 'checked', checked, true); } } @@ -61,7 +60,7 @@ export function setInitInputValue(dom: HTMLInputElement, props: Props) { const { value, defaultValue } = props; const { initValue, initChecked } = getInitValue(dom, props); - if (!isNull(value) || !isNull(defaultValue)) { + if (value !== undefined || defaultValue !== undefined) { // value 的使用优先级 value 属性 > defaultValue 属性 > 空字符串 const initValueStr = String(initValue); diff --git a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts index 86297315..9ba9273f 100644 --- a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts @@ -14,7 +14,6 @@ */ import { HorizonSelect, Props } from '../utils/Interface'; -import { isNull } from '../utils/Common'; function updateMultipleValue(options, newValues) { const newValueSet = new Set(); @@ -70,18 +69,18 @@ export function updateSelectValue(dom: HorizonSelect, props: Props, isInit = fal dom._multiple = newMultiple; // 设置了 value 属性 - if (!isNull(value)) { + if (value !== null && value !== undefined) { updateValue(dom.options, value, newMultiple); } else if (oldMultiple !== newMultiple) { // 修改了 multiple 属性 // 切换 multiple 之后,如果设置了 defaultValue 需要重新应用 - if (!isNull(defaultValue)) { + if (defaultValue !== null && defaultValue !== undefined) { updateValue(dom.options, defaultValue, newMultiple); } else { // 恢复到未选定状态 updateValue(dom.options, newMultiple ? [] : '', newMultiple); } - } else if (isInit && !isNull(defaultValue)) { + } else if (isInit && defaultValue !== null && defaultValue !== undefined) { // 设置了 defaultValue 属性 updateValue(dom.options, defaultValue, newMultiple); } diff --git a/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts b/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts index e852cd1f..cf91b9c0 100644 --- a/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts @@ -14,13 +14,12 @@ */ import { Props } from '../utils/Interface'; -import { isNull } from '../utils/Common'; // 值的优先级 value > children > defaultValue function getInitValue(props: Props) { const { value } = props; - if (isNull(value)) { + if (value === undefined) { const { defaultValue, children } = props; let initValue = defaultValue; diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index 56e1567c..baf965dd 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -73,7 +73,7 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { const nativeFullName = isCapture ? nativeEvent + 'capture' : nativeEvent; // 事件存储在DOM节点属性,避免多个VNode(root和portal)对应同一个DOM, 造成事件重复监听 - currentRoot.realNode.$EV = currentRoot.realNode.$EV || {}; + currentRoot.realNode.$EV = currentRoot.realNode.$EV ?? {}; const events = currentRoot.realNode.$EV; if (!events[nativeFullName]) { diff --git a/libs/horizon/src/event/FormValueController.ts b/libs/horizon/src/event/FormValueController.ts index 2e296e6d..1b1649d0 100644 --- a/libs/horizon/src/event/FormValueController.ts +++ b/libs/horizon/src/event/FormValueController.ts @@ -14,7 +14,7 @@ */ import { getVNodeProps } from '../dom/DOMInternalKeys'; -import { getDomTag, isNull } from '../dom/utils/Common'; +import { getDomTag, isNotNull } from '../dom/utils/Common'; import { Props } from '../dom/utils/Interface'; import { updateTextareaValue } from '../dom/valueHandler/TextareaValueHandler'; import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler'; @@ -41,14 +41,14 @@ function controlInputValue(inputDom: HTMLInputElement, props: Props) { const { name, type } = props; // 如果是 radio,找出同一form内,name相同的Radio,更新它们Handler的Value - if (type === 'radio' && !isNull(name)) { + if (type === 'radio' && isNotNull(name)) { const radioList = document.querySelectorAll(`input[type="radio"][name="${name}"]`); for (let i = 0; i < radioList.length; i++) { const radio = radioList[i]; if (radio === inputDom) { continue; } - if (!isNull(radio.form) && !isNull(inputDom.form) && radio.form !== inputDom.form) { + if (isNotNull(radio.form) && isNotNull(inputDom.form) && radio.form !== inputDom.form) { continue; } diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index cc99e968..ad27609c 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -54,7 +54,6 @@ import { import { getPathArr } from './utils/vNodePath'; import { injectUpdater } from '../external/devtools'; import { popCurrentRoot, pushCurrentRoot } from './RootStack'; -import { isNull } from '../dom/utils/Common'; // 不可恢复错误 let unrecoverableErrorDuringBuild: any = null; @@ -140,7 +139,7 @@ function bubbleVNode(vNode: VNode): void { node = parent; // 更新processing,抛出异常时可以使用 processing = node; - } while (!isNull(node)); + } while (node); // 修改结果 if (getBuildResult() === BuildInComplete) { @@ -181,7 +180,7 @@ function getChildByIndex(vNode: VNode, idx: number) { let node = vNode.child; for (let i = 0; i < idx; i++) { // 场景:当组件被销毁,业务若异步(定时器)调用setState修改状态,可能出现路径错误,此处进行保护。 - if (isNull(node)) { + if (node === null || node === undefined) { return null; } @@ -226,7 +225,7 @@ export function calcStartUpdateVNode(treeRoot: VNode) { const pathIndex = Number(startNodePath[i]); node = getChildByIndex(node, pathIndex)!; // 路径错误时,回退到从根更新 - if (isNull(node)) { + if (node === null) { return treeRoot; } } diff --git a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts index 94d69743..9a39a69b 100644 --- a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts +++ b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts @@ -17,10 +17,7 @@ import { useLayoutEffectImpl } from './UseEffectHook'; import { getHookStage } from './HookStage'; import { throwNotInFuncError } from './BaseHook'; import type { Ref } from './HookType'; - -function isNotNull(object: any): boolean { - return object !== null && object !== undefined; -} +import { isNotNull } from '../../dom/utils/Common'; function effectFunc(func: () => R, ref: Ref | ((any) => any) | null): (() => void) | null { if (typeof ref === 'function') { diff --git a/libs/horizon/src/renderer/render/DomComponent.ts b/libs/horizon/src/renderer/render/DomComponent.ts index 247986ad..77273d96 100644 --- a/libs/horizon/src/renderer/render/DomComponent.ts +++ b/libs/horizon/src/renderer/render/DomComponent.ts @@ -23,7 +23,6 @@ import { markRef } from './BaseComponent'; import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags'; import { travelVNodeTree } from '../vnode/VNodeUtils'; import { createChildrenByDiff } from '../diff/nodeDiffComparator'; -import { isNull } from '../../dom/utils/Common'; function updateDom(processing: VNode, type: any, newProps: Props) { // 如果oldProps !== newProps,意味着存在更新,并且需要处理其相关的副作用 @@ -55,7 +54,7 @@ export function bubbleRender(processing: VNode) { const type = processing.type; const newProps = processing.props; - if (!processing.isCreated && !isNull(processing.realNode)) { + if (!processing.isCreated && processing.realNode !== null) { // 更新dom属性 updateDom(processing, type, newProps); diff --git a/libs/horizon/src/renderer/render/DomText.ts b/libs/horizon/src/renderer/render/DomText.ts index 2f8cec01..663f15f6 100644 --- a/libs/horizon/src/renderer/render/DomText.ts +++ b/libs/horizon/src/renderer/render/DomText.ts @@ -27,7 +27,7 @@ export function captureRender(): VNode | null { export function bubbleRender(processing: VNode) { const newText = processing.props; - if (!processing.isCreated && !isNull(processing.realNode)) { + if (!processing.isCreated && processing.realNode !== null) { // 更新 const oldText = processing.oldProps; // 如果文本不同,将其标记为更新 diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 27e08e99..65b777c8 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -44,7 +44,6 @@ import { import { VNode } from './VNode'; import { JSXElement, Source } from '../Types'; import { markVNodePath } from '../utils/vNodePath'; -import { isNull } from '../../dom/utils/Common'; const typeLazyMap = { [TYPE_FORWARD_REF]: ForwardRef, @@ -137,7 +136,7 @@ export function createUndeterminedVNode(type, key, props, source: Source | null) vNodeTag = typeMap[type.vtype]; isLazy = type.vtype === TYPE_LAZY; } else { - throw Error(`Component type is invalid, got: ${isNull(type) ? type : componentType}`); + throw Error(`Component type is invalid, got: ${type === null || type === undefined ? type : componentType}`); } const vNode = newVirtualNode(vNodeTag, key, props); From 914425a54511569271d45b148ad0ace460261ba7 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 7 Jun 2023 16:33:34 +0800 Subject: [PATCH 69/74] Match-id-930581f027f9f3c888ded7e7eac3fb5f165bcb4e --- .../src/horizonx/proxy/ProxyHandler.ts | 4 +- .../proxy/handlers/ArrayProxyHandler.ts | 8 +- .../src/horizonx/proxy/handlers/MapProxy.ts | 198 ++++++++++-------- .../proxy/handlers/ObjectProxyHandler.ts | 8 +- .../src/horizonx/proxy/handlers/SetProxy.ts | 44 ++-- .../horizonx/proxy/handlers/WeakMapProxy.ts | 26 +-- .../horizonx/proxy/handlers/WeakSetProxy.ts | 24 ++- .../src/horizonx/store/StoreHandler.ts | 2 +- 8 files changed, 169 insertions(+), 145 deletions(-) diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index b2ff5cdd..d37cca1a 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -31,9 +31,7 @@ export function getObserver(rawObj: any): Observer { return rawObj[OBSERVER_KEY]; } -export function createProxy(rawObj: any, isHookObserver: boolean, listener: { current: (...args) => any }): any { - isHookObserver = isHookObserver || true; - +export function createProxy(rawObj: any, listener: { current: (...args) => any }, isHookObserver = true): any { // 不是对象(是原始数据类型)不用代理 if (!(rawObj && isObject(rawObj))) { return rawObj; diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index 630abd53..ea94e4a0 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -35,7 +35,7 @@ function set(rawObj: any[], key: string, value: any, receiver: any) { if (!isSame(newValue, oldValue)) { // 值不一样,触发监听器 - if (observer.watchers[key]) { + if (observer.watchers?.[key]) { observer.watchers[key].forEach(cb => { cb(key, oldValue, newValue, mutation); }); @@ -97,7 +97,7 @@ export function createArrayProxy(rawObj: any[], listener: { current: (...args) = // 对于value也需要进一步代理 const valProxy = singleLevel ? value - : createProxy(value, hookObserverMap.get(rawObj), { + : createProxy(value, { current: change => { if (!change.parents) change.parents = []; change.parents.push(rawObj); @@ -108,7 +108,9 @@ export function createArrayProxy(rawObj: any[], listener: { current: (...args) = listener.current(mutation); listeners.forEach(lst => lst(mutation)); }, - }); + }, + hookObserverMap.get(rawObj) + ); return valProxy; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts index 6fd0b03d..03accb28 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts @@ -38,18 +38,20 @@ export function createMapProxy( const value = rawObj.get(keyProxy); // 对于value也需要进一步代理 - const valProxy = createProxy(value, hookObserverMap.get(rawObj), { - current: change => { - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, [key]: change.mutation.from }, - { ...rawObj, [key]: change.mutation.to } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + const valProxy = createProxy(value, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [key]: change.mutation.from }, + { ...rawObj, [key]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }); + hookObserverMap.get(rawObj) + ); return valProxy; } @@ -84,19 +86,21 @@ export function createMapProxy( oldData = [...Array.from(rawObj.entries())]; } else { // NEW VALUE - const keyProxy = createProxy(key, hookObserverMap.get(rawObj), { - current: change => { - // KEY CHANGE - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, ['_keyChange']: change.mutation.from }, - { ...rawObj, ['_keyChange']: change.mutation.to } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + const keyProxy = createProxy(key, { + current: change => { + // KEY CHANGE + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, ['_keyChange']: change.mutation.from }, + { ...rawObj, ['_keyChange']: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }); + hookObserverMap.get(rawObj) + ); proxies.set(key, keyProxy); rawObj.set(keyProxy, value); @@ -180,32 +184,36 @@ export function createMapProxy( const observer = getObserver(rawObj); observer.useProp(COLLECTION_CHANGE); rawObj.forEach((value, key) => { - const keyProxy = createProxy(value, hookObserverMap.get(rawObj), { - current: change => { - //KEY ATTRIBUTES CHANGED - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, ['_keyChange']: change.mutation.from }, - { ...rawObj, ['_keyChange']: change.mutation.to } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + const keyProxy = createProxy(value, { + current: change => { + //KEY ATTRIBUTES CHANGED + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, ['_keyChange']: change.mutation.from }, + { ...rawObj, ['_keyChange']: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }); - const valProxy = createProxy(key, hookObserverMap.get(rawObj), { - current: change => { - // VALUE ATTRIBUTE CHANGED - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, key: change.mutation.from }, - { ...rawObj, key: change.mutation.to } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + hookObserverMap.get(rawObj) + ); + const valProxy = createProxy(key, { + current: change => { + // VALUE ATTRIBUTE CHANGED + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, key: change.mutation.from }, + { ...rawObj, key: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }); + hookObserverMap.get(rawObj) + ); // 最后一个参数要返回代理对象 return callback(keyProxy, valProxy, rawObj); }); @@ -221,18 +229,20 @@ export function createMapProxy( const { value, done } = rawIt.next(); if (done) { return { - value: createProxy(value, hookObserver, { - current: change => { - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, [value]: change.mutation.from }, - { ...rawObj, [value]: change.mutation.to } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + value: createProxy(value, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [value]: change.mutation.from }, + { ...rawObj, [value]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }), + hookObserver + ), done, }; } @@ -242,45 +252,51 @@ export function createMapProxy( if (type === 'entries') { //ENTRY CHANGED newVal = [ - createProxy(value[0], hookObserver, { - current: change => { - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, ['itemChange']: { key: change.mutation.from, value: value[1] } }, - { ...rawObj, ['itemChange']: { key: change.mutation.to, value: value[1] } } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + createProxy(value[0], { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, ['itemChange']: { key: change.mutation.from, value: value[1] } }, + { ...rawObj, ['itemChange']: { key: change.mutation.to, value: value[1] } } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }), - createProxy(value[1], hookObserver, { - current: change => { - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, item: { key: value[0], value: change.mutation.from } }, - { ...rawObj, item: { key: value[0], value: change.mutation.to } } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + hookObserver + ), + createProxy(value[1], { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, item: { key: value[0], value: change.mutation.from } }, + { ...rawObj, item: { key: value[0], value: change.mutation.to } } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }), + hookObserver + ), ]; } else { // SINGLE VALUE CHANGED - newVal = createProxy(value, hookObserver, { - current: change => { - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, [type === 'keys' ? 'key' : 'value']: change.mutation.from }, - { ...rawObj, [type === 'keys' ? 'key' : 'value']: change.mutation.to } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + newVal = createProxy(value, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [type === 'keys' ? 'key' : 'value']: change.mutation.from }, + { ...rawObj, [type === 'keys' ? 'key' : 'value']: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }); + hookObserver + ); } return { value: newVal, done }; diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 4f7448ab..1a216477 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -29,7 +29,7 @@ function set(rawObj: object, key: string, value: any, receiver: any): boolean { const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj); if (!isSame(newValue, oldValue)) { - if (observer.watchers[key]) { + if (observer.watchers?.[key]) { observer.watchers[key].forEach(cb => { cb(key, oldValue, newValue, mutation); }); @@ -87,7 +87,7 @@ export function createObjectProxy( // 对于value也需要进一步代理 const valProxy = singleLevel ? value - : createProxy(value, hookObserverMap.get(rawObj), { + : createProxy(value, { current: change => { if (!change.parents) change.parents = []; change.parents.push(rawObj); @@ -98,7 +98,9 @@ export function createObjectProxy( listener.current({ ...change, mutation }); listeners.forEach(lst => lst({ ...change, mutation })); }, - }); + }, + hookObserverMap.get(rawObj) + ); return valProxy; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts index 3eeeebd0..e5b19e4f 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts @@ -29,26 +29,28 @@ export function createSetProxy( // Set的add方法 function add(rawObj: { add: (any) => void; has: (any) => boolean; values: () => any[] }, value: any): Object { if (!rawObj.has(proxies.get(value))) { - const proxy = createProxy(value, hookObserverMap.get(rawObj), { - current: change => { - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, valueChange: change.mutation.from }, - { ...rawObj, valueChange: change.mutation.to } - ); - listener.current({ - ...change, - mutation, - }); - listeners.forEach(lst => - lst({ + const proxy = createProxy(value, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, valueChange: change.mutation.from }, + { ...rawObj, valueChange: change.mutation.to } + ); + listener.current({ ...change, mutation, - }) - ); + }); + listeners.forEach(lst => + lst({ + ...change, + mutation, + }) + ); + }, }, - }); + hookObserverMap.get(rawObj) + ); const oldValues = Array.from(rawObj.values()); proxies.set(value, proxy); @@ -210,13 +212,13 @@ export function createSetProxy( }; const { value, done } = rawIt.next(); if (done) { - return { value: createProxy(value, hookObserver, currentListener), done }; + return { value: createProxy(value, currentListener, hookObserver), done }; } observer.useProp(COLLECTION_CHANGE); let newVal; - newVal = createProxy(value, hookObserver, currentListener); + newVal = createProxy(value, currentListener, hookObserver); return { value: newVal, done }; }, @@ -274,8 +276,8 @@ export function createSetProxy( ); }, }; - const valProxy = createProxy(value, hookObserverMap.get(rawObj), currentListener); - const keyProxy = createProxy(key, hookObserverMap.get(rawObj), currentListener); + const valProxy = createProxy(value, currentListener, hookObserverMap.get(rawObj)); + const keyProxy = createProxy(key, currentListener, hookObserverMap.get(rawObj)); // 最后一个参数要返回代理对象 return callback(valProxy, keyProxy, rawObj); }); diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts index 30c32fd8..70124cb6 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts @@ -42,18 +42,20 @@ export function createWeakMapProxy( const value = rawObj.get(key); // 对于value也需要进一步代理 - const valProxy = createProxy(value, hookObserverMap.get(rawObj), { - current: change => { - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, [key]: change.mutation.from }, - { ...rawObj, [key]: change.mutation.to } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + const valProxy = createProxy(value, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [key]: change.mutation.from }, + { ...rawObj, [key]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }); + hookObserverMap.get(rawObj) + ); return valProxy; } @@ -116,7 +118,7 @@ export function createWeakMapProxy( } if (valChange) { - if (observer.watchers[key]) { + if (observer.watchers?.[key]) { observer.watchers[key].forEach(cb => { cb(key, oldValue, newValue, mutation); }); diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts index a9914be9..baaa6a2b 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts @@ -66,18 +66,20 @@ export function createWeakSetProxy( // Set的add方法 function add(rawObj: { add: (any) => void; has: (any) => boolean }, value: any): Object { if (!rawObj.has(proxies.get(value))) { - const proxy = createProxy(value, hookObserverMap.get(rawObj), { - current: change => { - if (!change.parents) change.parents = []; - change.parents.push(rawObj); - let mutation = resolveMutation( - { ...rawObj, [value]: change.mutation.from }, - { ...rawObj, [value]: change.mutation.to } - ); - listener.current({ ...change, mutation }); - listeners.forEach(lst => lst({ ...change, mutation })); + const proxy = createProxy(value, { + current: change => { + if (!change.parents) change.parents = []; + change.parents.push(rawObj); + let mutation = resolveMutation( + { ...rawObj, [value]: change.mutation.from }, + { ...rawObj, [value]: change.mutation.to } + ); + listener.current({ ...change, mutation }); + listeners.forEach(lst => lst({ ...change, mutation })); + }, }, - }); + hookObserverMap.get(rawObj) + ); proxies.set(value, proxy); diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index 2afd33f2..469ff88b 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -161,7 +161,7 @@ export function createStore, C extend current: listener => {}, }; - const proxyObj = createProxy(config.state, !config.options?.isReduxAdapter, listener); + const proxyObj = createProxy(config.state, listener, !config.options?.isReduxAdapter); proxyObj.$pending = false; From 75dabdb169b5a3b6ad3e7992c6ab758bb5ad63dd Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 7 Jun 2023 17:25:20 +0800 Subject: [PATCH 70/74] Match-id-17719191991ce335a66dd99e1b57c5e119685a4b --- libs/horizon/index.ts | 2 ++ libs/horizon/src/horizonx/Constants.ts | 2 +- libs/horizon/src/horizonx/proxy/ProxyHandler.ts | 14 +++++++++++++- .../horizonx/proxy/handlers/ArrayProxyHandler.ts | 4 ++++ .../src/horizonx/proxy/handlers/MapProxy.ts | 4 ++++ .../horizonx/proxy/handlers/ObjectProxyHandler.ts | 4 ++++ .../src/horizonx/proxy/handlers/SetProxy.ts | 5 +++++ .../src/horizonx/proxy/handlers/WeakMapProxy.ts | 4 ++++ .../src/horizonx/proxy/handlers/WeakSetProxy.ts | 5 +++++ 9 files changed, 42 insertions(+), 2 deletions(-) diff --git a/libs/horizon/index.ts b/libs/horizon/index.ts index efe6df6b..90c536d5 100644 --- a/libs/horizon/index.ts +++ b/libs/horizon/index.ts @@ -69,6 +69,7 @@ import { } from './src/dom/DOMExternal'; import { syncUpdates as flushSync } from './src/renderer/TreeBuilder'; +import { toRaw } from './src/horizonx/proxy/ProxyHandler'; const Horizon = { Children, @@ -157,6 +158,7 @@ export { clearStore, reduxAdapter, watch, + toRaw, // 兼容ReactIs isFragment, isElement, diff --git a/libs/horizon/src/horizonx/Constants.ts b/libs/horizon/src/horizonx/Constants.ts index 6d65ab52..205e008e 100644 --- a/libs/horizon/src/horizonx/Constants.ts +++ b/libs/horizon/src/horizonx/Constants.ts @@ -13,4 +13,4 @@ * See the Mulan PSL v2 for more details. */ -export const OBSERVER_KEY = '_horizonObserver'; +export const OBSERVER_KEY = typeof Symbol === 'function' ? Symbol('_horizonObserver') : '_horizonObserver'; diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index d37cca1a..d27c77eb 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -52,7 +52,15 @@ export function createProxy(rawObj: any, listener: { current: (...args) => any } let observer: IObserver = getObserver(rawObj); if (!observer) { observer = isHookObserver ? new Observer() : new HooklessObserver(); - rawObj[OBSERVER_KEY] = observer; + if (typeof OBSERVER_KEY === 'string') { + Object.defineProperty(rawObj, OBSERVER_KEY, { + configurable: false, + enumerable: false, + value: observer, + }); + } else { + rawObj[OBSERVER_KEY] = observer; + } } hookObserverMap.set(rawObj, isHookObserver); @@ -96,3 +104,7 @@ export function createProxy(rawObj: any, listener: { current: (...args) => any } return proxyObj; } + +export function toRaw(observed: T): T { + return observed && (observed)['_rawValue']; +} diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index ea94e4a0..7e0c1810 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -137,6 +137,10 @@ export function createArrayProxy(rawObj: any[], listener: { current: (...args) = return objectGet(rawObj, key, receiver); } + if (key === '_rawValue') { + return rawObj; + } + return Reflect.get(rawObj, key, receiver); } diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts index 03accb28..eee505b7 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts @@ -388,6 +388,10 @@ export function createMapProxy( }; } + if (key === '_rawValue') { + return rawObj; + } + return Reflect.get(rawObj, key, receiver); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 1a216477..13147980 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -78,6 +78,10 @@ export function createObjectProxy( }; } + if (key === '_rawValue') { + return rawObj; + } + observer.useProp(key); const value = Reflect.get(rawObj, key, receiver); diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts index e5b19e4f..4295da46 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts @@ -179,6 +179,11 @@ export function createSetProxy( }; }; } + + if (key === '_rawValue') { + return rawObj; + } + return Reflect.get(rawObj, key, receiver); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts index 70124cb6..97b462b7 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts @@ -96,6 +96,10 @@ export function createWeakMapProxy( }; } + if (key === '_rawValue') { + return rawObj; + } + return Reflect.get(rawObj, key, receiver); } diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts index baaa6a2b..8d768a65 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts @@ -60,6 +60,11 @@ export function createWeakSetProxy( }; }; } + + if (key === '_rawValue') { + return rawObj; + } + return Reflect.get(rawObj, key, receiver); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// From e867a248aac01c4719cf33ee404dee523ef0b119 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 7 Jun 2023 17:51:35 +0800 Subject: [PATCH 71/74] Match-id-6d5ca3fa4860f0cee0c43f85a5510f326302938b --- libs/horizon/src/horizonx/Constants.ts | 2 ++ .../src/horizonx/proxy/ProxyHandler.ts | 26 +++++++++++-------- .../proxy/handlers/ArrayProxyHandler.ts | 4 +-- .../src/horizonx/proxy/handlers/MapProxy.ts | 3 ++- .../proxy/handlers/ObjectProxyHandler.ts | 4 +-- .../src/horizonx/proxy/handlers/SetProxy.ts | 3 ++- .../horizonx/proxy/handlers/WeakMapProxy.ts | 3 ++- .../horizonx/proxy/handlers/WeakSetProxy.ts | 3 ++- 8 files changed, 29 insertions(+), 19 deletions(-) diff --git a/libs/horizon/src/horizonx/Constants.ts b/libs/horizon/src/horizonx/Constants.ts index 205e008e..ded649bd 100644 --- a/libs/horizon/src/horizonx/Constants.ts +++ b/libs/horizon/src/horizonx/Constants.ts @@ -14,3 +14,5 @@ */ export const OBSERVER_KEY = typeof Symbol === 'function' ? Symbol('_horizonObserver') : '_horizonObserver'; + +export const RAW_VALUE = '_rawValue'; diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index d27c77eb..6d4027af 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -20,7 +20,7 @@ import { isArray, isCollection, isObject } from '../CommonUtils'; import { createArrayProxy } from './handlers/ArrayProxyHandler'; import { createCollectionProxy } from './handlers/CollectionProxyHandler'; import type { IObserver } from '../types'; -import { OBSERVER_KEY } from '../Constants'; +import { OBSERVER_KEY, RAW_VALUE } from '../Constants'; // 保存rawObj -> Proxy const proxyMap = new WeakMap(); @@ -31,6 +31,18 @@ export function getObserver(rawObj: any): Observer { return rawObj[OBSERVER_KEY]; } +const setObserverKey = typeof OBSERVER_KEY === 'string' + ? (rawObj, observer) => { + Object.defineProperty(rawObj, OBSERVER_KEY, { + configurable: false, + enumerable: false, + value: observer, + }); + } + : (rawObj, observer) => { + rawObj[OBSERVER_KEY] = observer; + }; + export function createProxy(rawObj: any, listener: { current: (...args) => any }, isHookObserver = true): any { // 不是对象(是原始数据类型)不用代理 if (!(rawObj && isObject(rawObj))) { @@ -52,15 +64,7 @@ export function createProxy(rawObj: any, listener: { current: (...args) => any } let observer: IObserver = getObserver(rawObj); if (!observer) { observer = isHookObserver ? new Observer() : new HooklessObserver(); - if (typeof OBSERVER_KEY === 'string') { - Object.defineProperty(rawObj, OBSERVER_KEY, { - configurable: false, - enumerable: false, - value: observer, - }); - } else { - rawObj[OBSERVER_KEY] = observer; - } + setObserverKey(rawObj, observer); } hookObserverMap.set(rawObj, isHookObserver); @@ -106,5 +110,5 @@ export function createProxy(rawObj: any, listener: { current: (...args) => any } } export function toRaw(observed: T): T { - return observed && (observed)['_rawValue']; + return observed && (observed)[RAW_VALUE]; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index 7e0c1810..c5aa46e3 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -17,7 +17,7 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { isSame, isValidIntegerKey } from '../../CommonUtils'; import { resolveMutation } from '../../CommonUtils'; import { isPanelActive } from '../../devtools'; -import { OBSERVER_KEY } from '../../Constants'; +import { OBSERVER_KEY, RAW_VALUE } from '../../Constants'; function set(rawObj: any[], key: string, value: any, receiver: any) { const oldValue = rawObj[key]; @@ -137,7 +137,7 @@ export function createArrayProxy(rawObj: any[], listener: { current: (...args) = return objectGet(rawObj, key, receiver); } - if (key === '_rawValue') { + if (key === RAW_VALUE) { return rawObj; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts index eee505b7..62ab7d74 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts @@ -17,6 +17,7 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { isSame } from '../../CommonUtils'; import { resolveMutation } from '../../CommonUtils'; import { isPanelActive } from '../../devtools'; +import { RAW_VALUE } from "../../Constants"; const COLLECTION_CHANGE = '_collectionChange'; @@ -388,7 +389,7 @@ export function createMapProxy( }; } - if (key === '_rawValue') { + if (key === RAW_VALUE) { return rawObj; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index 13147980..0244bbdf 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -15,7 +15,7 @@ import { isSame, resolveMutation } from '../../CommonUtils'; import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; -import { OBSERVER_KEY } from '../../Constants'; +import { OBSERVER_KEY, RAW_VALUE } from '../../Constants'; import { isPanelActive } from '../../devtools'; function set(rawObj: object, key: string, value: any, receiver: any): boolean { @@ -78,7 +78,7 @@ export function createObjectProxy( }; } - if (key === '_rawValue') { + if (key === RAW_VALUE) { return rawObj; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts index 4295da46..1270b9c6 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts @@ -15,6 +15,7 @@ import { resolveMutation } from '../../CommonUtils'; import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; +import { RAW_VALUE } from "../../Constants"; const COLLECTION_CHANGE = '_collectionChange'; @@ -180,7 +181,7 @@ export function createSetProxy( }; } - if (key === '_rawValue') { + if (key === RAW_VALUE) { return rawObj; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts index 97b462b7..14d13178 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts @@ -17,6 +17,7 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { isSame } from '../../CommonUtils'; import { resolveMutation } from '../../CommonUtils'; import { isPanelActive } from '../../devtools'; +import { RAW_VALUE } from "../../Constants"; const COLLECTION_CHANGE = '_collectionChange'; @@ -96,7 +97,7 @@ export function createWeakMapProxy( }; } - if (key === '_rawValue') { + if (key === RAW_VALUE) { return rawObj; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts index 8d768a65..3131225e 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts @@ -15,6 +15,7 @@ import { resolveMutation } from '../../CommonUtils'; import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; +import { RAW_VALUE } from "../../Constants"; export function createWeakSetProxy( rawObj: T, @@ -61,7 +62,7 @@ export function createWeakSetProxy( }; } - if (key === '_rawValue') { + if (key === RAW_VALUE) { return rawObj; } From ecbece26375b8b36c77e788af59a07b73676a08f Mon Sep 17 00:00:00 2001 From: * <*> Date: Thu, 8 Jun 2023 09:56:31 +0800 Subject: [PATCH 72/74] Match-id-c767a5e1e4d1e06b93c3cd67a1735b75c17a2983 --- libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts | 2 +- libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts | 2 +- libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts | 2 +- libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts index 62ab7d74..8529f35c 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts @@ -17,7 +17,7 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { isSame } from '../../CommonUtils'; import { resolveMutation } from '../../CommonUtils'; import { isPanelActive } from '../../devtools'; -import { RAW_VALUE } from "../../Constants"; +import { RAW_VALUE } from '../../Constants'; const COLLECTION_CHANGE = '_collectionChange'; diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts index 1270b9c6..933ad73c 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts @@ -15,7 +15,7 @@ import { resolveMutation } from '../../CommonUtils'; import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; -import { RAW_VALUE } from "../../Constants"; +import { RAW_VALUE } from '../../Constants'; const COLLECTION_CHANGE = '_collectionChange'; diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts index 14d13178..0c818b3c 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts @@ -17,7 +17,7 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { isSame } from '../../CommonUtils'; import { resolveMutation } from '../../CommonUtils'; import { isPanelActive } from '../../devtools'; -import { RAW_VALUE } from "../../Constants"; +import { RAW_VALUE } from '../../Constants'; const COLLECTION_CHANGE = '_collectionChange'; diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts index 3131225e..e1a843cd 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts @@ -15,7 +15,7 @@ import { resolveMutation } from '../../CommonUtils'; import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; -import { RAW_VALUE } from "../../Constants"; +import { RAW_VALUE } from '../../Constants'; export function createWeakSetProxy( rawObj: T, From 83d0f2081b9cd1af5bea21c40b99f5ee09376351 Mon Sep 17 00:00:00 2001 From: * <*> Date: Mon, 10 Jul 2023 17:20:59 +0800 Subject: [PATCH 73/74] Match-id-196a52c4e94e9e29af3ba17f9477417aa762cd98 --- libs/horizon/src/renderer/hooks/BaseHook.ts | 2 +- libs/horizon/src/renderer/hooks/HookExternal.ts | 13 ++++++++----- libs/horizon/src/renderer/hooks/HookType.ts | 2 +- libs/horizon/src/renderer/hooks/UseRefHook.ts | 2 +- libs/horizon/src/renderer/hooks/UseStateHook.ts | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/libs/horizon/src/renderer/hooks/BaseHook.ts b/libs/horizon/src/renderer/hooks/BaseHook.ts index 0109bf62..1cac614f 100644 --- a/libs/horizon/src/renderer/hooks/BaseHook.ts +++ b/libs/horizon/src/renderer/hooks/BaseHook.ts @@ -34,7 +34,7 @@ export function setCurrentHook(hook: Hook | null) { currentHook = hook; } -export function throwNotInFuncError() { +export function throwNotInFuncError(): never { throw Error('Hooks should be used inside function component.'); } diff --git a/libs/horizon/src/renderer/hooks/HookExternal.ts b/libs/horizon/src/renderer/hooks/HookExternal.ts index 60adc0bd..ddc9c8a2 100644 --- a/libs/horizon/src/renderer/hooks/HookExternal.ts +++ b/libs/horizon/src/renderer/hooks/HookExternal.ts @@ -27,22 +27,25 @@ import { getProcessingVNode } from '../GlobalVar'; import { Ref, Trigger } from './HookType'; type BasicStateAction = ((S) => S) | S; -type Dispatch = (A) => void; +type Dispatch = (value: A) => void; export function useContext(Context: ContextType): T { const processingVNode = getProcessingVNode(); return getNewContext(processingVNode!, Context, true); } - -export function useState(initialState: (() => S) | S): [S, Dispatch>] { +export function useState(): [S | undefined, Dispatch>] +export function useState(initialState: (() => S) | S): [S, Dispatch>] +export function useState(initialState?: (() => S) | S): [S, Dispatch>] { return useStateImpl(initialState); } -export function useReducer(reducer: (S, A) => S, initialArg: I, init?: (I) => S): [S, Trigger] | void { +export function useReducer(reducer: (S, A) => S, initialArg: I, init?: (I) => S): [S, Trigger] { return useReducerImpl(reducer, initialArg, init); } -export function useRef(initialValue: T): Ref { +export function useRef(): Ref +export function useRef(initialValue: T): Ref +export function useRef(initialValue?: T): Ref { return useRefImpl(initialValue); } diff --git a/libs/horizon/src/renderer/hooks/HookType.ts b/libs/horizon/src/renderer/hooks/HookType.ts index fddf5223..a682b9a4 100644 --- a/libs/horizon/src/renderer/hooks/HookType.ts +++ b/libs/horizon/src/renderer/hooks/HookType.ts @@ -57,4 +57,4 @@ export type Ref = { current: V; }; -export type Trigger = (A) => void; +export type Trigger = (state: A) => void; diff --git a/libs/horizon/src/renderer/hooks/UseRefHook.ts b/libs/horizon/src/renderer/hooks/UseRefHook.ts index a9e597fb..b4becc2b 100644 --- a/libs/horizon/src/renderer/hooks/UseRefHook.ts +++ b/libs/horizon/src/renderer/hooks/UseRefHook.ts @@ -17,7 +17,7 @@ import { createHook, getCurrentHook, throwNotInFuncError } from './BaseHook'; import { getHookStage, HookStage } from './HookStage'; import type { Ref } from './HookType'; -export function useRefImpl(value: V): Ref { +export function useRefImpl(value?: V): Ref { const stage = getHookStage(); if (stage === null) { throwNotInFuncError(); diff --git a/libs/horizon/src/renderer/hooks/UseStateHook.ts b/libs/horizon/src/renderer/hooks/UseStateHook.ts index e7d10eb0..78135114 100644 --- a/libs/horizon/src/renderer/hooks/UseStateHook.ts +++ b/libs/horizon/src/renderer/hooks/UseStateHook.ts @@ -21,6 +21,6 @@ function defaultReducer(state: S, action: ((S) => S) | S): S { return typeof action === 'function' ? action(state) : action; } -export function useStateImpl(initArg: (() => S) | S): [S, Trigger<((S) => S) | S>] { +export function useStateImpl(initArg?: (() => S) | S): [S, Trigger<((S) => S) | S>] { return useReducerImpl(defaultReducer, initArg, undefined, true); } From 37e36700f9c7186f1cabc0f0f0c9569b9673f87c Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 26 Jul 2023 14:50:55 +0800 Subject: [PATCH 74/74] Match-id-576054d072d98d7dc612751b57b20a3f6e9ae57c --- app_define.json | 2 +- babel.config.js | 4 +- jest.config.js | 4 +- libs/horizon/README.md | 1 - .../src/horizonx/devtools/constants.ts | 10 -- libs/inula/README.md | 1 + libs/{horizon => inula}/global.d.ts | 2 +- libs/{horizon => inula}/index.ts | 16 +-- libs/{horizon => inula}/jsx-dev-runtime.ts | 0 libs/{horizon => inula}/jsx-runtime.ts | 0 libs/{horizon => inula}/npm/index.js | 4 +- libs/{horizon => inula}/package.json | 6 +- .../{horizon => inula}/src/dom/DOMExternal.ts | 0 .../src/dom/DOMInternalKeys.ts | 6 +- .../{horizon => inula}/src/dom/DOMOperator.ts | 2 +- .../DOMPropertiesHandler.ts | 8 +- .../dom/DOMPropertiesHandler/StyleHandler.ts | 0 .../DOMPropertiesHandler/UpdateCommonProp.ts | 0 .../src/dom/SelectionRangeHandler.ts | 0 .../src/dom/utils/Common.ts | 4 +- .../src/dom/utils/DomCreator.ts | 0 .../src/dom/utils/Interface.ts | 4 +- .../src/dom/validators/PropertiesData.ts | 0 .../src/dom/validators/ValidateProps.ts | 0 .../src/dom/valueHandler/InputValueHandler.ts | 0 .../dom/valueHandler/OptionValueHandler.ts | 0 .../dom/valueHandler/SelectValueHandler.ts | 6 +- .../dom/valueHandler/TextareaValueHandler.ts | 0 .../dom/valueHandler/ValueChangeHandler.ts | 2 +- .../src/dom/valueHandler/index.ts | 14 +-- .../src/event/EventBinding.ts | 38 +++--- libs/{horizon => inula}/src/event/EventHub.ts | 20 +-- .../src/event/EventWrapper.ts | 0 .../src/event/FormValueController.ts | 0 .../src/event/InulaEventMain.ts} | 24 ++-- .../src/event/ListenerGetter.ts | 8 +- .../src/event/MouseEvent.ts | 0 libs/{horizon => inula}/src/event/Types.ts | 4 +- libs/{horizon => inula}/src/event/utils.ts | 0 .../src/external/ChildrenUtil.ts | 4 +- .../src/external/InulaIs.ts} | 0 .../src/external/JSXElement.ts | 0 .../src/external/JSXElementType.ts | 0 .../src/external/TestUtil.ts | 0 .../src/external/devtools.ts | 2 +- .../src/inulax}/CommonUtils.ts | 2 +- .../src/inulax}/Constants.ts | 2 +- .../src/inulax}/adapters/redux.ts | 6 +- .../src/inulax}/adapters/reduxReact.ts | 2 +- .../src/inulax}/adapters/reduxThunk.ts | 0 libs/inula/src/inulax/devtools/constants.ts | 10 ++ .../src/inulax}/devtools/index.ts | 32 ++--- .../src/inulax}/proxy/HooklessObserver.ts | 0 .../src/inulax}/proxy/Observer.ts | 0 .../src/inulax}/proxy/ProxyHandler.ts | 0 .../proxy/handlers/ArrayProxyHandler.ts | 2 +- .../proxy/handlers/CollectionProxyHandler.ts | 0 .../src/inulax}/proxy/handlers/MapProxy.ts | 0 .../proxy/handlers/ObjectProxyHandler.ts | 2 +- .../src/inulax}/proxy/handlers/SetProxy.ts | 0 .../inulax}/proxy/handlers/WeakMapProxy.ts | 0 .../inulax}/proxy/handlers/WeakSetProxy.ts | 0 .../src/inulax}/proxy/readonlyProxy.ts | 0 .../src/inulax}/proxy/watch.ts | 0 .../src/inulax}/store/StoreHandler.ts | 0 .../horizonx => inula/src/inulax}/types.d.ts | 0 .../src/renderer/ContextSaver.ts | 0 .../src/renderer/ErrorHandler.ts | 0 .../src/renderer/ExecuteMode.ts | 0 .../src/renderer/GlobalVar.ts | 0 .../src/renderer/Renderer.ts | 0 .../src/renderer/RootStack.ts | 0 .../src/renderer/TreeBuilder.ts | 12 +- libs/{horizon => inula}/src/renderer/Types.ts | 0 .../src/renderer/UpdateHandler.ts | 4 +- .../renderer/components/BaseClassComponent.ts | 0 .../src/renderer/components/CreatePortal.ts | 0 .../src/renderer/components/CreateRef.ts | 0 .../src/renderer/components/ForwardRef.ts | 0 .../src/renderer/components/Lazy.ts | 0 .../src/renderer/components/Memo.ts | 0 .../renderer/components/context/Context.ts | 0 .../components/context/CreateContext.ts | 0 .../src/renderer/diff/DiffTools.ts | 0 .../src/renderer/diff/nodeDiffComparator.ts | 0 .../src/renderer/hooks/BaseHook.ts | 0 .../src/renderer/hooks/EffectConstant.js | 0 .../src/renderer/hooks/HookExternal.ts | 0 .../src/renderer/hooks/HookMain.ts | 0 .../src/renderer/hooks/HookStage.ts | 0 .../src/renderer/hooks/HookType.ts | 0 .../src/renderer/hooks/UseCallbackHook.ts | 0 .../src/renderer/hooks/UseEffectHook.ts | 0 .../src/renderer/hooks/UseImperativeHook.ts | 0 .../src/renderer/hooks/UseMemoHook.ts | 0 .../src/renderer/hooks/UseReducerHook.ts | 0 .../src/renderer/hooks/UseRefHook.ts | 0 .../src/renderer/hooks/UseStateHook.ts | 0 .../src/renderer/render/BaseComponent.ts | 2 +- .../src/renderer/render/ClassComponent.ts | 0 .../src/renderer/render/ContextConsumer.ts | 0 .../src/renderer/render/ContextProvider.ts | 0 .../src/renderer/render/DomComponent.ts | 0 .../src/renderer/render/DomPortal.ts | 0 .../src/renderer/render/DomText.ts | 2 +- .../src/renderer/render/ForwardRef.ts | 0 .../src/renderer/render/Fragment.ts | 0 .../src/renderer/render/FunctionComponent.ts | 0 .../src/renderer/render/LazyComponent.ts | 0 .../src/renderer/render/MemoComponent.ts | 2 +- .../src/renderer/render/SuspenseComponent.ts | 0 .../src/renderer/render/TreeRoot.ts | 0 .../render/class/ClassLifeCycleProcessor.ts | 0 .../src/renderer/render/index.ts | 0 .../src/renderer/submit/HookEffectHandler.ts | 0 .../src/renderer/submit/LifeCycleHandler.ts | 6 +- .../src/renderer/submit/Submit.ts | 0 .../src/renderer/taskExecutor/BrowserAsync.ts | 0 .../src/renderer/taskExecutor/RenderQueue.ts | 0 .../src/renderer/taskExecutor/TaskExecutor.ts | 0 .../src/renderer/taskExecutor/TaskQueue.ts | 0 .../src/renderer/utils/compare.ts | 0 .../src/renderer/utils/throwIfTrue.ts | 0 .../src/renderer/utils/vNodePath.ts | 0 .../src/renderer/vnode/VNode.ts | 10 +- .../src/renderer/vnode/VNodeCreator.ts | 2 +- .../src/renderer/vnode/VNodeFlags.ts | 0 .../src/renderer/vnode/VNodeShouldUpdate.ts | 0 .../src/renderer/vnode/VNodeTags.ts | 0 .../src/renderer/vnode/VNodeUtils.ts | 4 +- package.json | 8 +- scripts/__tests__/ActTest/act.test.js | 4 +- .../__tests__/ComponentTest/ClassRefs.test.js | 8 +- .../ComponentTest/ComponentError.test.js | 10 +- .../__tests__/ComponentTest/Context.test.js | 78 ++++++------ .../ComponentTest/DiffAlgorithm.test.js | 8 +- .../ComponentTest/ForwardRef.test.js | 26 ++-- .../ComponentTest/FragmentComponent.test.js | 84 ++++++------- .../ComponentTest/FunctionComponent.test.js | 26 ++-- .../HookTest/UseCallback.test.js | 8 +- .../ComponentTest/HookTest/UseContext.test.js | 14 +-- .../ComponentTest/HookTest/UseEffect.test.js | 96 +++++++------- .../HookTest/UseImperativeHandle.test.js | 22 ++-- .../HookTest/UseLayoutEffect.test.js | 16 +-- .../ComponentTest/HookTest/UseMemo.test.js | 22 ++-- .../ComponentTest/HookTest/UseReducer.test.js | 12 +- .../ComponentTest/HookTest/UseRef.test.js | 8 +- .../ComponentTest/HookTest/UseState.test.js | 24 ++-- .../ComponentTest/JsxElement.test.js | 6 +- .../ComponentTest/LazyComponent.test.js | 82 ++++++------ .../__tests__/ComponentTest/LifeCycle.test.js | 74 +++++------ scripts/__tests__/ComponentTest/Memo.test.js | 12 +- .../ComponentTest/PortalComponent.test.js | 76 +++++------ .../ComponentTest/SuspenseComponent.test.js | 18 +-- scripts/__tests__/DomTest/Attribute.test.js | 34 ++--- scripts/__tests__/DomTest/DomInput.test.js | 118 +++++++++--------- scripts/__tests__/DomTest/DomSelect.test.js | 48 +++---- scripts/__tests__/DomTest/DomTextarea.test.js | 52 ++++---- scripts/__tests__/EventTest/EventMain.test.js | 32 ++--- .../__tests__/EventTest/FocusEvent.test.js | 6 +- .../__tests__/EventTest/KeyboardEvent.test.js | 12 +- .../EventTest/MouseEnterEvent.test.js | 60 ++++----- .../__tests__/EventTest/MouseEvent.test.js | 16 +-- .../__tests__/EventTest/WheelEvent.test.js | 6 +- scripts/__tests__/HorizonIsTest/index.test.js | 71 ----------- .../StateManager/StateArray.test.tsx | 16 +-- .../StateManager/StateMap.test.tsx | 58 ++++----- .../StateManager/StateMixType.test.tsx | 12 +- .../StateManager/StateSet.test.tsx | 50 ++++---- .../StateManager/StateWeakMap.test.tsx | 16 +-- .../StateManager/StateWeakSet.test.tsx | 12 +- .../StoreFunctionality/async.test.tsx | 6 +- .../StoreFunctionality/basicAccess.test.tsx | 24 ++-- .../StoreFunctionality/cloneDeep.test.js | 18 +-- .../StoreFunctionality/dollarAccess.test.tsx | 10 +- .../StoreFunctionality/otherCases.test.tsx | 20 +-- .../HorizonXTest/StoreFunctionality/reset.js | 16 +-- .../HorizonXTest/StoreFunctionality/store.ts | 2 +- .../StoreFunctionality/utils.test.js | 2 +- .../StoreFunctionality/watch.test.tsx | 4 +- .../adapters/ReduxAdapter.test.tsx | 4 +- .../adapters/ReduxAdapterThunk.test.tsx | 4 +- .../adapters/ReduxReactAdapter.test.tsx | 38 +++--- .../HorizonXTest/adapters/connectTest.tsx | 8 +- .../class/ClassException.test.tsx | 12 +- .../class/ClassStateArray.test.tsx | 24 ++-- .../HorizonXTest/class/ClassStateMap.test.tsx | 74 +++++------ .../clear/ClassVNodeClear.test.tsx | 22 ++-- .../clear/FunctionVNodeClear.test.tsx | 22 ++-- .../edgeCases/deepVariableObserver.test.tsx | 2 +- .../edgeCases/multipleStores.test.tsx | 26 ++-- .../HorizonXTest/edgeCases/proxy.test.tsx | 4 +- scripts/__tests__/InulaIsTest/index.test.js | 71 +++++++++++ scripts/__tests__/jest/commonComponents.js | 2 +- scripts/__tests__/jest/jestEnvironment.js | 2 +- scripts/__tests__/jest/jestSetting.js | 2 +- scripts/gen3rdLib.js | 8 +- scripts/rollup/rollup.config.js | 12 +- tsconfig.json | 2 +- 199 files changed, 1050 insertions(+), 1050 deletions(-) delete mode 100644 libs/horizon/README.md delete mode 100644 libs/horizon/src/horizonx/devtools/constants.ts create mode 100644 libs/inula/README.md rename libs/{horizon => inula}/global.d.ts (94%) rename libs/{horizon => inula}/index.ts (90%) rename libs/{horizon => inula}/jsx-dev-runtime.ts (100%) rename libs/{horizon => inula}/jsx-runtime.ts (100%) rename libs/{horizon => inula}/npm/index.js (83%) rename libs/{horizon => inula}/package.json (66%) rename libs/{horizon => inula}/src/dom/DOMExternal.ts (100%) rename libs/{horizon => inula}/src/dom/DOMInternalKeys.ts (94%) rename libs/{horizon => inula}/src/dom/DOMOperator.ts (97%) rename libs/{horizon => inula}/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts (96%) rename libs/{horizon => inula}/src/dom/DOMPropertiesHandler/StyleHandler.ts (100%) rename libs/{horizon => inula}/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts (100%) rename libs/{horizon => inula}/src/dom/SelectionRangeHandler.ts (100%) rename libs/{horizon => inula}/src/dom/utils/Common.ts (96%) rename libs/{horizon => inula}/src/dom/utils/DomCreator.ts (100%) rename libs/{horizon => inula}/src/dom/utils/Interface.ts (80%) rename libs/{horizon => inula}/src/dom/validators/PropertiesData.ts (100%) rename libs/{horizon => inula}/src/dom/validators/ValidateProps.ts (100%) rename libs/{horizon => inula}/src/dom/valueHandler/InputValueHandler.ts (100%) rename libs/{horizon => inula}/src/dom/valueHandler/OptionValueHandler.ts (100%) rename libs/{horizon => inula}/src/dom/valueHandler/SelectValueHandler.ts (91%) rename libs/{horizon => inula}/src/dom/valueHandler/TextareaValueHandler.ts (100%) rename libs/{horizon => inula}/src/dom/valueHandler/ValueChangeHandler.ts (96%) rename libs/{horizon => inula}/src/dom/valueHandler/index.ts (81%) rename libs/{horizon => inula}/src/event/EventBinding.ts (74%) rename libs/{horizon => inula}/src/event/EventHub.ts (85%) rename libs/{horizon => inula}/src/event/EventWrapper.ts (100%) rename libs/{horizon => inula}/src/event/FormValueController.ts (100%) rename libs/{horizon/src/event/HorizonEventMain.ts => inula/src/event/InulaEventMain.ts} (87%) rename libs/{horizon => inula}/src/event/ListenerGetter.ts (96%) rename libs/{horizon => inula}/src/event/MouseEvent.ts (100%) rename libs/{horizon => inula}/src/event/Types.ts (92%) rename libs/{horizon => inula}/src/event/utils.ts (100%) rename libs/{horizon => inula}/src/external/ChildrenUtil.ts (96%) rename libs/{horizon/src/external/HorizonIs.ts => inula/src/external/InulaIs.ts} (100%) rename libs/{horizon => inula}/src/external/JSXElement.ts (100%) rename libs/{horizon => inula}/src/external/JSXElementType.ts (100%) rename libs/{horizon => inula}/src/external/TestUtil.ts (100%) rename libs/{horizon => inula}/src/external/devtools.ts (99%) rename libs/{horizon/src/horizonx => inula/src/inulax}/CommonUtils.ts (99%) rename libs/{horizon/src/horizonx => inula/src/inulax}/Constants.ts (93%) rename libs/{horizon/src/horizonx => inula/src/inulax}/adapters/redux.ts (97%) rename libs/{horizon/src/horizonx => inula/src/inulax}/adapters/reduxReact.ts (98%) rename libs/{horizon/src/horizonx => inula/src/inulax}/adapters/reduxThunk.ts (100%) create mode 100644 libs/inula/src/inulax/devtools/constants.ts rename libs/{horizon/src/horizonx => inula/src/inulax}/devtools/index.ts (84%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/HooklessObserver.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/Observer.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/ProxyHandler.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/handlers/ArrayProxyHandler.ts (97%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/handlers/CollectionProxyHandler.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/handlers/MapProxy.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/handlers/ObjectProxyHandler.ts (96%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/handlers/SetProxy.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/handlers/WeakMapProxy.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/handlers/WeakSetProxy.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/readonlyProxy.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/proxy/watch.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/store/StoreHandler.ts (100%) rename libs/{horizon/src/horizonx => inula/src/inulax}/types.d.ts (100%) rename libs/{horizon => inula}/src/renderer/ContextSaver.ts (100%) rename libs/{horizon => inula}/src/renderer/ErrorHandler.ts (100%) rename libs/{horizon => inula}/src/renderer/ExecuteMode.ts (100%) rename libs/{horizon => inula}/src/renderer/GlobalVar.ts (100%) rename libs/{horizon => inula}/src/renderer/Renderer.ts (100%) rename libs/{horizon => inula}/src/renderer/RootStack.ts (100%) rename libs/{horizon => inula}/src/renderer/TreeBuilder.ts (96%) rename libs/{horizon => inula}/src/renderer/Types.ts (100%) rename libs/{horizon => inula}/src/renderer/UpdateHandler.ts (95%) rename libs/{horizon => inula}/src/renderer/components/BaseClassComponent.ts (100%) rename libs/{horizon => inula}/src/renderer/components/CreatePortal.ts (100%) rename libs/{horizon => inula}/src/renderer/components/CreateRef.ts (100%) rename libs/{horizon => inula}/src/renderer/components/ForwardRef.ts (100%) rename libs/{horizon => inula}/src/renderer/components/Lazy.ts (100%) rename libs/{horizon => inula}/src/renderer/components/Memo.ts (100%) rename libs/{horizon => inula}/src/renderer/components/context/Context.ts (100%) rename libs/{horizon => inula}/src/renderer/components/context/CreateContext.ts (100%) rename libs/{horizon => inula}/src/renderer/diff/DiffTools.ts (100%) rename libs/{horizon => inula}/src/renderer/diff/nodeDiffComparator.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/BaseHook.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/EffectConstant.js (100%) rename libs/{horizon => inula}/src/renderer/hooks/HookExternal.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/HookMain.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/HookStage.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/HookType.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/UseCallbackHook.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/UseEffectHook.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/UseImperativeHook.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/UseMemoHook.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/UseReducerHook.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/UseRefHook.ts (100%) rename libs/{horizon => inula}/src/renderer/hooks/UseStateHook.ts (100%) rename libs/{horizon => inula}/src/renderer/render/BaseComponent.ts (97%) rename libs/{horizon => inula}/src/renderer/render/ClassComponent.ts (100%) rename libs/{horizon => inula}/src/renderer/render/ContextConsumer.ts (100%) rename libs/{horizon => inula}/src/renderer/render/ContextProvider.ts (100%) rename libs/{horizon => inula}/src/renderer/render/DomComponent.ts (100%) rename libs/{horizon => inula}/src/renderer/render/DomPortal.ts (100%) rename libs/{horizon => inula}/src/renderer/render/DomText.ts (96%) rename libs/{horizon => inula}/src/renderer/render/ForwardRef.ts (100%) rename libs/{horizon => inula}/src/renderer/render/Fragment.ts (100%) rename libs/{horizon => inula}/src/renderer/render/FunctionComponent.ts (100%) rename libs/{horizon => inula}/src/renderer/render/LazyComponent.ts (100%) rename libs/{horizon => inula}/src/renderer/render/MemoComponent.ts (97%) rename libs/{horizon => inula}/src/renderer/render/SuspenseComponent.ts (100%) rename libs/{horizon => inula}/src/renderer/render/TreeRoot.ts (100%) rename libs/{horizon => inula}/src/renderer/render/class/ClassLifeCycleProcessor.ts (100%) rename libs/{horizon => inula}/src/renderer/render/index.ts (100%) rename libs/{horizon => inula}/src/renderer/submit/HookEffectHandler.ts (100%) rename libs/{horizon => inula}/src/renderer/submit/LifeCycleHandler.ts (98%) rename libs/{horizon => inula}/src/renderer/submit/Submit.ts (100%) rename libs/{horizon => inula}/src/renderer/taskExecutor/BrowserAsync.ts (100%) rename libs/{horizon => inula}/src/renderer/taskExecutor/RenderQueue.ts (100%) rename libs/{horizon => inula}/src/renderer/taskExecutor/TaskExecutor.ts (100%) rename libs/{horizon => inula}/src/renderer/taskExecutor/TaskQueue.ts (100%) rename libs/{horizon => inula}/src/renderer/utils/compare.ts (100%) rename libs/{horizon => inula}/src/renderer/utils/throwIfTrue.ts (100%) rename libs/{horizon => inula}/src/renderer/utils/vNodePath.ts (100%) rename libs/{horizon => inula}/src/renderer/vnode/VNode.ts (94%) rename libs/{horizon => inula}/src/renderer/vnode/VNodeCreator.ts (99%) rename libs/{horizon => inula}/src/renderer/vnode/VNodeFlags.ts (100%) rename libs/{horizon => inula}/src/renderer/vnode/VNodeShouldUpdate.ts (100%) rename libs/{horizon => inula}/src/renderer/vnode/VNodeTags.ts (100%) rename libs/{horizon => inula}/src/renderer/vnode/VNodeUtils.ts (98%) delete mode 100644 scripts/__tests__/HorizonIsTest/index.test.js create mode 100644 scripts/__tests__/InulaIsTest/index.test.js diff --git a/app_define.json b/app_define.json index cd13e351..a674d06c 100644 --- a/app_define.json +++ b/app_define.json @@ -1 +1 @@ -{"fileVersion":"1","name":"Horizon","serviceId":"067e5bef6cd240ae9460229d107a99a6","description":"","version":"1.0.0","type":"microService","processes":{"Horizon":{"subscribes":[]}}} \ No newline at end of file +{"fileVersion":"1","name":"Inula","serviceId":"067e5bef6cd240ae9460229d107a99a6","description":"","version":"1.0.0","type":"microService","processes":{"Inula":{"subscribes":[]}}} diff --git a/babel.config.js b/babel.config.js index fa0e4b8c..522db7a3 100644 --- a/babel.config.js +++ b/babel.config.js @@ -20,8 +20,8 @@ module.exports = { [ '@babel/plugin-transform-react-jsx', { - pragma: 'Horizon.createElement', - pragmaFrag: 'Horizon.Fragment', + pragma: 'Inula.createElement', + pragmaFrag: 'Inula.Fragment', }, ], ['@babel/plugin-proposal-class-properties', { loose: true }], diff --git a/jest.config.js b/jest.config.js index 69f84f2b..5c55a50d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -26,8 +26,8 @@ module.exports = { testEnvironment: 'jest-environment-jsdom-sixteen', testMatch: [ - // '/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx', - // '/scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx', + // '/scripts/__tests__/InulaXTest/edgeCases/deepVariableObserver.test.tsx', + // '/scripts/__tests__/InulaXTest/StateManager/StateMap.test.tsx', '/scripts/__tests__/**/*.test.js', '/scripts/__tests__/**/*.test.tsx', ], diff --git a/libs/horizon/README.md b/libs/horizon/README.md deleted file mode 100644 index c44e6d74..00000000 --- a/libs/horizon/README.md +++ /dev/null @@ -1 +0,0 @@ -# `horizon` diff --git a/libs/horizon/src/horizonx/devtools/constants.ts b/libs/horizon/src/horizonx/devtools/constants.ts deleted file mode 100644 index 3e105125..00000000 --- a/libs/horizon/src/horizonx/devtools/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const INITIALIZED = 'horizonx store initialized'; -export const STATE_CHANGE = 'horizonx state change'; -export const SUBSCRIBED = 'horizonx subscribed'; -export const UNSUBSCRIBED = 'horizonx unsubscribed'; -export const ACTION = 'horizonx action'; -export const ACTION_QUEUED = 'horizonx action queued'; -export const QUEUE_PENDING = 'horizonx queue pending'; -export const QUEUE_FINISHED = 'horizonx queue finished'; -export const RENDER_TRIGGERED = 'horizonx render triggered'; -export const OBSERVED_COMPONENTS = 'horizonx observed components'; diff --git a/libs/inula/README.md b/libs/inula/README.md new file mode 100644 index 00000000..95818b1f --- /dev/null +++ b/libs/inula/README.md @@ -0,0 +1 @@ +# `inula` diff --git a/libs/horizon/global.d.ts b/libs/inula/global.d.ts similarity index 94% rename from libs/horizon/global.d.ts rename to libs/inula/global.d.ts index 76352c78..963ff285 100644 --- a/libs/horizon/global.d.ts +++ b/libs/inula/global.d.ts @@ -20,4 +20,4 @@ declare var isDev: boolean; declare var isTest: boolean; declare const __VERSION__: string; declare var setImmediate: Function; -declare var __HORIZON_DEV_HOOK__: any; +declare var __INULA_DEV_HOOK__: any; diff --git a/libs/horizon/index.ts b/libs/inula/index.ts similarity index 90% rename from libs/horizon/index.ts rename to libs/inula/index.ts index 20e47409..7668ddb2 100644 --- a/libs/horizon/index.ts +++ b/libs/inula/index.ts @@ -54,10 +54,10 @@ import { isLazy, isMemo, isPortal, -} from './src/external/HorizonIs'; -import { createStore, useStore, clearStore } from './src/horizonx/store/StoreHandler'; -import * as reduxAdapter from './src/horizonx/adapters/redux'; -import { watch } from './src/horizonx/proxy/watch'; +} from './src/external/InulaIs'; +import { createStore, useStore, clearStore } from './src/inulax/store/StoreHandler'; +import * as reduxAdapter from './src/inulax/adapters/redux'; +import { watch } from './src/inulax/proxy/watch'; import { act } from './src/external/TestUtil'; import { @@ -69,9 +69,9 @@ import { } from './src/dom/DOMExternal'; import { syncUpdates as flushSync } from './src/renderer/TreeBuilder'; -import { toRaw } from './src/horizonx/proxy/ProxyHandler'; +import { toRaw } from './src/inulax/proxy/ProxyHandler'; -const Horizon = { +const Inula = { Children, createRef, Component, @@ -152,7 +152,7 @@ export { unmountComponentAtNode, act, flushSync, - // 状态管理器HorizonX接口 + // 状态管理器InulaX接口 createStore, useStore, clearStore, @@ -177,4 +177,4 @@ export { Suspense, }; -export default Horizon; +export default Inula; diff --git a/libs/horizon/jsx-dev-runtime.ts b/libs/inula/jsx-dev-runtime.ts similarity index 100% rename from libs/horizon/jsx-dev-runtime.ts rename to libs/inula/jsx-dev-runtime.ts diff --git a/libs/horizon/jsx-runtime.ts b/libs/inula/jsx-runtime.ts similarity index 100% rename from libs/horizon/jsx-runtime.ts rename to libs/inula/jsx-runtime.ts diff --git a/libs/horizon/npm/index.js b/libs/inula/npm/index.js similarity index 83% rename from libs/horizon/npm/index.js rename to libs/inula/npm/index.js index b2c3033b..7cf8fa19 100644 --- a/libs/horizon/npm/index.js +++ b/libs/inula/npm/index.js @@ -16,7 +16,7 @@ 'use strict'; if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/horizon.production.min.js'); + module.exports = require('./cjs/inula.production.min.js'); } else { - module.exports = require('./cjs/horizon.development.js'); + module.exports = require('./cjs/inula.development.js'); } diff --git a/libs/horizon/package.json b/libs/inula/package.json similarity index 66% rename from libs/horizon/package.json rename to libs/inula/package.json index f64cd277..0e57bbe8 100644 --- a/libs/horizon/package.json +++ b/libs/inula/package.json @@ -1,8 +1,8 @@ { - "name": "@cloudsop/horizon", - "description": "Horizon is a JavaScript framework library.", + "name": "inulajs", + "description": "InulaJS is a JavaScript framework library.", "keywords": [ - "horizon" + "inula" ], "version": "0.0.52", "homepage": "", diff --git a/libs/horizon/src/dom/DOMExternal.ts b/libs/inula/src/dom/DOMExternal.ts similarity index 100% rename from libs/horizon/src/dom/DOMExternal.ts rename to libs/inula/src/dom/DOMExternal.ts diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/inula/src/dom/DOMInternalKeys.ts similarity index 94% rename from libs/horizon/src/dom/DOMInternalKeys.ts rename to libs/inula/src/dom/DOMInternalKeys.ts index 3c356644..baa40fcc 100644 --- a/libs/horizon/src/dom/DOMInternalKeys.ts +++ b/libs/inula/src/dom/DOMInternalKeys.ts @@ -22,9 +22,9 @@ import type { Container, Props } from './DOMOperator'; import { DomComponent, DomText, TreeRoot } from '../renderer/vnode/VNodeTags'; -const INTERNAL_VNODE = '_horizon_VNode'; -const INTERNAL_PROPS = '_horizon_Props'; -const INTERNAL_NONDELEGATEEVENTS = '_horizon_NonDelegatedEvents'; +const INTERNAL_VNODE = '_inula_VNode'; +const INTERNAL_PROPS = '_inula_Props'; +const INTERNAL_NONDELEGATEEVENTS = '_inula_NonDelegatedEvents'; // 通过 VNode 实例获取 DOM 节点 export function getDom(vNode: VNode): Element | Text | null { diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/inula/src/dom/DOMOperator.ts similarity index 97% rename from libs/horizon/src/dom/DOMOperator.ts rename to libs/inula/src/dom/DOMOperator.ts index ca952804..a4adebb4 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/inula/src/dom/DOMOperator.ts @@ -72,7 +72,7 @@ export function resetAfterSubmit(): void { // 创建 DOM 对象 export function newDom(tagName: string, props: Props, parentNamespace: string, vNode: VNode): Element { // document取值于treeRoot对应的DOM的ownerDocument。 - // 解决:在iframe中使用top的horizon时,horizon在创建DOM时用到的document并不是iframe的document,而是top中的document的问题。 + // 解决:在iframe中使用top的inula时,inula在创建DOM时用到的document并不是iframe的document,而是top中的document的问题。 const rootDom = getCurrentRoot().realNode; const doc = isDocument(rootDom) ? rootDom : rootDom.ownerDocument; diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts b/libs/inula/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts similarity index 96% rename from libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts rename to libs/inula/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts index f2657883..b3d83f8a 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts +++ b/libs/inula/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import { allDelegatedHorizonEvents } from '../../event/EventHub'; +import { allDelegatedInulaEvents } from '../../event/EventHub'; import { updateCommonProp } from './UpdateCommonProp'; import { setStyles } from './StyleHandler'; import { lazyDelegateOnRoot, listenNonDelegatedEvent } from '../../event/EventBinding'; @@ -35,7 +35,7 @@ export function setDomProps(dom: Element, props: Object, isNativeTag: boolean, i } else if (isEventProp(propName)) { // 事件监听属性处理 const currentRoot = getCurrentRoot(); - if (!allDelegatedHorizonEvents.has(propName)) { + if (!allDelegatedInulaEvents.has(propName)) { listenNonDelegatedEvent(propName, dom, propVal); } else if (currentRoot && !currentRoot.delegatedEvents.has(propName)) { lazyDelegateOnRoot(currentRoot, propName); @@ -84,7 +84,7 @@ export function compareProps(oldProps: Object, newProps: Object): Object { } else if (propName === 'autoFocus' || propName === 'children' || propName === 'dangerouslySetInnerHTML') { continue; } else if (isEventProp(propName)) { - if (!allDelegatedHorizonEvents.has(propName)) { + if (!allDelegatedInulaEvents.has(propName)) { toUpdateProps[propName] = null; } } else { @@ -155,7 +155,7 @@ export function compareProps(oldProps: Object, newProps: Object): Object { } } else if (isEventProp(propName)) { const currentRoot = getCurrentRoot(); - if (!allDelegatedHorizonEvents.has(propName)) { + if (!allDelegatedInulaEvents.has(propName)) { toUpdateProps[propName] = newPropValue; } else if (currentRoot && !currentRoot.delegatedEvents.has(propName)) { lazyDelegateOnRoot(currentRoot, propName); diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts b/libs/inula/src/dom/DOMPropertiesHandler/StyleHandler.ts similarity index 100% rename from libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts rename to libs/inula/src/dom/DOMPropertiesHandler/StyleHandler.ts diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts b/libs/inula/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts similarity index 100% rename from libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts rename to libs/inula/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts diff --git a/libs/horizon/src/dom/SelectionRangeHandler.ts b/libs/inula/src/dom/SelectionRangeHandler.ts similarity index 100% rename from libs/horizon/src/dom/SelectionRangeHandler.ts rename to libs/inula/src/dom/SelectionRangeHandler.ts diff --git a/libs/horizon/src/dom/utils/Common.ts b/libs/inula/src/dom/utils/Common.ts similarity index 96% rename from libs/horizon/src/dom/utils/Common.ts rename to libs/inula/src/dom/utils/Common.ts index 7b2af2b5..50dc845f 100644 --- a/libs/horizon/src/dom/utils/Common.ts +++ b/libs/inula/src/dom/utils/Common.ts @@ -13,14 +13,14 @@ * See the Mulan PSL v2 for more details. */ -import { HorizonDom } from './Interface'; +import { InulaDom } from './Interface'; import { Props } from '../DOMOperator'; /** * 获取当前聚焦的 input 或者 textarea 元素 * @param doc 指定 document */ -export function getFocusedDom(doc?: Document): HorizonDom | null { +export function getFocusedDom(doc?: Document): InulaDom | null { const currentDocument = doc ?? document; return currentDocument.activeElement ?? currentDocument.body; diff --git a/libs/horizon/src/dom/utils/DomCreator.ts b/libs/inula/src/dom/utils/DomCreator.ts similarity index 100% rename from libs/horizon/src/dom/utils/DomCreator.ts rename to libs/inula/src/dom/utils/DomCreator.ts diff --git a/libs/horizon/src/dom/utils/Interface.ts b/libs/inula/src/dom/utils/Interface.ts similarity index 80% rename from libs/horizon/src/dom/utils/Interface.ts rename to libs/inula/src/dom/utils/Interface.ts index 367bcebf..9cbc2536 100644 --- a/libs/horizon/src/dom/utils/Interface.ts +++ b/libs/inula/src/dom/utils/Interface.ts @@ -17,8 +17,8 @@ export interface Props { [propName: string]: any; } -export interface HorizonSelect extends HTMLSelectElement { +export interface InulaSelect extends HTMLSelectElement { _multiple?: boolean; } -export type HorizonDom = Element | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; +export type InulaDom = Element | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; diff --git a/libs/horizon/src/dom/validators/PropertiesData.ts b/libs/inula/src/dom/validators/PropertiesData.ts similarity index 100% rename from libs/horizon/src/dom/validators/PropertiesData.ts rename to libs/inula/src/dom/validators/PropertiesData.ts diff --git a/libs/horizon/src/dom/validators/ValidateProps.ts b/libs/inula/src/dom/validators/ValidateProps.ts similarity index 100% rename from libs/horizon/src/dom/validators/ValidateProps.ts rename to libs/inula/src/dom/validators/ValidateProps.ts diff --git a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts b/libs/inula/src/dom/valueHandler/InputValueHandler.ts similarity index 100% rename from libs/horizon/src/dom/valueHandler/InputValueHandler.ts rename to libs/inula/src/dom/valueHandler/InputValueHandler.ts diff --git a/libs/horizon/src/dom/valueHandler/OptionValueHandler.ts b/libs/inula/src/dom/valueHandler/OptionValueHandler.ts similarity index 100% rename from libs/horizon/src/dom/valueHandler/OptionValueHandler.ts rename to libs/inula/src/dom/valueHandler/OptionValueHandler.ts diff --git a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts b/libs/inula/src/dom/valueHandler/SelectValueHandler.ts similarity index 91% rename from libs/horizon/src/dom/valueHandler/SelectValueHandler.ts rename to libs/inula/src/dom/valueHandler/SelectValueHandler.ts index ecb767a6..083fed75 100644 --- a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts +++ b/libs/inula/src/dom/valueHandler/SelectValueHandler.ts @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import { HorizonSelect, Props } from '../utils/Interface'; +import { InulaSelect, Props } from '../utils/Interface'; function updateMultipleValue(options, newValues) { const newValueSet = new Set(); @@ -54,14 +54,14 @@ function updateValue(options, newValues: any, isMultiple: boolean) { } } -export function getSelectPropsWithoutValue(dom: HorizonSelect, properties: Object) { +export function getSelectPropsWithoutValue(dom: InulaSelect, properties: Object) { return { ...properties, value: undefined, }; } -export function updateSelectValue(dom: HorizonSelect, props: Props, isInit = false) { +export function updateSelectValue(dom: InulaSelect, props: Props, isInit = false) { const { value, defaultValue, multiple } = props; const oldMultiple = dom._multiple !== undefined ? dom._multiple : dom.multiple; diff --git a/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts b/libs/inula/src/dom/valueHandler/TextareaValueHandler.ts similarity index 100% rename from libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts rename to libs/inula/src/dom/valueHandler/TextareaValueHandler.ts diff --git a/libs/horizon/src/dom/valueHandler/ValueChangeHandler.ts b/libs/inula/src/dom/valueHandler/ValueChangeHandler.ts similarity index 96% rename from libs/horizon/src/dom/valueHandler/ValueChangeHandler.ts rename to libs/inula/src/dom/valueHandler/ValueChangeHandler.ts index 0d390902..99968321 100644 --- a/libs/horizon/src/dom/valueHandler/ValueChangeHandler.ts +++ b/libs/inula/src/dom/valueHandler/ValueChangeHandler.ts @@ -14,7 +14,7 @@ */ /** - * Horizon的输入框和文本框的change事件在原生的change事件上做了一层处理 + * Inula的输入框和文本框的change事件在原生的change事件上做了一层处理 * 只有值发生变化时才会触发change事件。 */ diff --git a/libs/horizon/src/dom/valueHandler/index.ts b/libs/inula/src/dom/valueHandler/index.ts similarity index 81% rename from libs/horizon/src/dom/valueHandler/index.ts rename to libs/inula/src/dom/valueHandler/index.ts index b432b658..6a9ad5a3 100644 --- a/libs/horizon/src/dom/valueHandler/index.ts +++ b/libs/inula/src/dom/valueHandler/index.ts @@ -18,21 +18,21 @@ * 处理组件被代理和不被代理情况下的不同逻辑 */ -import { HorizonDom, HorizonSelect, Props } from '../utils/Interface'; +import { InulaDom, InulaSelect, Props } from '../utils/Interface'; import { getInputPropsWithoutValue, setInitInputValue, updateInputValue } from './InputValueHandler'; import { getOptionPropsWithoutValue } from './OptionValueHandler'; import { getSelectPropsWithoutValue, updateSelectValue } from './SelectValueHandler'; import { getTextareaPropsWithoutValue, updateTextareaValue } from './TextareaValueHandler'; // 获取元素除了被代理的值以外的属性 -function getPropsWithoutValue(type: string, dom: HorizonDom, props: Props) { +function getPropsWithoutValue(type: string, dom: InulaDom, props: Props) { switch (type) { case 'input': return getInputPropsWithoutValue(dom, props); case 'option': return getOptionPropsWithoutValue(dom, props); case 'select': - return getSelectPropsWithoutValue(dom, props); + return getSelectPropsWithoutValue(dom, props); case 'textarea': return getTextareaPropsWithoutValue(dom, props); default: @@ -41,13 +41,13 @@ function getPropsWithoutValue(type: string, dom: HorizonDom, props: Props) { } // 其它属性挂载完成后处理被代理值相关的属性 -function setInitValue(type: string, dom: HorizonDom, props: Props) { +function setInitValue(type: string, dom: InulaDom, props: Props) { switch (type) { case 'input': setInitInputValue(dom, props); break; case 'select': - updateSelectValue(dom, props, true); + updateSelectValue(dom, props, true); break; case 'textarea': updateTextareaValue(dom, props, true); @@ -58,13 +58,13 @@ function setInitValue(type: string, dom: HorizonDom, props: Props) { } // 更新需要适配的属性 -function updateValue(type: string, dom: HorizonDom, props: Props) { +function updateValue(type: string, dom: InulaDom, props: Props) { switch (type) { case 'input': updateInputValue(dom, props); break; case 'select': - updateSelectValue(dom, props); + updateSelectValue(dom, props); break; case 'textarea': updateTextareaValue(dom, props); diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/inula/src/event/EventBinding.ts similarity index 74% rename from libs/horizon/src/event/EventBinding.ts rename to libs/inula/src/event/EventBinding.ts index 8c81d763..aefcf1b4 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/inula/src/event/EventBinding.ts @@ -16,11 +16,11 @@ /** * 事件绑定实现,分为绑定委托事件和非委托事件 */ -import { allDelegatedHorizonEvents, simulatedDelegatedEvents } from './EventHub'; +import { allDelegatedInulaEvents, simulatedDelegatedEvents } from './EventHub'; import { isDocument } from '../dom/utils/Common'; import { getNearestVNode, getNonDelegatedListenerMap } from '../dom/DOMInternalKeys'; import { asyncUpdates, runDiscreteUpdates } from '../renderer/TreeBuilder'; -import { handleEventMain } from './HorizonEventMain'; +import { handleEventMain } from './InulaEventMain'; import { decorateNativeEvent } from './EventWrapper'; import { VNode } from '../renderer/vnode/VNode'; @@ -55,11 +55,11 @@ function listenToNativeEvent(nativeEvtName: string, delegatedElement: Element, i } // 是否捕获事件 -function isCaptureEvent(horizonEventName) { - if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') { +function isCaptureEvent(inulaEventName) { + if (inulaEventName === 'onLostPointerCapture' || inulaEventName === 'onGotPointerCapture') { return false; } - return horizonEventName.slice(-7) === 'Capture'; + return inulaEventName.slice(-7) === 'Capture'; } // 事件懒委托,当用户定义事件后,再进行委托到根节点 @@ -67,7 +67,7 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { currentRoot.delegatedEvents.add(eventName); const isCapture = isCaptureEvent(eventName); - const nativeEvents = allDelegatedHorizonEvents.get(eventName); + const nativeEvents = allDelegatedInulaEvents.get(eventName); nativeEvents.forEach(nativeEvent => { const nativeFullName = isCapture ? nativeEvent + 'capture' : nativeEvent; @@ -89,13 +89,13 @@ export function listenSimulatedDelegatedEvents(root: VNode) { } } -// 通过horizon事件名获取到native事件名 -function getNativeEvtName(horizonEventName, capture) { +// 通过inula事件名获取到native事件名 +function getNativeEvtName(inulaEventName, capture) { let nativeName; if (capture) { - nativeName = horizonEventName.slice(2, -7); + nativeName = inulaEventName.slice(2, -7); } else { - nativeName = horizonEventName.slice(2); + nativeName = inulaEventName.slice(2); } if (!nativeName) { return ''; @@ -104,9 +104,9 @@ function getNativeEvtName(horizonEventName, capture) { } // 封装监听函数 -function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) { +function getWrapperListener(inulaEventName, nativeEvtName, targetElement, listener) { return event => { - const customEvent = decorateNativeEvent(horizonEventName, nativeEvtName, event); + const customEvent = decorateNativeEvent(inulaEventName, nativeEvtName, event); asyncUpdates(() => { listener(customEvent); }); @@ -114,16 +114,16 @@ function getWrapperListener(horizonEventName, nativeEvtName, targetElement, list } // 非委托事件单独监听到各自dom节点 -export function listenNonDelegatedEvent(horizonEventName: string, domElement: Element, listener): void { - const isCapture = isCaptureEvent(horizonEventName); - const nativeEvtName = getNativeEvtName(horizonEventName, isCapture); +export function listenNonDelegatedEvent(inulaEventName: string, domElement: Element, listener): void { + const isCapture = isCaptureEvent(inulaEventName); + const nativeEvtName = getNativeEvtName(inulaEventName, isCapture); // 先判断是否存在老的监听事件,若存在则移除 const nonDelegatedListenerMap = getNonDelegatedListenerMap(domElement); - const currentListener = nonDelegatedListenerMap.get(horizonEventName); + const currentListener = nonDelegatedListenerMap.get(inulaEventName); if (currentListener) { domElement.removeEventListener(nativeEvtName, currentListener); - nonDelegatedListenerMap.delete(horizonEventName); + nonDelegatedListenerMap.delete(inulaEventName); } if (typeof listener !== 'function') { @@ -131,8 +131,8 @@ export function listenNonDelegatedEvent(horizonEventName: string, domElement: El } // 为了和委托事件对外行为一致,将事件对象封装成CustomBaseEvent - const wrapperListener = getWrapperListener(horizonEventName, nativeEvtName, domElement, listener); + const wrapperListener = getWrapperListener(inulaEventName, nativeEvtName, domElement, listener); // 添加新的监听 - nonDelegatedListenerMap.set(horizonEventName, wrapperListener); + nonDelegatedListenerMap.set(inulaEventName, wrapperListener); domElement.addEventListener(nativeEvtName, wrapperListener, isCapture); } diff --git a/libs/horizon/src/event/EventHub.ts b/libs/inula/src/event/EventHub.ts similarity index 85% rename from libs/horizon/src/event/EventHub.ts rename to libs/inula/src/event/EventHub.ts index 992f1457..d813b247 100644 --- a/libs/horizon/src/event/EventHub.ts +++ b/libs/inula/src/event/EventHub.ts @@ -13,16 +13,16 @@ * See the Mulan PSL v2 for more details. */ -// 需要委托的horizon事件和原生事件对应关系 -export const allDelegatedHorizonEvents = new Map(); +// 需要委托的inula事件和原生事件对应关系 +export const allDelegatedInulaEvents = new Map(); // 模拟委托事件,不冒泡事件需要利用其他事件来触发冒泡过程 export const simulatedDelegatedEvents = ['onMouseEnter', 'onMouseLeave']; // 所有委托的原生事件集合 export const allDelegatedNativeEvents = new Set(); -// Horizon事件和原生事件对应关系 -export const horizonEventToNativeMap = new Map([ +// Inula事件和原生事件对应关系 +export const inulaEventToNativeMap = new Map([ ['onKeyPress', ['keypress']], ['onTextInput', ['textInput']], ['onClick', ['click']], @@ -60,7 +60,7 @@ export const horizonEventToNativeMap = new Map([ ['onAnimationStart', ['animationstart']], ['onTransitionEnd', ['transitionend']], ]); -export const NativeEventToHorizonMap = { +export const NativeEventToInulaMap = { click: 'click', wheel: 'wheel', dblclick: 'doubleClick', @@ -99,17 +99,17 @@ export const EVENT_TYPE_BUBBLE = 'Bubble'; export const EVENT_TYPE_CAPTURE = 'Capture'; export const EVENT_TYPE_ALL = 'All'; -horizonEventToNativeMap.forEach((dependencies, horizonEvent) => { - allDelegatedHorizonEvents.set(horizonEvent, dependencies); - allDelegatedHorizonEvents.set(horizonEvent + 'Capture', dependencies); +inulaEventToNativeMap.forEach((dependencies, inulaEvent) => { + allDelegatedInulaEvents.set(inulaEvent, dependencies); + allDelegatedInulaEvents.set(inulaEvent + 'Capture', dependencies); dependencies.forEach(d => { allDelegatedNativeEvents.add(d); }); }); -export function transformToHorizonEvent(nativeEvtName: string) { - const name = NativeEventToHorizonMap[nativeEvtName]; +export function transformToInulaEvent(nativeEvtName: string) { + const name = NativeEventToInulaMap[nativeEvtName]; // 例:dragEnd -> onDragEnd return !name ? '' : `on${name[0].toUpperCase()}${name.slice(1)}`; } diff --git a/libs/horizon/src/event/EventWrapper.ts b/libs/inula/src/event/EventWrapper.ts similarity index 100% rename from libs/horizon/src/event/EventWrapper.ts rename to libs/inula/src/event/EventWrapper.ts diff --git a/libs/horizon/src/event/FormValueController.ts b/libs/inula/src/event/FormValueController.ts similarity index 100% rename from libs/horizon/src/event/FormValueController.ts rename to libs/inula/src/event/FormValueController.ts diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/inula/src/event/InulaEventMain.ts similarity index 87% rename from libs/horizon/src/event/HorizonEventMain.ts rename to libs/inula/src/event/InulaEventMain.ts index 9330af30..08e5f892 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/inula/src/event/InulaEventMain.ts @@ -24,8 +24,8 @@ import { EVENT_TYPE_ALL, EVENT_TYPE_BUBBLE, EVENT_TYPE_CAPTURE, - horizonEventToNativeMap, - transformToHorizonEvent, + inulaEventToNativeMap, + transformToInulaEvent, } from './EventHub'; import { getDomTag } from '../dom/utils/Common'; import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler'; @@ -94,9 +94,9 @@ function getCommonListeners( target: null | EventTarget, isCapture: boolean ): ListenerUnitList { - const horizonEvtName = transformToHorizonEvent(nativeEvtName); + const inulaEvtName = transformToInulaEvent(nativeEvtName); - if (!horizonEvtName) { + if (!inulaEvtName) { return []; } @@ -113,8 +113,8 @@ function getCommonListeners( nativeEvtName = 'blur'; } - const horizonEvent = decorateNativeEvent(horizonEvtName, nativeEvtName, nativeEvent); - return getListenersFromTree(vNode, horizonEvtName, horizonEvent, isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE); + const inulaEvent = decorateNativeEvent(inulaEvtName, nativeEvtName, nativeEvent); + return getListenersFromTree(vNode, inulaEvtName, inulaEvent, isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE); } // 按顺序执行事件队列 @@ -132,8 +132,8 @@ function processListeners(listenerList: ListenerUnitList): void { }); } -// 触发可以被执行的horizon事件监听 -function triggerHorizonEvents( +// 触发可以被执行的inula事件监听 +function triggerInulaEvents( nativeEvtName: string, isCapture: boolean, nativeEvent: AnyNativeEvent, @@ -145,7 +145,7 @@ function triggerHorizonEvents( const listenerList: ListenerUnitList = getCommonListeners(nativeEvtName, vNode, nativeEvent, target, isCapture); let mouseEnterListeners: ListenerUnitList = []; - if (horizonEventToNativeMap.get('onMouseEnter')!.includes(nativeEvtName)) { + if (inulaEventToNativeMap.get('onMouseEnter')!.includes(nativeEvtName)) { mouseEnterListeners = getMouseEnterListeners( nativeEvtName, vNode, @@ -156,7 +156,7 @@ function triggerHorizonEvents( let changeEvents: ListenerUnitList = []; // 触发特殊handler委托事件 - if (!isCapture && horizonEventToNativeMap.get('onChange')!.includes(nativeEvtName)) { + if (!isCapture && inulaEventToNativeMap.get('onChange')!.includes(nativeEvtName)) { changeEvents = getChangeListeners(nativeEvtName, nativeEvent, vNode, target); } @@ -185,14 +185,14 @@ export function handleEventMain( // 有事件正在执行,同步执行事件 if (isInEventsExecution) { - triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode); + triggerInulaEvents(nativeEvtName, isCapture, nativeEvent, startVNode); return; } // 没有事件在执行,经过调度再执行事件 isInEventsExecution = true; try { - asyncUpdates(() => triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode)); + asyncUpdates(() => triggerInulaEvents(nativeEvtName, isCapture, nativeEvent, startVNode)); } finally { isInEventsExecution = false; if (shouldControlValue()) { diff --git a/libs/horizon/src/event/ListenerGetter.ts b/libs/inula/src/event/ListenerGetter.ts similarity index 96% rename from libs/horizon/src/event/ListenerGetter.ts rename to libs/inula/src/event/ListenerGetter.ts index c6562d3c..2a387771 100644 --- a/libs/horizon/src/event/ListenerGetter.ts +++ b/libs/inula/src/event/ListenerGetter.ts @@ -40,11 +40,11 @@ function getListenerFromVNode(vNode: VNode, eventName: string): Function | null // 获取监听事件 export function getListenersFromTree( targetVNode: VNode | null, - horizonEvtName: string | null, + inulaEvtName: string | null, nativeEvent: WrappedEvent, eventType: string ): ListenerUnitList { - if (!horizonEvtName) { + if (!inulaEvtName) { return []; } @@ -57,7 +57,7 @@ export function getListenersFromTree( const { realNode, tag } = vNode; if (tag === DomComponent && realNode !== null) { if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_CAPTURE) { - const captureName = horizonEvtName + EVENT_TYPE_CAPTURE; + const captureName = inulaEvtName + EVENT_TYPE_CAPTURE; const captureListener = getListenerFromVNode(vNode, captureName); if (captureListener) { listeners.unshift({ @@ -70,7 +70,7 @@ export function getListenersFromTree( } if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_BUBBLE) { - const bubbleListener = getListenerFromVNode(vNode, horizonEvtName); + const bubbleListener = getListenerFromVNode(vNode, inulaEvtName); if (bubbleListener) { listeners.push({ vNode, diff --git a/libs/horizon/src/event/MouseEvent.ts b/libs/inula/src/event/MouseEvent.ts similarity index 100% rename from libs/horizon/src/event/MouseEvent.ts rename to libs/inula/src/event/MouseEvent.ts diff --git a/libs/horizon/src/event/Types.ts b/libs/inula/src/event/Types.ts similarity index 92% rename from libs/horizon/src/event/Types.ts rename to libs/inula/src/event/Types.ts index e4f839aa..4ef5125a 100644 --- a/libs/horizon/src/event/Types.ts +++ b/libs/inula/src/event/Types.ts @@ -18,13 +18,13 @@ import { WrappedEvent } from './EventWrapper'; export type AnyNativeEvent = KeyboardEvent | MouseEvent | TouchEvent | UIEvent | Event; -export interface HorizonEventListener { +export interface InulaEventListener { (event: WrappedEvent): void; } export type ListenerUnit = { vNode: null | VNode; - listener: HorizonEventListener; + listener: InulaEventListener; currentTarget: EventTarget; event: WrappedEvent; }; diff --git a/libs/horizon/src/event/utils.ts b/libs/inula/src/event/utils.ts similarity index 100% rename from libs/horizon/src/event/utils.ts rename to libs/inula/src/event/utils.ts diff --git a/libs/horizon/src/external/ChildrenUtil.ts b/libs/inula/src/external/ChildrenUtil.ts similarity index 96% rename from libs/horizon/src/external/ChildrenUtil.ts rename to libs/inula/src/external/ChildrenUtil.ts index 96e41bdc..64d56f28 100644 --- a/libs/horizon/src/external/ChildrenUtil.ts +++ b/libs/inula/src/external/ChildrenUtil.ts @@ -88,7 +88,7 @@ function mapChildrenToArray(children: any, arr: Array, prefix: string, call processArrayChildren(children, arr, prefix, callback); return; } - throw new Error('Object is invalid as a Horizon child. '); + throw new Error('Object is invalid as a Inula child. '); // No Default } } @@ -119,7 +119,7 @@ const Children = { return n; }, only: children => { - throwIfTrue(!isValidElement(children), 'Horizon.Children.only function received invalid element.'); + throwIfTrue(!isValidElement(children), 'Inula.Children.only function received invalid element.'); return children; }, toArray: children => { diff --git a/libs/horizon/src/external/HorizonIs.ts b/libs/inula/src/external/InulaIs.ts similarity index 100% rename from libs/horizon/src/external/HorizonIs.ts rename to libs/inula/src/external/InulaIs.ts diff --git a/libs/horizon/src/external/JSXElement.ts b/libs/inula/src/external/JSXElement.ts similarity index 100% rename from libs/horizon/src/external/JSXElement.ts rename to libs/inula/src/external/JSXElement.ts diff --git a/libs/horizon/src/external/JSXElementType.ts b/libs/inula/src/external/JSXElementType.ts similarity index 100% rename from libs/horizon/src/external/JSXElementType.ts rename to libs/inula/src/external/JSXElementType.ts diff --git a/libs/horizon/src/external/TestUtil.ts b/libs/inula/src/external/TestUtil.ts similarity index 100% rename from libs/horizon/src/external/TestUtil.ts rename to libs/inula/src/external/TestUtil.ts diff --git a/libs/horizon/src/external/devtools.ts b/libs/inula/src/external/devtools.ts similarity index 99% rename from libs/horizon/src/external/devtools.ts rename to libs/inula/src/external/devtools.ts index b1802cc7..21648f4b 100644 --- a/libs/horizon/src/external/devtools.ts +++ b/libs/inula/src/external/devtools.ts @@ -128,7 +128,7 @@ export const helper = { }; export function injectUpdater() { - const hook = window.__HORIZON_DEV_HOOK__; + const hook = window.__INULA_DEV_HOOK__; if (hook) { hook.init(helper); } diff --git a/libs/horizon/src/horizonx/CommonUtils.ts b/libs/inula/src/inulax/CommonUtils.ts similarity index 99% rename from libs/horizon/src/horizonx/CommonUtils.ts rename to libs/inula/src/inulax/CommonUtils.ts index 46f82ba5..c4330834 100644 --- a/libs/horizon/src/horizonx/CommonUtils.ts +++ b/libs/inula/src/inulax/CommonUtils.ts @@ -138,7 +138,7 @@ export function resolveMutation(from, to) { } } - let keys = Object.keys({ ...from, ...to }).filter(key => key !== '_horizonObserver'); + let keys = Object.keys({ ...from, ...to }).filter(key => key !== '_inulaObserver'); const res = {}; let found = false; keys.forEach(key => { diff --git a/libs/horizon/src/horizonx/Constants.ts b/libs/inula/src/inulax/Constants.ts similarity index 93% rename from libs/horizon/src/horizonx/Constants.ts rename to libs/inula/src/inulax/Constants.ts index c18ce664..1e841db7 100644 --- a/libs/horizon/src/horizonx/Constants.ts +++ b/libs/inula/src/inulax/Constants.ts @@ -13,6 +13,6 @@ * See the Mulan PSL v2 for more details. */ -export const OBSERVER_KEY = typeof Symbol === 'function' ? Symbol('_horizonObserver') : '_horizonObserver'; +export const OBSERVER_KEY = typeof Symbol === 'function' ? Symbol('_inulaObserver') : '_inulaObserver'; export const RAW_VALUE = '_rawValue'; diff --git a/libs/horizon/src/horizonx/adapters/redux.ts b/libs/inula/src/inulax/adapters/redux.ts similarity index 97% rename from libs/horizon/src/horizonx/adapters/redux.ts rename to libs/inula/src/inulax/adapters/redux.ts index 6930ecd9..a196a486 100644 --- a/libs/horizon/src/horizonx/adapters/redux.ts +++ b/libs/inula/src/inulax/adapters/redux.ts @@ -126,13 +126,13 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): replaceReducer: newReducer => { reducer = newReducer; }, - _horizonXstore: store, + _inulaXstore: store, dispatch: store.$a.dispatch, }; enhancers && enhancers(result); - result.dispatch({ type: 'HorizonX' }); + result.dispatch({ type: 'InulaX' }); store.reduxHandler = result; @@ -197,7 +197,7 @@ export function compose(...middlewares: ReduxMiddleware[]) { }; } -// HorizonX batches updates by default, this function is only for backwards compatibility +// InulaX batches updates by default, this function is only for backwards compatibility export function batch(fn: () => void) { fn(); } diff --git a/libs/horizon/src/horizonx/adapters/reduxReact.ts b/libs/inula/src/inulax/adapters/reduxReact.ts similarity index 98% rename from libs/horizon/src/horizonx/adapters/reduxReact.ts rename to libs/inula/src/inulax/adapters/reduxReact.ts index 31468796..d4dddbd3 100644 --- a/libs/horizon/src/horizonx/adapters/reduxReact.ts +++ b/libs/inula/src/inulax/adapters/reduxReact.ts @@ -30,7 +30,7 @@ export function Provider({ context: Context; children?: any[]; }) { - const Context = context; // NOTE: bind redux API to horizon API requires this renaming; + const Context = context; // NOTE: bind redux API to inula API requires this renaming; return createElement(Context.Provider, { value: store }, children); } diff --git a/libs/horizon/src/horizonx/adapters/reduxThunk.ts b/libs/inula/src/inulax/adapters/reduxThunk.ts similarity index 100% rename from libs/horizon/src/horizonx/adapters/reduxThunk.ts rename to libs/inula/src/inulax/adapters/reduxThunk.ts diff --git a/libs/inula/src/inulax/devtools/constants.ts b/libs/inula/src/inulax/devtools/constants.ts new file mode 100644 index 00000000..f6478862 --- /dev/null +++ b/libs/inula/src/inulax/devtools/constants.ts @@ -0,0 +1,10 @@ +export const INITIALIZED = 'inulax store initialized'; +export const STATE_CHANGE = 'inulax state change'; +export const SUBSCRIBED = 'inulax subscribed'; +export const UNSUBSCRIBED = 'inulax unsubscribed'; +export const ACTION = 'inulax action'; +export const ACTION_QUEUED = 'inulax action queued'; +export const QUEUE_PENDING = 'inulax queue pending'; +export const QUEUE_FINISHED = 'inulax queue finished'; +export const RENDER_TRIGGERED = 'inulax render triggered'; +export const OBSERVED_COMPONENTS = 'inulax observed components'; diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/inula/src/inulax/devtools/index.ts similarity index 84% rename from libs/horizon/src/horizonx/devtools/index.ts rename to libs/inula/src/inulax/devtools/index.ts index 711c0d62..e7b51b78 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/inula/src/inulax/devtools/index.ts @@ -7,7 +7,7 @@ const sessionId = Date.now(); // this function is used to detect devtool connection export function isPanelActive() { - return window['__HORIZON_DEV_HOOK__']; + return window['__INULA_DEV_HOOK__']; } // safely serializes variables containing values wrapped in Proxy object @@ -44,7 +44,7 @@ function makeProxySnapshot(obj, visited: any[] = []) { if (type === 'vnode') { return { _type: 'VNode', - id: window['__HORIZON_DEV_HOOK__'].getVnodeId(obj), + id: window['__INULA_DEV_HOOK__'].getVnodeId(obj), tag: obj.tag, }; } @@ -114,39 +114,39 @@ function makeStoreSnapshot({ type, data }) { } export const devtools = { - // returns vNode id from horizon devtools + // returns vNode id from inula devtools getVNodeId: vNode => { if (!isPanelActive()) { return null; } - window['__HORIZON_DEV_HOOK__'].send(); // update list first - return window['__HORIZON_DEV_HOOK__'].getVnodeId(vNode); + window['__INULA_DEV_HOOK__'].send(); // update list first + return window['__INULA_DEV_HOOK__'].getVnodeId(vNode); }, - // sends horizonx devtool message to extension + // sends inulax devtool message to extension emit: (type, data) => { if (!isPanelActive()) { return; } window.postMessage({ - type: 'HORIZON_DEV_TOOLS', + type: 'INULA_DEV_TOOLS', payload: makeStoreSnapshot({ type, data }), from: 'dev tool hook', }, ''); }, }; -// collects components that are dependant on horizonx store and their ids +// collects components that are dependant on inulax store and their ids function getAffectedComponents() { const allStores = getAllStores(); const keys = Object.keys(allStores); const res = {}; keys.forEach(key => { - if (!allStores[key].$config.state._horizonObserver.keyVNodes) { + if (!allStores[key].$config.state._inulaObserver.keyVNodes) { res[key] = []; return; } const subRes = new Set(); - const process = Array.from(allStores[key].$config.state._horizonObserver.keyVNodes.values()); + const process = Array.from(allStores[key].$config.state._inulaObserver.keyVNodes.values()); while (process.length) { const pivot = process.shift() as { tag: 'string' }; if (pivot.tag) subRes.add(pivot); @@ -158,7 +158,7 @@ function getAffectedComponents() { .toString() .replace(/\{.*\}/, '{...}') .replace('function ', ''), - nodeId: window.__HORIZON_DEV_HOOK__.getVnodeId(vNode), + nodeId: window.__INULA_DEV_HOOK__.getVnodeId(vNode), }; }); }); @@ -168,11 +168,11 @@ function getAffectedComponents() { // listens to messages from background window.addEventListener('message', (messageEvent?) => { - if (messageEvent?.data?.payload?.type === 'horizonx request observed components') { + if (messageEvent?.data?.payload?.type === 'inulax request observed components') { // get observed components setTimeout(() => { window.postMessage({ - type: 'HORIZON_DEV_TOOLS', + type: 'INULA_DEV_TOOLS', payload: { type: OBSERVED_COMPONENTS, data: getAffectedComponents() }, from: 'dev tool hook', }, ''); @@ -180,7 +180,7 @@ window.addEventListener('message', (messageEvent?) => { } // executes store action - if (messageEvent.data?.payload?.type === 'horizonx executue action') { + if (messageEvent.data?.payload?.type === 'inulax executue action') { const data = messageEvent.data.payload.data; const store = getStore(data.storeId); if (!store?.[data.action]) return; @@ -191,7 +191,7 @@ window.addEventListener('message', (messageEvent?) => { } // queues store action - if (messageEvent?.data?.payload?.type === 'horizonx queue action') { + if (messageEvent?.data?.payload?.type === 'inulax queue action') { const data = messageEvent.data.payload.data; const store = getStore(data.storeId); if (!store?.[data.action]) return; @@ -202,7 +202,7 @@ window.addEventListener('message', (messageEvent?) => { } // queues change store state - if (messageEvent?.data?.payload?.type === 'horizonx change state') { + if (messageEvent?.data?.payload?.type === 'inulax change state') { const data = messageEvent.data.payload; const store = getStore(data.storeId); if (!store) return; diff --git a/libs/horizon/src/horizonx/proxy/HooklessObserver.ts b/libs/inula/src/inulax/proxy/HooklessObserver.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/HooklessObserver.ts rename to libs/inula/src/inulax/proxy/HooklessObserver.ts diff --git a/libs/horizon/src/horizonx/proxy/Observer.ts b/libs/inula/src/inulax/proxy/Observer.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/Observer.ts rename to libs/inula/src/inulax/proxy/Observer.ts diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/inula/src/inulax/proxy/ProxyHandler.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/ProxyHandler.ts rename to libs/inula/src/inulax/proxy/ProxyHandler.ts diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/inula/src/inulax/proxy/handlers/ArrayProxyHandler.ts similarity index 97% rename from libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts rename to libs/inula/src/inulax/proxy/handlers/ArrayProxyHandler.ts index ce6c73c3..0907f33b 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/inula/src/inulax/proxy/handlers/ArrayProxyHandler.ts @@ -57,7 +57,7 @@ export function createArrayProxy(rawObj: any[], listener: { current: (...args) = let listeners = [] as ((...args) => void)[]; function objectGet(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any { - // The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. + // The observer object of symbol ('_inulaObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. if (key === OBSERVER_KEY) { return undefined; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts b/libs/inula/src/inulax/proxy/handlers/CollectionProxyHandler.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/handlers/CollectionProxyHandler.ts rename to libs/inula/src/inulax/proxy/handlers/CollectionProxyHandler.ts diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/inula/src/inulax/proxy/handlers/MapProxy.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts rename to libs/inula/src/inulax/proxy/handlers/MapProxy.ts diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/inula/src/inulax/proxy/handlers/ObjectProxyHandler.ts similarity index 96% rename from libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts rename to libs/inula/src/inulax/proxy/handlers/ObjectProxyHandler.ts index 88a0c93b..c9e5521d 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/inula/src/inulax/proxy/handlers/ObjectProxyHandler.ts @@ -47,7 +47,7 @@ export function createObjectProxy( let listeners = [] as ((...args) => void)[]; function get(rawObj: object, key: string | symbol, receiver: any): any { - // The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. + // The observer object of symbol ('_inulaObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep. if (key === OBSERVER_KEY) { return undefined; } diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/inula/src/inulax/proxy/handlers/SetProxy.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts rename to libs/inula/src/inulax/proxy/handlers/SetProxy.ts diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts b/libs/inula/src/inulax/proxy/handlers/WeakMapProxy.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/handlers/WeakMapProxy.ts rename to libs/inula/src/inulax/proxy/handlers/WeakMapProxy.ts diff --git a/libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts b/libs/inula/src/inulax/proxy/handlers/WeakSetProxy.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/handlers/WeakSetProxy.ts rename to libs/inula/src/inulax/proxy/handlers/WeakSetProxy.ts diff --git a/libs/horizon/src/horizonx/proxy/readonlyProxy.ts b/libs/inula/src/inulax/proxy/readonlyProxy.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/readonlyProxy.ts rename to libs/inula/src/inulax/proxy/readonlyProxy.ts diff --git a/libs/horizon/src/horizonx/proxy/watch.ts b/libs/inula/src/inulax/proxy/watch.ts similarity index 100% rename from libs/horizon/src/horizonx/proxy/watch.ts rename to libs/inula/src/inulax/proxy/watch.ts diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/inula/src/inulax/store/StoreHandler.ts similarity index 100% rename from libs/horizon/src/horizonx/store/StoreHandler.ts rename to libs/inula/src/inulax/store/StoreHandler.ts diff --git a/libs/horizon/src/horizonx/types.d.ts b/libs/inula/src/inulax/types.d.ts similarity index 100% rename from libs/horizon/src/horizonx/types.d.ts rename to libs/inula/src/inulax/types.d.ts diff --git a/libs/horizon/src/renderer/ContextSaver.ts b/libs/inula/src/renderer/ContextSaver.ts similarity index 100% rename from libs/horizon/src/renderer/ContextSaver.ts rename to libs/inula/src/renderer/ContextSaver.ts diff --git a/libs/horizon/src/renderer/ErrorHandler.ts b/libs/inula/src/renderer/ErrorHandler.ts similarity index 100% rename from libs/horizon/src/renderer/ErrorHandler.ts rename to libs/inula/src/renderer/ErrorHandler.ts diff --git a/libs/horizon/src/renderer/ExecuteMode.ts b/libs/inula/src/renderer/ExecuteMode.ts similarity index 100% rename from libs/horizon/src/renderer/ExecuteMode.ts rename to libs/inula/src/renderer/ExecuteMode.ts diff --git a/libs/horizon/src/renderer/GlobalVar.ts b/libs/inula/src/renderer/GlobalVar.ts similarity index 100% rename from libs/horizon/src/renderer/GlobalVar.ts rename to libs/inula/src/renderer/GlobalVar.ts diff --git a/libs/horizon/src/renderer/Renderer.ts b/libs/inula/src/renderer/Renderer.ts similarity index 100% rename from libs/horizon/src/renderer/Renderer.ts rename to libs/inula/src/renderer/Renderer.ts diff --git a/libs/horizon/src/renderer/RootStack.ts b/libs/inula/src/renderer/RootStack.ts similarity index 100% rename from libs/horizon/src/renderer/RootStack.ts rename to libs/inula/src/renderer/RootStack.ts diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/inula/src/renderer/TreeBuilder.ts similarity index 96% rename from libs/horizon/src/renderer/TreeBuilder.ts rename to libs/inula/src/renderer/TreeBuilder.ts index 1c3f4202..f079b896 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/inula/src/renderer/TreeBuilder.ts @@ -355,10 +355,10 @@ function renderFromRoot(treeRoot) { submitToRender(treeRoot); popCurrentRoot(); - if (window.__HORIZON_DEV_HOOK__) { - const hook = window.__HORIZON_DEV_HOOK__; - // injector.js 可能在 Horizon 代码之后加载,此时无 __HORIZON_DEV_HOOK__ 全局变量 - // Horizon 代码初次加载时不会初始化 helper + if (window.__INULA_DEV_HOOK__) { + const hook = window.__INULA_DEV_HOOK__; + // injector.js 可能在 Inula 代码之后加载,此时无 __INULA_DEV_HOOK__ 全局变量 + // Inula 代码初次加载时不会初始化 helper if (!hook.isInit) { injectUpdater(); } @@ -398,7 +398,7 @@ export function launchUpdateFromVNode(vNode: VNode) { ) { // 不是渲染阶段触发 - // 业务直接调用Horizon.render的时候会进入这个分支,同步渲染。 + // 业务直接调用Inula.render的时候会进入这个分支,同步渲染。 // 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。 renderFromRoot(treeRoot); } else { @@ -411,7 +411,7 @@ export function launchUpdateFromVNode(vNode: VNode) { } } -// ============================== HorizonDOM使用 ============================== +// ============================== InulaDOM使用 ============================== export function runDiscreteUpdates() { if (checkMode(ByAsync) || checkMode(InRender)) { // 已经渲染,不能再同步执行待工作的任务,有可能是被生命周期或effect触发的事件导致的,如el.focus() diff --git a/libs/horizon/src/renderer/Types.ts b/libs/inula/src/renderer/Types.ts similarity index 100% rename from libs/horizon/src/renderer/Types.ts rename to libs/inula/src/renderer/Types.ts diff --git a/libs/horizon/src/renderer/UpdateHandler.ts b/libs/inula/src/renderer/UpdateHandler.ts similarity index 95% rename from libs/horizon/src/renderer/UpdateHandler.ts rename to libs/inula/src/renderer/UpdateHandler.ts index 7febd61e..21f40299 100644 --- a/libs/horizon/src/renderer/UpdateHandler.ts +++ b/libs/inula/src/renderer/UpdateHandler.ts @@ -35,8 +35,8 @@ export enum UpdateState { export function newUpdate(): Update { return { type: UpdateState.Update, // 更新的类型 - content: null, // ClassComponent的content是setState第一个参数,TreeRoot的content是HorizonDOM.render的第一个参数 - callback: null, // setState的第二个参数,HorizonDOM.render的第三个参数 + content: null, // ClassComponent的content是setState第一个参数,TreeRoot的content是InulaDOM.render的第一个参数 + callback: null, // setState的第二个参数,InulaDOM.render的第三个参数 }; } diff --git a/libs/horizon/src/renderer/components/BaseClassComponent.ts b/libs/inula/src/renderer/components/BaseClassComponent.ts similarity index 100% rename from libs/horizon/src/renderer/components/BaseClassComponent.ts rename to libs/inula/src/renderer/components/BaseClassComponent.ts diff --git a/libs/horizon/src/renderer/components/CreatePortal.ts b/libs/inula/src/renderer/components/CreatePortal.ts similarity index 100% rename from libs/horizon/src/renderer/components/CreatePortal.ts rename to libs/inula/src/renderer/components/CreatePortal.ts diff --git a/libs/horizon/src/renderer/components/CreateRef.ts b/libs/inula/src/renderer/components/CreateRef.ts similarity index 100% rename from libs/horizon/src/renderer/components/CreateRef.ts rename to libs/inula/src/renderer/components/CreateRef.ts diff --git a/libs/horizon/src/renderer/components/ForwardRef.ts b/libs/inula/src/renderer/components/ForwardRef.ts similarity index 100% rename from libs/horizon/src/renderer/components/ForwardRef.ts rename to libs/inula/src/renderer/components/ForwardRef.ts diff --git a/libs/horizon/src/renderer/components/Lazy.ts b/libs/inula/src/renderer/components/Lazy.ts similarity index 100% rename from libs/horizon/src/renderer/components/Lazy.ts rename to libs/inula/src/renderer/components/Lazy.ts diff --git a/libs/horizon/src/renderer/components/Memo.ts b/libs/inula/src/renderer/components/Memo.ts similarity index 100% rename from libs/horizon/src/renderer/components/Memo.ts rename to libs/inula/src/renderer/components/Memo.ts diff --git a/libs/horizon/src/renderer/components/context/Context.ts b/libs/inula/src/renderer/components/context/Context.ts similarity index 100% rename from libs/horizon/src/renderer/components/context/Context.ts rename to libs/inula/src/renderer/components/context/Context.ts diff --git a/libs/horizon/src/renderer/components/context/CreateContext.ts b/libs/inula/src/renderer/components/context/CreateContext.ts similarity index 100% rename from libs/horizon/src/renderer/components/context/CreateContext.ts rename to libs/inula/src/renderer/components/context/CreateContext.ts diff --git a/libs/horizon/src/renderer/diff/DiffTools.ts b/libs/inula/src/renderer/diff/DiffTools.ts similarity index 100% rename from libs/horizon/src/renderer/diff/DiffTools.ts rename to libs/inula/src/renderer/diff/DiffTools.ts diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/inula/src/renderer/diff/nodeDiffComparator.ts similarity index 100% rename from libs/horizon/src/renderer/diff/nodeDiffComparator.ts rename to libs/inula/src/renderer/diff/nodeDiffComparator.ts diff --git a/libs/horizon/src/renderer/hooks/BaseHook.ts b/libs/inula/src/renderer/hooks/BaseHook.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/BaseHook.ts rename to libs/inula/src/renderer/hooks/BaseHook.ts diff --git a/libs/horizon/src/renderer/hooks/EffectConstant.js b/libs/inula/src/renderer/hooks/EffectConstant.js similarity index 100% rename from libs/horizon/src/renderer/hooks/EffectConstant.js rename to libs/inula/src/renderer/hooks/EffectConstant.js diff --git a/libs/horizon/src/renderer/hooks/HookExternal.ts b/libs/inula/src/renderer/hooks/HookExternal.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/HookExternal.ts rename to libs/inula/src/renderer/hooks/HookExternal.ts diff --git a/libs/horizon/src/renderer/hooks/HookMain.ts b/libs/inula/src/renderer/hooks/HookMain.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/HookMain.ts rename to libs/inula/src/renderer/hooks/HookMain.ts diff --git a/libs/horizon/src/renderer/hooks/HookStage.ts b/libs/inula/src/renderer/hooks/HookStage.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/HookStage.ts rename to libs/inula/src/renderer/hooks/HookStage.ts diff --git a/libs/horizon/src/renderer/hooks/HookType.ts b/libs/inula/src/renderer/hooks/HookType.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/HookType.ts rename to libs/inula/src/renderer/hooks/HookType.ts diff --git a/libs/horizon/src/renderer/hooks/UseCallbackHook.ts b/libs/inula/src/renderer/hooks/UseCallbackHook.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/UseCallbackHook.ts rename to libs/inula/src/renderer/hooks/UseCallbackHook.ts diff --git a/libs/horizon/src/renderer/hooks/UseEffectHook.ts b/libs/inula/src/renderer/hooks/UseEffectHook.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/UseEffectHook.ts rename to libs/inula/src/renderer/hooks/UseEffectHook.ts diff --git a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts b/libs/inula/src/renderer/hooks/UseImperativeHook.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/UseImperativeHook.ts rename to libs/inula/src/renderer/hooks/UseImperativeHook.ts diff --git a/libs/horizon/src/renderer/hooks/UseMemoHook.ts b/libs/inula/src/renderer/hooks/UseMemoHook.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/UseMemoHook.ts rename to libs/inula/src/renderer/hooks/UseMemoHook.ts diff --git a/libs/horizon/src/renderer/hooks/UseReducerHook.ts b/libs/inula/src/renderer/hooks/UseReducerHook.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/UseReducerHook.ts rename to libs/inula/src/renderer/hooks/UseReducerHook.ts diff --git a/libs/horizon/src/renderer/hooks/UseRefHook.ts b/libs/inula/src/renderer/hooks/UseRefHook.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/UseRefHook.ts rename to libs/inula/src/renderer/hooks/UseRefHook.ts diff --git a/libs/horizon/src/renderer/hooks/UseStateHook.ts b/libs/inula/src/renderer/hooks/UseStateHook.ts similarity index 100% rename from libs/horizon/src/renderer/hooks/UseStateHook.ts rename to libs/inula/src/renderer/hooks/UseStateHook.ts diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/inula/src/renderer/render/BaseComponent.ts similarity index 97% rename from libs/horizon/src/renderer/render/BaseComponent.ts rename to libs/inula/src/renderer/render/BaseComponent.ts index 7c86a0ca..e3b08455 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/inula/src/renderer/render/BaseComponent.ts @@ -21,7 +21,7 @@ import { FlagUtils } from '../vnode/VNodeFlags'; import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator'; import componentRenders from './index'; import { setProcessingVNode } from '../GlobalVar'; -import { clearVNodeObservers } from '../../horizonx/store/StoreHandler'; +import { clearVNodeObservers } from '../../inulax/store/StoreHandler'; import { pushCurrentRoot } from '../RootStack'; // 复用vNode时,也需对树的上下文值处理,如context,portal, namespaceContext diff --git a/libs/horizon/src/renderer/render/ClassComponent.ts b/libs/inula/src/renderer/render/ClassComponent.ts similarity index 100% rename from libs/horizon/src/renderer/render/ClassComponent.ts rename to libs/inula/src/renderer/render/ClassComponent.ts diff --git a/libs/horizon/src/renderer/render/ContextConsumer.ts b/libs/inula/src/renderer/render/ContextConsumer.ts similarity index 100% rename from libs/horizon/src/renderer/render/ContextConsumer.ts rename to libs/inula/src/renderer/render/ContextConsumer.ts diff --git a/libs/horizon/src/renderer/render/ContextProvider.ts b/libs/inula/src/renderer/render/ContextProvider.ts similarity index 100% rename from libs/horizon/src/renderer/render/ContextProvider.ts rename to libs/inula/src/renderer/render/ContextProvider.ts diff --git a/libs/horizon/src/renderer/render/DomComponent.ts b/libs/inula/src/renderer/render/DomComponent.ts similarity index 100% rename from libs/horizon/src/renderer/render/DomComponent.ts rename to libs/inula/src/renderer/render/DomComponent.ts diff --git a/libs/horizon/src/renderer/render/DomPortal.ts b/libs/inula/src/renderer/render/DomPortal.ts similarity index 100% rename from libs/horizon/src/renderer/render/DomPortal.ts rename to libs/inula/src/renderer/render/DomPortal.ts diff --git a/libs/horizon/src/renderer/render/DomText.ts b/libs/inula/src/renderer/render/DomText.ts similarity index 96% rename from libs/horizon/src/renderer/render/DomText.ts rename to libs/inula/src/renderer/render/DomText.ts index 213f9f8b..8e90e88b 100644 --- a/libs/horizon/src/renderer/render/DomText.ts +++ b/libs/inula/src/renderer/render/DomText.ts @@ -41,7 +41,7 @@ export function bubbleRender(processing: VNode) { throwIfTrue( processing.realNode === null, 'We must have new text for new mounted node. This error is likely ' + - 'caused by a bug in Horizon. Please file an issue.' + 'caused by a bug in Inula. Please file an issue.' ); } // 获得对应节点 diff --git a/libs/horizon/src/renderer/render/ForwardRef.ts b/libs/inula/src/renderer/render/ForwardRef.ts similarity index 100% rename from libs/horizon/src/renderer/render/ForwardRef.ts rename to libs/inula/src/renderer/render/ForwardRef.ts diff --git a/libs/horizon/src/renderer/render/Fragment.ts b/libs/inula/src/renderer/render/Fragment.ts similarity index 100% rename from libs/horizon/src/renderer/render/Fragment.ts rename to libs/inula/src/renderer/render/Fragment.ts diff --git a/libs/horizon/src/renderer/render/FunctionComponent.ts b/libs/inula/src/renderer/render/FunctionComponent.ts similarity index 100% rename from libs/horizon/src/renderer/render/FunctionComponent.ts rename to libs/inula/src/renderer/render/FunctionComponent.ts diff --git a/libs/horizon/src/renderer/render/LazyComponent.ts b/libs/inula/src/renderer/render/LazyComponent.ts similarity index 100% rename from libs/horizon/src/renderer/render/LazyComponent.ts rename to libs/inula/src/renderer/render/LazyComponent.ts diff --git a/libs/horizon/src/renderer/render/MemoComponent.ts b/libs/inula/src/renderer/render/MemoComponent.ts similarity index 97% rename from libs/horizon/src/renderer/render/MemoComponent.ts rename to libs/inula/src/renderer/render/MemoComponent.ts index 95ebb64c..a229093c 100644 --- a/libs/horizon/src/renderer/render/MemoComponent.ts +++ b/libs/inula/src/renderer/render/MemoComponent.ts @@ -32,7 +32,7 @@ export function captureMemoComponent(processing: VNode, shouldUpdate: boolean): const Component = processing.type; // 合并 函数组件或类组件 的defaultProps let newProps = mergeDefaultProps(Component, processing.props); - // 解决Horizon.memo(Horizon.forwardRef(()=>{}))两层包装的场景 + // 解决Inula.memo(Inula.forwardRef(()=>{}))两层包装的场景 newProps = mergeDefaultProps(Component.type, newProps); if (processing.isCreated) { diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/inula/src/renderer/render/SuspenseComponent.ts similarity index 100% rename from libs/horizon/src/renderer/render/SuspenseComponent.ts rename to libs/inula/src/renderer/render/SuspenseComponent.ts diff --git a/libs/horizon/src/renderer/render/TreeRoot.ts b/libs/inula/src/renderer/render/TreeRoot.ts similarity index 100% rename from libs/horizon/src/renderer/render/TreeRoot.ts rename to libs/inula/src/renderer/render/TreeRoot.ts diff --git a/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts b/libs/inula/src/renderer/render/class/ClassLifeCycleProcessor.ts similarity index 100% rename from libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts rename to libs/inula/src/renderer/render/class/ClassLifeCycleProcessor.ts diff --git a/libs/horizon/src/renderer/render/index.ts b/libs/inula/src/renderer/render/index.ts similarity index 100% rename from libs/horizon/src/renderer/render/index.ts rename to libs/inula/src/renderer/render/index.ts diff --git a/libs/horizon/src/renderer/submit/HookEffectHandler.ts b/libs/inula/src/renderer/submit/HookEffectHandler.ts similarity index 100% rename from libs/horizon/src/renderer/submit/HookEffectHandler.ts rename to libs/inula/src/renderer/submit/HookEffectHandler.ts diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/inula/src/renderer/submit/LifeCycleHandler.ts similarity index 98% rename from libs/horizon/src/renderer/submit/LifeCycleHandler.ts rename to libs/inula/src/renderer/submit/LifeCycleHandler.ts index d405b73e..35aaa7ba 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/inula/src/renderer/submit/LifeCycleHandler.ts @@ -268,7 +268,7 @@ function unmountVNode(vNode: VNode): void { callComponentWillUnmount(vNode, instance); } - // HorizonX会在classComponentWillUnmount中清除对VNode的引入用 + // InulaX会在classComponentWillUnmount中清除对VNode的引入用 if (vNode.classComponentWillUnmount) { vNode.classComponentWillUnmount(vNode); vNode.classComponentWillUnmount = null; @@ -345,9 +345,9 @@ function submitAddition(vNode: VNode): void { function submitClear(vNode: VNode): void { const realNode = vNode.realNode; - const cloneDom = realNode.cloneNode(false); // 复制节点后horizon添加给dom的属性未能复制 + const cloneDom = realNode.cloneNode(false); // 复制节点后inula添加给dom的属性未能复制 // 真实 dom 获取的keys只包含新增的属性 - // 比如真实 dom 拿到的 keys 一般只有两个 horizon 自定义属性 + // 比如真实 dom 拿到的 keys 一般只有两个 inula 自定义属性 // 但考虑到用户可能自定义其他属性,所以采用遍历赋值的方式 const customizeKeys = Object.keys(realNode); const keyLength = customizeKeys.length; diff --git a/libs/horizon/src/renderer/submit/Submit.ts b/libs/inula/src/renderer/submit/Submit.ts similarity index 100% rename from libs/horizon/src/renderer/submit/Submit.ts rename to libs/inula/src/renderer/submit/Submit.ts diff --git a/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts b/libs/inula/src/renderer/taskExecutor/BrowserAsync.ts similarity index 100% rename from libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts rename to libs/inula/src/renderer/taskExecutor/BrowserAsync.ts diff --git a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts b/libs/inula/src/renderer/taskExecutor/RenderQueue.ts similarity index 100% rename from libs/horizon/src/renderer/taskExecutor/RenderQueue.ts rename to libs/inula/src/renderer/taskExecutor/RenderQueue.ts diff --git a/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts b/libs/inula/src/renderer/taskExecutor/TaskExecutor.ts similarity index 100% rename from libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts rename to libs/inula/src/renderer/taskExecutor/TaskExecutor.ts diff --git a/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts b/libs/inula/src/renderer/taskExecutor/TaskQueue.ts similarity index 100% rename from libs/horizon/src/renderer/taskExecutor/TaskQueue.ts rename to libs/inula/src/renderer/taskExecutor/TaskQueue.ts diff --git a/libs/horizon/src/renderer/utils/compare.ts b/libs/inula/src/renderer/utils/compare.ts similarity index 100% rename from libs/horizon/src/renderer/utils/compare.ts rename to libs/inula/src/renderer/utils/compare.ts diff --git a/libs/horizon/src/renderer/utils/throwIfTrue.ts b/libs/inula/src/renderer/utils/throwIfTrue.ts similarity index 100% rename from libs/horizon/src/renderer/utils/throwIfTrue.ts rename to libs/inula/src/renderer/utils/throwIfTrue.ts diff --git a/libs/horizon/src/renderer/utils/vNodePath.ts b/libs/inula/src/renderer/utils/vNodePath.ts similarity index 100% rename from libs/horizon/src/renderer/utils/vNodePath.ts rename to libs/inula/src/renderer/utils/vNodePath.ts diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/inula/src/renderer/vnode/VNode.ts similarity index 94% rename from libs/horizon/src/renderer/vnode/VNode.ts rename to libs/inula/src/renderer/vnode/VNode.ts index b383e62a..da1b59aa 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/inula/src/renderer/vnode/VNode.ts @@ -36,7 +36,7 @@ import type { VNodeTag } from './VNodeTags'; import type { RefType, ContextType, SuspenseState, Source } from '../Types'; import type { Hook } from '../hooks/HookType'; import { InitFlag } from './VNodeFlags'; -import { Observer } from '../../horizonx/proxy/Observer'; +import { Observer } from '../../inulax/proxy/Observer'; export const BELONG_CLASS_VNODE_KEY = typeof Symbol === 'function' ? Symbol('belongClassVNode') : 'belongClassVNode'; @@ -52,7 +52,7 @@ export class VNode { child: VNode | null = null; // 子节点 next: VNode | null = null; // 兄弟节点 cIndex = 0; // 节点在children数组中的位置 - eIndex = 0; // HorizonElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致 + eIndex = 0; // InulaElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致 ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数,submit阶段使用,比如将外部useRef生成的对象赋值到ref上 oldProps: any = null; @@ -62,7 +62,7 @@ export class VNode { changeList: any; // DOM的变更列表 effectList: any[] | null; // useEffect 的更新数组 updates: any[] | null; // TreeRoot和ClassComponent使用的更新数组 - stateCallbacks: any[] | null; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组 + stateCallbacks: any[] | null; // 存放存在setState的第二个参数和InulaDOM.render的第三个参数所在的node数组 isForceUpdate: boolean; // 是否使用强制更新 isSuspended = false; // 是否被suspense打断更新 state: any; // ClassComponent和TreeRoot的状态 @@ -101,10 +101,10 @@ export class VNode { [BELONG_CLASS_VNODE_KEY]: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用 - // 状态管理器HorizonX使用 + // 状态管理器InulaX使用 isStoreChange: boolean; observers: Set | null = null; // 记录这个函数组件/类组件依赖哪些Observer - classComponentWillUnmount: ((vNode: VNode) => any) | null; // HorizonX会在classComponentWillUnmount中清除对VNode的引入用 + classComponentWillUnmount: ((vNode: VNode) => any) | null; // InulaX会在classComponentWillUnmount中清除对VNode的引入用 src: Source | null; // 节点所在代码位置 constructor(tag: VNodeTag, props: any, key: null | string, realNode) { diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/inula/src/renderer/vnode/VNodeCreator.ts similarity index 99% rename from libs/horizon/src/renderer/vnode/VNodeCreator.ts rename to libs/inula/src/renderer/vnode/VNodeCreator.ts index 5b3db8ae..31cf3d19 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/inula/src/renderer/vnode/VNodeCreator.ts @@ -74,7 +74,7 @@ export function getLazyVNodeTag(lazyComp: any): string { } else if (lazyComp !== undefined && lazyComp !== null && typeLazyMap[lazyComp.vtype]) { return typeLazyMap[lazyComp.vtype]; } - throw Error("Horizon can't resolve the content of lazy"); + throw Error("Inula can't resolve the content of lazy"); } // 创建processing diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/inula/src/renderer/vnode/VNodeFlags.ts similarity index 100% rename from libs/horizon/src/renderer/vnode/VNodeFlags.ts rename to libs/inula/src/renderer/vnode/VNodeFlags.ts diff --git a/libs/horizon/src/renderer/vnode/VNodeShouldUpdate.ts b/libs/inula/src/renderer/vnode/VNodeShouldUpdate.ts similarity index 100% rename from libs/horizon/src/renderer/vnode/VNodeShouldUpdate.ts rename to libs/inula/src/renderer/vnode/VNodeShouldUpdate.ts diff --git a/libs/horizon/src/renderer/vnode/VNodeTags.ts b/libs/inula/src/renderer/vnode/VNodeTags.ts similarity index 100% rename from libs/horizon/src/renderer/vnode/VNodeTags.ts rename to libs/inula/src/renderer/vnode/VNodeTags.ts diff --git a/libs/horizon/src/renderer/vnode/VNodeUtils.ts b/libs/inula/src/renderer/vnode/VNodeUtils.ts similarity index 98% rename from libs/horizon/src/renderer/vnode/VNodeUtils.ts rename to libs/inula/src/renderer/vnode/VNodeUtils.ts index 952b9541..7f960098 100644 --- a/libs/horizon/src/renderer/vnode/VNodeUtils.ts +++ b/libs/inula/src/renderer/vnode/VNodeUtils.ts @@ -125,8 +125,8 @@ export function clearVNode(vNode: VNode) { vNode.toUpdateNodes = null; vNode[BELONG_CLASS_VNODE_KEY] = null; - if (window.__HORIZON_DEV_HOOK__) { - const hook = window.__HORIZON_DEV_HOOK__; + if (window.__INULA_DEV_HOOK__) { + const hook = window.__INULA_DEV_HOOK__; hook.deleteVNode(vNode); } } diff --git a/package.json b/package.json index 22d01c6d..d2c20795 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "horizon-core", - "description": "Horizon is a JavaScript framework library.", + "name": "inulajs", + "description": "InulaJS is a JavaScript framework library.", "version": "0.0.52", "private": true, "workspaces": [ @@ -11,8 +11,8 @@ "prettier": "prettier -w libs/**/*.ts", "build": "rollup --config ./scripts/rollup/rollup.config.js", "build:watch": "rollup --watch --config ./scripts/rollup/rollup.config.js", - "build:horizon3rdLib-dev": "npm run build & node ./scripts/gen3rdLib.js build:horizon3rdLib-dev", - "build-types": "tsc -p ./libs/horizon/index.ts --emitDeclarationOnly --declaration --declarationDir ./build/horizon/@types --skipLibCheck || echo \"WARNING: TSC exited with status $?\"", + "build:inula3rdLib-dev": "npm run build & node ./scripts/gen3rdLib.js build:inula3rdLib-dev", + "build-types": "tsc -p libs/inula/index.ts --emitDeclarationOnly --declaration --declarationDir ./build/inula/@types --skipLibCheck || echo \\\"WARNING: TSC exited with status $?\\\"", "debug-test": "yarn test --debug", "test": "jest --config=jest.config.js", "watch-test": "yarn test --watch --dev" diff --git a/scripts/__tests__/ActTest/act.test.js b/scripts/__tests__/ActTest/act.test.js index 00f719ca..e0768094 100644 --- a/scripts/__tests__/ActTest/act.test.js +++ b/scripts/__tests__/ActTest/act.test.js @@ -13,9 +13,9 @@ * See the Mulan PSL v2 for more details. */ -import Horizon, { render, useState, act, useEffect } from '@cloudsop/horizon/index.ts'; +import Inula, { render, useState, act, useEffect } from '../../../libs/inula/index'; -describe('Horizon.act function Test', () => { +describe('Inula.act function Test', () => { it('The act can wait for the useEffect update to complete.', function () { const Parent = props => { const [buttonOptions, setBtn] = useState([]); diff --git a/scripts/__tests__/ComponentTest/ClassRefs.test.js b/scripts/__tests__/ComponentTest/ClassRefs.test.js index d5e91d29..c898e069 100644 --- a/scripts/__tests__/ComponentTest/ClassRefs.test.js +++ b/scripts/__tests__/ComponentTest/ClassRefs.test.js @@ -13,13 +13,13 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; describe('Class refs Test', () => { it('Parent can get Child instance by refs', function () { let pInst; - class Parent extends Horizon.Component { + class Parent extends Inula.Component { componentDidMount() { pInst = this; } @@ -35,7 +35,7 @@ describe('Class refs Test', () => { } } - class Child extends Horizon.Component { + class Child extends Inula.Component { state = { y: 0 }; render() { @@ -43,7 +43,7 @@ describe('Class refs Test', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(pInst.refs['child'].state.y).toEqual(0); expect(pInst.refs['childDiv'].innerHTML).toEqual('childDiv'); diff --git a/scripts/__tests__/ComponentTest/ComponentError.test.js b/scripts/__tests__/ComponentTest/ComponentError.test.js index aa25bcf0..3a16c00b 100755 --- a/scripts/__tests__/ComponentTest/ComponentError.test.js +++ b/scripts/__tests__/ComponentTest/ComponentError.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from '../jest/testUtils'; describe('Component Error Test', () => { @@ -24,11 +24,11 @@ describe('Component Error Test', () => { jest.spyOn(console, 'error').mockImplementation(); expect(() => { - Horizon.render(, document.createElement('div')); + Inula.render(, document.createElement('div')); }).toThrow('Component type is invalid, got: null'); expect(() => { - Horizon.render(, document.createElement('div')); + Inula.render(, document.createElement('div')); }).toThrow('Component type is invalid, got: undefined'); const App = () => { @@ -42,7 +42,7 @@ describe('Component Error Test', () => { }; expect(() => { - Horizon.render(, document.createElement('div')); + Inula.render(, document.createElement('div')); }).toThrow('Component type is invalid, got: null'); AppChild = () => { @@ -52,7 +52,7 @@ describe('Component Error Test', () => { }; expect(() => { - Horizon.render(, document.createElement('div')); + Inula.render(, document.createElement('div')); }).toThrow('Component type is invalid, got: undefined'); }); }); diff --git a/scripts/__tests__/ComponentTest/Context.test.js b/scripts/__tests__/ComponentTest/Context.test.js index a3e6c562..70cc7111 100644 --- a/scripts/__tests__/ComponentTest/Context.test.js +++ b/scripts/__tests__/ComponentTest/Context.test.js @@ -13,18 +13,18 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from '../jest/testUtils'; describe('Context Test', () => { const LogUtils = getLogUtils(); - it('Provider及其内部consumer组件都不受制于shouldComponentUpdate函数或者Horizon.memo()', () => { + it('Provider及其内部consumer组件都不受制于shouldComponentUpdate函数或者Inula.memo()', () => { const LanguageTypes = { JAVA: 'Java', JAVASCRIPT: 'JavaScript', }; const defaultValue = { type: LanguageTypes.JAVASCRIPT }; - const SystemLanguageContext = Horizon.createContext(defaultValue); + const SystemLanguageContext = Inula.createContext(defaultValue); const SystemLanguageConsumer = SystemLanguageContext.Consumer; const SystemLanguageProvider = (props) => { LogUtils.log('SystemLanguageProvider'); @@ -47,7 +47,7 @@ describe('Context Test', () => { ); }; - class Middle extends Horizon.Component { + class Middle extends Inula.Component { shouldComponentUpdate() { return false; } @@ -70,7 +70,7 @@ describe('Context Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('Java'); expect(LogUtils.getAndClear()).toEqual([ 'App', @@ -82,14 +82,14 @@ describe('Context Test', () => { ]); // 组件不变,Middle没有更新,消费者也不会执行 - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('Java'); expect(LogUtils.getAndClear()).toEqual([ 'App', 'SystemLanguageProvider' ]); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('JavaScript'); // 组件更新,但是Middle没有更新,会绕过Middle expect(LogUtils.getAndClear()).toEqual([ @@ -104,7 +104,7 @@ describe('Context Test', () => { ONE: 1, TWO: 2, }; - const NumberContext = Horizon.createContext(0); + const NumberContext = Inula.createContext(0); const NumberConsumer = NumberContext.Consumer; const NumberProvider = (props) => { LogUtils.log(`SystemLanguageProvider: ${props.type}`); @@ -127,7 +127,7 @@ describe('Context Test', () => { ); }; - class Middle extends Horizon.Component { + class Middle extends Inula.Component { shouldComponentUpdate() { return false; } @@ -151,7 +151,7 @@ describe('Context Test', () => { }; // Consumer决定于距离它最近的provider - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('2'); expect(LogUtils.getAndClear()).toEqual([ 'App', @@ -162,7 +162,7 @@ describe('Context Test', () => { 'Consumer DOM mutations' ]); // 更新 - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('3'); expect(LogUtils.getAndClear()).toEqual([ 'App', @@ -177,8 +177,8 @@ describe('Context Test', () => { ONE: 1, TWO: 2, }; - const NumberContext = Horizon.createContext(0); - const NewNumberContext = Horizon.createContext(1); + const NumberContext = Inula.createContext(0); + const NewNumberContext = Inula.createContext(1); const NumberConsumer = NumberContext.Consumer; const NumberProvider = props => { return ( @@ -195,7 +195,7 @@ describe('Context Test', () => { ); }; - class Middle extends Horizon.Component { + class Middle extends Inula.Component { shouldComponentUpdate() { return false; } @@ -234,18 +234,18 @@ describe('Context Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); // 没有匹配到Provider,会使用defaultValue expect(container.querySelector('p').innerHTML).toBe('0'); // 更新,设置value为undefined - Horizon.render(, container); + Inula.render(, container); // 设置value为undefined时,defaultValue不生效 expect(container.querySelector('p').innerHTML).toBe(''); }); it('不同provider下的多个consumer', () => { - const NumContext = Horizon.createContext(1); + const NumContext = Inula.createContext(1); const Consumer = NumContext.Consumer; function Provider(props) { @@ -260,7 +260,7 @@ describe('Context Test', () => { ); } - class Middle extends Horizon.Component { + class Middle extends Inula.Component { shouldComponentUpdate() { return false; } @@ -290,22 +290,22 @@ describe('Context Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('4'); expect(container.querySelector('#p').innerHTML).toBe('2'); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('6'); expect(container.querySelector('#p').innerHTML).toBe('3'); }); it('consumer里的child更新是不会重新渲染', () => { - const NumContext = Horizon.createContext(1); + const NumContext = Inula.createContext(1); const Consumer = NumContext.Consumer; let setNum; const ReturnDom = props => { - const [num, _setNum] = Horizon.useState(0); + const [num, _setNum] = Inula.useState(0); setNum = _setNum; LogUtils.log('ReturnDom'); return ( @@ -326,7 +326,7 @@ describe('Context Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('Context: 2, Num: 0'); expect(LogUtils.getAndClear()).toEqual([ 'Consumer', @@ -339,11 +339,11 @@ describe('Context Test', () => { it('consumer可以拿到其他context的值', () => { - const NumContext = Horizon.createContext(1); - const TypeContext = Horizon.createContext('typeA'); + const NumContext = Inula.createContext(1); + const TypeContext = Inula.createContext('typeA'); const NumAndType = () => { - const type = Horizon.useContext(TypeContext); + const type = Inula.useContext(TypeContext); return ( {value => { @@ -364,23 +364,23 @@ describe('Context Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('Num: 2, Type: typeB'); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('Num: 2, Type: typeR'); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('Num: 8, Type: typeR'); }); // antd menu 级连context场景,menu路径使用级联context实现 it('nested context', () => { - const NestedContext = Horizon.createContext([]); + const NestedContext = Inula.createContext([]); let updateContext; function App() { - const [state, useState] = Horizon.useState([]); + const [state, useState] = Inula.useState([]); updateContext = useState; return ( @@ -390,13 +390,13 @@ describe('Context Test', () => { ); } - const div1Ref = Horizon.createRef(); - const div2Ref = Horizon.createRef(); + const div1Ref = Inula.createRef(); + const div2Ref = Inula.createRef(); let updateSub1; function Sub1() { - const path = Horizon.useContext(NestedContext); - const [_, setState] = Horizon.useState({}); + const path = Inula.useContext(NestedContext); + const [_, setState] = Inula.useState({}); updateSub1 = () => setState({}); return ( @@ -406,7 +406,7 @@ describe('Context Test', () => { } function Sub2() { - const path = Horizon.useContext(NestedContext); + const path = Inula.useContext(NestedContext); return ( @@ -416,7 +416,7 @@ describe('Context Test', () => { } function Sub3() { - const path = Horizon.useContext(NestedContext); + const path = Inula.useContext(NestedContext); return ( @@ -426,7 +426,7 @@ describe('Context Test', () => { } function Son({ divRef }) { - const path = Horizon.useContext(NestedContext); + const path = Inula.useContext(NestedContext); return (
{path.join(',')}
@@ -434,7 +434,7 @@ describe('Context Test', () => { ); } - Horizon.render(, container); + Inula.render(, container); updateSub1(); expect(div1Ref.current.innerHTML).toEqual('1'); expect(div2Ref.current.innerHTML).toEqual('2,3'); diff --git a/scripts/__tests__/ComponentTest/DiffAlgorithm.test.js b/scripts/__tests__/ComponentTest/DiffAlgorithm.test.js index ccd501c3..c81f16b2 100644 --- a/scripts/__tests__/ComponentTest/DiffAlgorithm.test.js +++ b/scripts/__tests__/ComponentTest/DiffAlgorithm.test.js @@ -13,13 +13,13 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; describe('Diff Algorithm', () => { it('null should diff correctly', () => { const fn = jest.fn(); - class C extends Horizon.Component { + class C extends Inula.Component { constructor() { super(); fn(); @@ -33,7 +33,7 @@ describe('Diff Algorithm', () => { let update; function App() { - const [current, setCurrent] = Horizon.useState(1); + const [current, setCurrent] = Inula.useState(1); update = setCurrent; return ( <> @@ -44,7 +44,7 @@ describe('Diff Algorithm', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(fn).toHaveBeenCalledTimes(1); update(2); diff --git a/scripts/__tests__/ComponentTest/ForwardRef.test.js b/scripts/__tests__/ComponentTest/ForwardRef.test.js index 16f4cf5b..18261cc9 100644 --- a/scripts/__tests__/ComponentTest/ForwardRef.test.js +++ b/scripts/__tests__/ComponentTest/ForwardRef.test.js @@ -13,14 +13,14 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from '../jest/testUtils'; describe('ForwardRef', () => { const LogUtils = getLogUtils(); it('ForwardRef包裹的函数组件应该正常触发effect', () => { function App(props, ref) { - Horizon.useEffect(() => { + Inula.useEffect(() => { LogUtils.log('effect'); return () => { LogUtils.log('effect remove'); @@ -29,32 +29,32 @@ describe('ForwardRef', () => { return ; } - const Wrapper = Horizon.forwardRef(App); + const Wrapper = Inula.forwardRef(App); - Horizon.act(() => { - Horizon.render(, container); + Inula.act(() => { + Inula.render(, container); }); expect(LogUtils.getAndClear()).toEqual(['effect']); - Horizon.act(() => { - Horizon.render(, container); + Inula.act(() => { + Inula.render(, container); }); expect(LogUtils.getAndClear()).toEqual(['effect remove', 'effect']); }); it('memo组件包裹的类组件', () => { - class Component extends Horizon.Component { + class Component extends Inula.Component { render() { return ; } } - const Wrapper = Horizon.memo(Component); + const Wrapper = Inula.memo(Component); - Horizon.act(() => { - Horizon.render(, container); + Inula.act(() => { + Inula.render(, container); }); - Horizon.act(() => { - Horizon.render(, container); + Inula.act(() => { + Inula.render(, container); }); }); }); diff --git a/scripts/__tests__/ComponentTest/FragmentComponent.test.js b/scripts/__tests__/ComponentTest/FragmentComponent.test.js index c0b79fb1..ce8650ef 100755 --- a/scripts/__tests__/ComponentTest/FragmentComponent.test.js +++ b/scripts/__tests__/ComponentTest/FragmentComponent.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { Text } from '../jest/commonComponents'; import { getLogUtils } from '../jest/testUtils'; @@ -23,24 +23,24 @@ describe('Fragment', () => { useEffect, useRef, act, - } = Horizon; + } = Inula; it('可以渲染空元素', () => { const element = ( - + ); - Horizon.render(element, container); + Inula.render(element, container); expect(container.textContent).toBe(''); }); it('可以渲染单个元素', () => { const element = ( - + - + ); - Horizon.render(element, container); + Inula.render(element, container); expect(LogUtils.getAndClear()).toEqual(['Fragment']); expect(container.textContent).toBe('Fragment'); @@ -48,12 +48,12 @@ describe('Fragment', () => { it('可以渲染混合元素', () => { const element = ( - + Java and - + ); - Horizon.render(element, container); + Inula.render(element, container); expect(LogUtils.getAndClear()).toEqual(['JavaScript']); expect(container.textContent).toBe('Java and JavaScript'); @@ -67,7 +67,7 @@ describe('Fragment', () => { ); - Horizon.render(element, container); + Inula.render(element, container); expect(LogUtils.getAndClear()).toEqual(['Java', 'JavaScript']); expect(container.textContent).toBe('JavaJavaScript'); @@ -103,18 +103,18 @@ describe('Fragment', () => { }; act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); act(() => { - Horizon.render(, container); + Inula.render(, container); }); // 切换到不同层级Fragment时,副作用状态不会保留 expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('2'); act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('1'); @@ -145,18 +145,18 @@ describe('Fragment', () => { }; act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); act(() => { - Horizon.render(, container); + Inula.render(, container); }); // 状态会保留 expect(LogUtils.getNotClear()).toEqual(['useEffect']); expect(container.textContent).toBe('2'); act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']); expect(container.textContent).toBe('1'); @@ -188,18 +188,18 @@ describe('Fragment', () => { }; act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); act(() => { - Horizon.render(, container); + Inula.render(, container); }); // 状态不会保留 expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('1232'); act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('1'); @@ -234,18 +234,18 @@ describe('Fragment', () => { }; act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); act(() => { - Horizon.render(, container); + Inula.render(, container); }); // 状态不会保留 expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('2'); act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('1'); @@ -286,18 +286,18 @@ describe('Fragment', () => { }; act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); act(() => { - Horizon.render(, container); + Inula.render(, container); }); // 状态会保留 expect(LogUtils.getNotClear()).toEqual(['useEffect']); expect(container.textContent).toBe('2'); act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']); expect(container.textContent).toBe('1'); @@ -338,18 +338,18 @@ describe('Fragment', () => { }; act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); act(() => { - Horizon.render(, container); + Inula.render(, container); }); // 状态会保留 expect(LogUtils.getNotClear()).toEqual(['useEffect']); expect(container.textContent).toBe('2'); act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']); expect(container.textContent).toBe('1'); @@ -380,18 +380,18 @@ describe('Fragment', () => { }; act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); act(() => { - Horizon.render(, container); + Inula.render(, container); }); // 状态会保留 expect(LogUtils.getNotClear()).toEqual(['useEffect']); expect(container.textContent).toBe('2'); act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']); expect(container.textContent).toBe('1'); @@ -426,18 +426,18 @@ describe('Fragment', () => { }; act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); act(() => { - Horizon.render(, container); + Inula.render(, container); }); // 状态会保留 expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('2'); act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('[1]'); @@ -459,29 +459,29 @@ describe('Fragment', () => { const App = (props) => { return props.change ? ( - + - + ) : ( - + - + ); }; act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); act(() => { - Horizon.render(, container); + Inula.render(, container); }); // 状态不会保留 expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('2'); act(() => { - Horizon.render(, container); + Inula.render(, container); }); expect(LogUtils.getNotClear()).toEqual([]); expect(container.textContent).toBe('1'); diff --git a/scripts/__tests__/ComponentTest/FunctionComponent.test.js b/scripts/__tests__/ComponentTest/FunctionComponent.test.js index bd02b9d5..81e10fe4 100644 --- a/scripts/__tests__/ComponentTest/FunctionComponent.test.js +++ b/scripts/__tests__/ComponentTest/FunctionComponent.test.js @@ -13,14 +13,14 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; describe('FunctionComponent Test', () => { it('渲染无状态组件', () => { const App = (props) => { return

{props.text}

; }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('app'); }); @@ -29,13 +29,13 @@ describe('FunctionComponent Test', () => { return

{props.text}

; }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('app'); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('ABC'); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('abc'); }); @@ -44,10 +44,10 @@ describe('FunctionComponent Test', () => { return

{props.text}

; }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('app'); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); expect(container.querySelector('p')).toBe(null); }); @@ -56,24 +56,24 @@ describe('FunctionComponent Test', () => { return
; }; - const realNode = Horizon.render(, container); + const realNode = Inula.render(, container); expect(realNode).toBe(null); }); - it('测试函数组件的defaultProps:Horizon.memo(Horizon.forwardRef(()=>{}))两层包装的场景后,defaultProps依然正常', () => { + it('测试函数组件的defaultProps:Inula.memo(Inula.forwardRef(()=>{}))两层包装的场景后,defaultProps依然正常', () => { const App = () => { return ; }; - const DefaultPropsComp = Horizon.forwardRef(props => { + const DefaultPropsComp = Inula.forwardRef(props => { return
{props.name}
; }); DefaultPropsComp.defaultProps = { name: 'Hello!', }; - const DefaultPropsCompMemo = Horizon.memo(DefaultPropsComp); + const DefaultPropsCompMemo = Inula.memo(DefaultPropsComp); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('div').innerHTML).toBe('Hello!'); }); @@ -86,7 +86,7 @@ describe('FunctionComponent Test', () => { return
{props.name}
; }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('div').style['_values']['--max-segment-num']).toBe(10); }); diff --git a/scripts/__tests__/ComponentTest/HookTest/UseCallback.test.js b/scripts/__tests__/ComponentTest/HookTest/UseCallback.test.js index 579f1d7d..cb599e32 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseCallback.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseCallback.test.js @@ -13,10 +13,10 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; describe('useCallback Hook Test', () => { - const { useState, useCallback } = Horizon; + const { useState, useCallback } = Inula; it('测试useCallback', () => { const App = (props) => { @@ -31,7 +31,7 @@ describe('useCallback Hook Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('0'); // 点击按钮触发num加1 container.querySelector('button').click(); @@ -40,7 +40,7 @@ describe('useCallback Hook Test', () => { container.querySelector('button').click(); expect(container.querySelector('p').innerHTML).toBe('1'); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('1'); // 依赖项有变化,点击按钮num增加 container.querySelector('button').click(); diff --git a/scripts/__tests__/ComponentTest/HookTest/UseContext.test.js b/scripts/__tests__/ComponentTest/HookTest/UseContext.test.js index 4fc48f97..decc1a94 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseContext.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseContext.test.js @@ -13,10 +13,10 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; describe('useContext Hook Test', () => { - const { useState, useContext, createContext, act, unmountComponentAtNode } = Horizon; + const { useState, useContext, createContext, act, unmountComponentAtNode } = Inula; it('简单使用useContext', () => { const LanguageTypes = { @@ -24,7 +24,7 @@ describe('useContext Hook Test', () => { JAVASCRIPT: 'JavaScript', }; const defaultValue = { type: LanguageTypes.JAVASCRIPT }; - const SystemLanguageContext = Horizon.createContext(defaultValue); + const SystemLanguageContext = Inula.createContext(defaultValue); const SystemLanguageProvider = ({ type, children }) => { return ( @@ -49,11 +49,11 @@ describe('useContext Hook Test', () => {
); }; - Horizon.render(, container); + Inula.render(, container); // 测试当Provider未提供时,获取到的默认值'JavaScript'。 expect(container.querySelector('p').innerHTML).toBe('JavaScript'); unmountComponentAtNode(container); - Horizon.render(, container); + Inula.render(, container); // 测试当Provider提供时,可以获取到Provider的值'Java'。 expect(container.querySelector('p').innerHTML).toBe('Java'); // 测试当Provider改变时,可以获取到最新Provider的值。 @@ -63,7 +63,7 @@ describe('useContext Hook Test', () => { it('更新后useContext仍能获取到context', () => { const Context = createContext({}); - const ref = Horizon.createRef(); + const ref = Inula.createRef(); function App() { return ( @@ -87,7 +87,7 @@ describe('useContext Hook Test', () => { return
{context.text}
; } - Horizon.render(, container); + Inula.render(, container); expect(ref.current.innerHTML).toBe('context'); update(); diff --git a/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js b/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js index 24799f55..a6da51ae 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import { getLogUtils } from '../../jest/testUtils'; import { Text } from '../../jest/commonComponents'; @@ -25,7 +25,7 @@ describe('useEffect Hook Test', () => { memo, forwardRef, act, - } = Horizon; + } = Inula; const LogUtils = getLogUtils(); it('简单使用useEffect', () => { @@ -41,7 +41,7 @@ describe('useEffect Hook Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById('p').style.display).toBe('block'); // 点击按钮触发num加1 container.querySelector('button').click(); @@ -57,7 +57,7 @@ describe('useEffect Hook Test', () => { }; act(() => { - Horizon.render(, container, () => { + Inula.render(, container, () => { LogUtils.log('num effect'); }); // 第一次渲染为同步,所以同步执行的可以写在act里做判断 @@ -65,7 +65,7 @@ describe('useEffect Hook Test', () => { expect(container.textContent).toBe('op'); }); act(() => { - Horizon.render(null, container, () => { + Inula.render(null, container, () => { LogUtils.log('num effect89'); }); // 第二次渲染为异步,所以同步执行的不可以写在act里做判断,act里拿到的为空数组 @@ -87,7 +87,7 @@ describe('useEffect Hook Test', () => { }; const na = ; // 必须设置key值,否则在diff的时候na会被视为不同组件 - Horizon.render([, na], container); + Inula.render([, na], container); expect(LogUtils.getAndClear()).toEqual([ 'App', 'NewApp' @@ -95,7 +95,7 @@ describe('useEffect Hook Test', () => { expect(container.textContent).toBe('AppNewApp'); expect(LogUtils.getAndClear()).toEqual([]); // 在执行新的render前,会执行完上一次render的useEffect,所以LogUtils会加入'NewApp effect'。 - Horizon.render([na], container); + Inula.render([na], container); expect(LogUtils.getAndClear()).toEqual(['NewApp effect', 'NewApp']); expect(container.textContent).toBe('NewApp'); expect(LogUtils.getAndClear()).toEqual([]); @@ -119,7 +119,7 @@ describe('useEffect Hook Test', () => { return ; }; // 必须设置key值,否则在diff的时候na会被视为不同组件 - Horizon.render([, ], container); + Inula.render([, ], container); expect(LogUtils.getAndClear()).toEqual([ 'App', 'NewApp', @@ -137,7 +137,7 @@ describe('useEffect Hook Test', () => { const App = () => { useLayoutEffect(() => { LogUtils.log('App Layout effect'); - Horizon.render(, newContainer); + Inula.render(, newContainer); }); return ; }; @@ -148,7 +148,7 @@ describe('useEffect Hook Test', () => { return ; }; // 必须设置key值,否则在diff的时候na会被视为不同组件 - Horizon.render([, ], container); + Inula.render([, ], container); expect(LogUtils.getAndClear()).toEqual([ 'App', 'NewApp', @@ -168,13 +168,13 @@ describe('useEffect Hook Test', () => { return ; }; act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']); expect(container.textContent).toEqual('num: 0'); }); expect(LogUtils.getAndClear()).toEqual(['First effect [0]']); act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); }); // 此时异步执行,act执行完后会执行新render的useEffect @@ -197,13 +197,13 @@ describe('useEffect Hook Test', () => { return ; }; act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']); expect(container.textContent).toEqual('num: 0'); }); expect(LogUtils.getAndClear()).toEqual(['First effect [0]', 'Second effect [0]']); act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); }); // 第二次render时异步执行,act保证所有效果都已更新,所以先常规记录日志 // 然后记录useEffect的日志 @@ -246,7 +246,7 @@ describe('useEffect Hook Test', () => { }; act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0,word: App', 'num Layouteffect [0]', @@ -261,21 +261,21 @@ describe('useEffect Hook Test', () => { act(() => { // 此时word改变,num不变 - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ - 'num: 0,word: Horizon', + 'num: 0,word: Inula', 'word Layouteffect destroy', - 'word Layouteffect [Horizon]', + 'word Layouteffect [Inula]', 'callback effect', // 最后执行异步的 'word effect destroy', - 'word effect [Horizon]', + 'word effect [Inula]', ]); act(() => { // 此时num和word的所有effect都销毁 - Horizon.render(null, container, () => LogUtils.log('callback effect')); + Inula.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'num Layouteffect destroy', @@ -299,7 +299,7 @@ describe('useEffect Hook Test', () => { }; act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0', 'callback effect' @@ -311,7 +311,7 @@ describe('useEffect Hook Test', () => { ]); act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'num: 1', @@ -324,7 +324,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual([]); act(() => { - Horizon.render(null, container, () => LogUtils.log('callback effect')); + Inula.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'callback effect', @@ -346,7 +346,7 @@ describe('useEffect Hook Test', () => { }; act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0', 'callback effect' @@ -358,7 +358,7 @@ describe('useEffect Hook Test', () => { ]); act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'num: 1', @@ -369,7 +369,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual([]); act(() => { - Horizon.render(null, container, () => LogUtils.log('callback effect')); + Inula.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'callback effect', @@ -382,7 +382,7 @@ describe('useEffect Hook Test', () => { it('useEffect里使用useState(1', () => { let setNum; const App = () => { - const [num, _setNum] = Horizon.useState(0); + const [num, _setNum] = Inula.useState(0); useEffect(() => { LogUtils.log(`num effect [${num}]`); setNum = () => _setNum(1); @@ -397,7 +397,7 @@ describe('useEffect Hook Test', () => { }; act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0', 'num Layouteffect [0]', @@ -430,7 +430,7 @@ describe('useEffect Hook Test', () => { return ; }; - Horizon.render(, container, () => LogUtils.log('App callback effect')); + Inula.render(, container, () => LogUtils.log('App callback effect')); expect(LogUtils.getAndClear()).toEqual(['Num: 0', 'App callback effect']); expect(container.textContent).toEqual('Num: 0'); act(() => { @@ -460,7 +460,7 @@ describe('useEffect Hook Test', () => { return ; }); act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 0, 'callback effect' @@ -471,7 +471,7 @@ describe('useEffect Hook Test', () => { // 不会重新渲染 act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual(['callback effect']); expect(container.textContent).toEqual('0'); @@ -490,7 +490,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual([]); act(() => { - Horizon.render(null, container, () => LogUtils.log('callback effect')); + Inula.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'callback effect', @@ -512,7 +512,7 @@ describe('useEffect Hook Test', () => { return ; }, compare); act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 0, 'callback effect' @@ -523,7 +523,7 @@ describe('useEffect Hook Test', () => { // 不会重新渲染 act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual(['callback effect']); expect(container.textContent).toEqual('0'); @@ -531,7 +531,7 @@ describe('useEffect Hook Test', () => { // 会重新渲染 act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 1, @@ -544,7 +544,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual([]); act(() => { - Horizon.render(null, container, () => LogUtils.log('callback effect')); + Inula.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual(['callback effect', 'num effect destroy 1']); expect(container.textContent).toEqual(''); @@ -565,7 +565,7 @@ describe('useEffect Hook Test', () => { return ; }; act(() => { - Horizon.render(, container, () => + Inula.render(, container, () => LogUtils.log('App callback effect'), ); expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'App callback effect']); @@ -575,7 +575,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual(['throw Error']); act(() => { - Horizon.render(null, container, () => + Inula.render(null, container, () => LogUtils.log('App callback effect'), ); }); @@ -607,14 +607,14 @@ describe('useEffect Hook Test', () => { }; act(() => { - Horizon.render(, container, () => LogUtils.log('num effect')); + Inula.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'num effect']); expect(container.textContent).toBe('Number: 0'); }); expect(LogUtils.getAndClear()).toEqual(['num effect [0]']); act(() => { - Horizon.render(null, container, () => LogUtils.log('num effect')); + Inula.render(null, container, () => LogUtils.log('num effect')); }); expect(LogUtils.getAndClear()).toEqual(['num effect', 'num effect destroy 0']); expect(container.textContent).toBe(''); @@ -633,7 +633,7 @@ describe('useEffect Hook Test', () => { return ; }; - Horizon.render(, container, () => LogUtils.log('num effect')); + Inula.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'num effect']); expect(container.textContent).toBe('Number: 0'); @@ -663,13 +663,13 @@ describe('useEffect Hook Test', () => { }; act(() => { - Horizon.render(, container, () => LogUtils.log('num effect')); + Inula.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual(['useEffect', 'num effect']); }); expect(LogUtils.getAndClear()).toEqual(['effect']); act(() => { - Horizon.render(null, container); + Inula.render(null, container); }); // 不会处理setNum(1) expect(LogUtils.getAndClear()).toEqual(['effect destroy']); @@ -678,7 +678,7 @@ describe('useEffect Hook Test', () => { it('当组件的更新方法在卸载函数中,组件的子组件更新不会告警', () => { const App = () => { LogUtils.log('App'); - const appRef = Horizon.createRef(null); + const appRef = Inula.createRef(null); useEffect(() => { LogUtils.log('App effect'); return () => { @@ -701,7 +701,7 @@ describe('useEffect Hook Test', () => { AppChild = forwardRef(AppChild); act(() => { - Horizon.render(, container, () => LogUtils.log('num effect')); + Inula.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual([ 'App', 'AppChild', @@ -711,7 +711,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual(['Child effect', 'App effect']); act(() => { - Horizon.render(null, container); + Inula.render(null, container); }); // 销毁时执行appRef.current(1)不会报错 expect(LogUtils.getAndClear()).toEqual(['App effect destroy']); @@ -737,7 +737,7 @@ describe('useEffect Hook Test', () => { }; act(() => { - Horizon.render(, container, () => LogUtils.log('num effect')); + Inula.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual([ 'App', 'AppChild', @@ -747,7 +747,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual(['Child effect']); act(() => { - Horizon.render(null, container); + Inula.render(null, container); }); // 销毁时执行 props.setNum(1);不会报错 expect(LogUtils.getAndClear()).toEqual(['Child effect destroy']); diff --git a/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js b/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js index a44c58b2..1de72341 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import { Text } from '../../jest/commonComponents'; import { getLogUtils } from '../../jest/testUtils'; @@ -23,8 +23,8 @@ describe('useImperativeHandle Hook Test', () => { useImperativeHandle, forwardRef, act, - } = Horizon; - const { unmountComponentAtNode } = Horizon; + } = Inula; + const { unmountComponentAtNode } = Inula; const LogUtils = getLogUtils(); it('测试useImperativeHandle', () => { @@ -41,9 +41,9 @@ describe('useImperativeHandle Hook Test', () => { App = forwardRef(App); App1 = forwardRef(App1); - const counter = Horizon.createRef(null); - const counter1 = Horizon.createRef(null); - Horizon.render(, container); + const counter = Inula.createRef(null); + const counter1 = Inula.createRef(null); + Inula.render(, container); expect(counter.current.num).toBe(0); act(() => { counter.current.setNum(1); @@ -53,7 +53,7 @@ describe('useImperativeHandle Hook Test', () => { // 清空container unmountComponentAtNode(container); - Horizon.render(, container); + Inula.render(, container); expect(counter1.current.num1).toBe(0); act(() => { counter1.current.setNum1(1); @@ -76,9 +76,9 @@ describe('useImperativeHandle Hook Test', () => { App = forwardRef(App); App1 = forwardRef(App1); - const counter = Horizon.createRef(null); - const counter1 = Horizon.createRef(null); - Horizon.render(, container); + const counter = Inula.createRef(null); + const counter1 = Inula.createRef(null); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([0]); expect(counter.current.num).toBe(0); act(() => { @@ -90,7 +90,7 @@ describe('useImperativeHandle Hook Test', () => { // 清空container unmountComponentAtNode(container); - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([0]); expect(counter1.current.num1).toBe(0); act(() => { diff --git a/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js b/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js index 8cba787c..66bfbf4e 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import { getLogUtils } from '../../jest/testUtils'; import { Text } from '../../jest/commonComponents'; @@ -23,7 +23,7 @@ describe('useLayoutEffect Hook Test', () => { useEffect, useLayoutEffect, act, - } = Horizon; + } = Inula; const LogUtils = getLogUtils(); it('简单使用useLayoutEffect', () => { const App = () => { @@ -38,7 +38,7 @@ describe('useLayoutEffect Hook Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById('p').style.display).toBe('none'); container.querySelector('button').click(); expect(container.querySelector('p').style.display).toBe('inline'); @@ -51,7 +51,7 @@ describe('useLayoutEffect Hook Test', () => { }); return ; }; - Horizon.render(, container, () => LogUtils.log('Sync effect')); + Inula.render(, container, () => LogUtils.log('Sync effect')); expect(LogUtils.getAndClear()).toEqual([ 1, // 同步在渲染之后 @@ -60,7 +60,7 @@ describe('useLayoutEffect Hook Test', () => { ]); expect(container.querySelector('p').innerHTML).toBe('1'); // 更新 - Horizon.render(, container, () => LogUtils.log('Sync effect')); + Inula.render(, container, () => LogUtils.log('Sync effect')); expect(LogUtils.getAndClear()).toEqual([ 2, 'LayoutEffect', @@ -87,7 +87,7 @@ describe('useLayoutEffect Hook Test', () => { }; act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0', 'num Layouteffect [0]', @@ -98,7 +98,7 @@ describe('useLayoutEffect Hook Test', () => { // 更新 act(() => { - Horizon.render(, container, () => LogUtils.log('callback effect')); + Inula.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ // 异步effect @@ -116,7 +116,7 @@ describe('useLayoutEffect Hook Test', () => { ]); act(() => { - Horizon.render(null, container, () => LogUtils.log('callback effect')); + Inula.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ // 同步Layouteffect销毁 diff --git a/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js b/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js index 59a25a09..fb1fe711 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js @@ -13,12 +13,12 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import { getLogUtils } from '../../jest/testUtils'; import { Text } from '../../jest/commonComponents'; describe('useMemo Hook Test', () => { - const { useMemo, useState } = Horizon; + const { useMemo, useState } = Inula; const LogUtils = getLogUtils(); it('测试useMemo', () => { @@ -38,7 +38,7 @@ describe('useMemo Hook Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('App'); expect(container.querySelector('#p').innerHTML).toBe('1'); // 修改useMemo的依赖项,num会加一,text会改变。 @@ -63,26 +63,26 @@ describe('useMemo Hook Test', () => { }, [props._num]); return ; }; - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([ 0, 1 ]); expect(container.textContent).toBe('1'); - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([ 1, 2 ]); expect(container.textContent).toBe('2'); - Horizon.render(, container); + Inula.render(, container); // 不会触发useMemo expect(LogUtils.getAndClear()).toEqual([2]); expect(container.textContent).toBe('2'); - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([ 2, 3 @@ -106,16 +106,16 @@ describe('useMemo Hook Test', () => { return 2; }; - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual(['num 1', 1]); - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual(['num 1', 1]); - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual(['num 1', 1]); - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual(['num 2', 2]); }); }); diff --git a/scripts/__tests__/ComponentTest/HookTest/UseReducer.test.js b/scripts/__tests__/ComponentTest/HookTest/UseReducer.test.js index 379d85ac..806e3662 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseReducer.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseReducer.test.js @@ -13,10 +13,10 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; describe('useReducer Hook Test', () => { - const { useReducer } = Horizon; + const { useReducer } = Inula; it('简单使用useReducer', () => { const intlCar = { logo: '', price: 0 }; @@ -59,7 +59,7 @@ describe('useReducer Hook Test', () => {
); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe(''); expect(container.querySelector('#senP').innerHTML).toBe('0'); // 触发bmw @@ -77,7 +77,7 @@ describe('useReducer Hook Test', () => { const reducer = () => { return { data: nextId++ }; }; - const btnRef = Horizon.createRef(); + const btnRef = Inula.createRef(); const Main = () => { const [{ data }, dispatch] = useReducer(reducer, { data: 0 }); const dispatchLogging = () => { @@ -95,8 +95,8 @@ describe('useReducer Hook Test', () => { ); }; - Horizon.render(
, container); - Horizon.act(() => { + Inula.render(
, container); + Inula.act(() => { btnRef.current.click(); }); expect(nextId).toBe(2); diff --git a/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js b/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js index 570f029b..ebefe6b8 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js @@ -13,12 +13,12 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import { getLogUtils } from '../../jest/testUtils'; import { Text } from '../../jest/commonComponents'; describe('useRef Hook Test', () => { - const { useState, useRef } = Horizon; + const { useState, useRef } = Inula; const LogUtils = getLogUtils(); it('测试useRef', () => { @@ -36,7 +36,7 @@ describe('useRef Hook Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('1'); expect(container.querySelector('#sp').innerHTML).toBe('1'); // 点击按钮触发num加1,ref不变 @@ -59,7 +59,7 @@ describe('useRef Hook Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([1]); expect(container.querySelector('p').innerHTML).toBe('1'); // 点击按钮触发ref.current加1 diff --git a/scripts/__tests__/ComponentTest/HookTest/UseState.test.js b/scripts/__tests__/ComponentTest/HookTest/UseState.test.js index b5e7409f..0c7e60ab 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseState.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseState.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import { getLogUtils } from '../../jest/testUtils'; import { Text } from '../../jest/commonComponents'; @@ -24,7 +24,7 @@ describe('useState Hook Test', () => { useImperativeHandle, memo, act, - } = Horizon; + } = Inula; const LogUtils = getLogUtils(); it('简单使用useState', () => { @@ -37,7 +37,7 @@ describe('useState Hook Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('0'); // 点击按钮触发num加1 container.querySelector('button').click(); @@ -59,7 +59,7 @@ describe('useState Hook Test', () => {

); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('00'); container.querySelector('p').click(); expect(container.querySelector('p').innerHTML).toBe('12'); @@ -81,7 +81,7 @@ describe('useState Hook Test', () => {

); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('0'); container.querySelector('p').click(); expect(container.querySelector('p').innerHTML).toBe('2'); @@ -96,7 +96,7 @@ describe('useState Hook Test', () => { setNum = _setNum; return ; }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('0'); expect(LogUtils.getAndClear()).toEqual([0]); // useState修改state 时,设置相同的值,函数组件不会重新渲染 @@ -115,8 +115,8 @@ describe('useState Hook Test', () => { return

{num}

; }); - const ref = Horizon.createRef(null); - Horizon.render(, container); + const ref = Inula.createRef(null); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([1]); expect(container.querySelector('p').innerHTML).toBe('1'); // 设置num为3 @@ -133,11 +133,11 @@ describe('useState Hook Test', () => { setNum = _setNum; return ; }); - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([0]); expect(container.querySelector('p').innerHTML).toBe('0'); // 不会重新渲染 - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([]); expect(container.querySelector('p').innerHTML).toBe('0'); // 会重新渲染 @@ -166,7 +166,7 @@ describe('useState Hook Test', () => { return ; }; - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual(['Number: 0, Count: 0']); expect(container.textContent).toBe('Number: 0, Count: 0'); act(() => { @@ -178,7 +178,7 @@ describe('useState Hook Test', () => { jest.spyOn(console, 'error').mockImplementation(); expect(() => { - Horizon.render(, container); + Inula.render(, container); }).toThrow('Hooks are less than expected, please check whether the hook is written in the condition.'); }); }); diff --git a/scripts/__tests__/ComponentTest/JsxElement.test.js b/scripts/__tests__/ComponentTest/JsxElement.test.js index 8794fbbf..5f3e573a 100644 --- a/scripts/__tests__/ComponentTest/JsxElement.test.js +++ b/scripts/__tests__/ComponentTest/JsxElement.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; describe('JSX Element test', () => { it('symbol attribute prevent cloneDeep unlimited loop', function () { @@ -29,7 +29,7 @@ describe('JSX Element test', () => { }) return result; } - class Demo extends Horizon.Component { + class Demo extends Inula.Component { render() { return (
@@ -39,7 +39,7 @@ describe('JSX Element test', () => { } } - const ele = Horizon.createElement(Demo); + const ele = Inula.createElement(Demo); const copy = cloneDeep(ele); expect(copy.vtype).toEqual(ele.vtype); expect(Object.getOwnPropertySymbols(copy).length).toEqual(0); diff --git a/scripts/__tests__/ComponentTest/LazyComponent.test.js b/scripts/__tests__/ComponentTest/LazyComponent.test.js index c50fede6..9f23e68e 100755 --- a/scripts/__tests__/ComponentTest/LazyComponent.test.js +++ b/scripts/__tests__/ComponentTest/LazyComponent.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { Text } from '../jest/commonComponents'; import { getLogUtils } from '../jest/testUtils'; @@ -23,8 +23,8 @@ describe('LazyComponent Test', () => { return { default: component }; }); - it('Horizon.lazy()', async () => { - class LazyComponent extends Horizon.Component { + it('Inula.lazy()', async () => { + class LazyComponent extends Inula.Component { static defaultProps = { language: 'Java' }; render() { @@ -33,12 +33,12 @@ describe('LazyComponent Test', () => { } } - const Lazy = Horizon.lazy(() => mockImport(LazyComponent)); + const Lazy = Inula.lazy(() => mockImport(LazyComponent)); - Horizon.render( - }> + Inula.render( + }> - , + , container ); @@ -47,10 +47,10 @@ describe('LazyComponent Test', () => { expect(container.querySelector('span')).toBe(null); await Promise.resolve(); - Horizon.render( - }> + Inula.render( + }> - , + , container ); @@ -59,16 +59,16 @@ describe('LazyComponent Test', () => { }); it('同步解析', async () => { - const LazyApp = Horizon.lazy(() => ({ + const LazyApp = Inula.lazy(() => ({ then(cb) { cb({ default: Text }); }, })); - Horizon.render( - Loading...
}> + Inula.render( + Loading...
}> - , + , container ); @@ -77,7 +77,7 @@ describe('LazyComponent Test', () => { }); it('异常捕获边界', async () => { - class ErrorBoundary extends Horizon.Component { + class ErrorBoundary extends Inula.Component { state = {}; static getDerivedStateFromError(error) { return { message: error.message }; @@ -90,7 +90,7 @@ describe('LazyComponent Test', () => { } const LazyComponent = () => { - const [num, setNum] = Horizon.useState(0); + const [num, setNum] = Inula.useState(0); if (num === 2) { throw new Error('num is 2'); } else { @@ -103,24 +103,24 @@ describe('LazyComponent Test', () => { } }; - const LazyApp = Horizon.lazy(() => mockImport(LazyComponent)); + const LazyApp = Inula.lazy(() => mockImport(LazyComponent)); - Horizon.render( + Inula.render( - Loading...
}> + Loading...
}> - + , container ); expect(container.textContent).toBe('Loading...'); await Promise.resolve(); - Horizon.render( + Inula.render( - }> + }> - + , container ); @@ -133,7 +133,7 @@ describe('LazyComponent Test', () => { }); it('componentDidCatch捕获异常', async () => { - class ErrorBoundary extends Horizon.Component { + class ErrorBoundary extends Inula.Component { state = { catchError: false, error: null, @@ -157,7 +157,7 @@ describe('LazyComponent Test', () => { } const LazyComponent = () => { - const [num, setNum] = Horizon.useState(0); + const [num, setNum] = Inula.useState(0); if (num === 2) { throw new Error('num is 2'); } else { @@ -170,24 +170,24 @@ describe('LazyComponent Test', () => { } }; - const LazyApp = Horizon.lazy(() => mockImport(LazyComponent)); + const LazyApp = Inula.lazy(() => mockImport(LazyComponent)); - Horizon.render( + Inula.render( - Loading...
}> + Loading...
}> - + , container ); expect(container.textContent).toBe('Loading...'); await Promise.resolve(); - Horizon.render( + Inula.render( - }> + }> - + , container ); @@ -202,25 +202,25 @@ describe('LazyComponent Test', () => { it('#24 配合memo', async () => { const fnComp = () => { - return

horizon

; + return

inula

; }; - const LazyApp = Horizon.lazy(() => ({ + const LazyApp = Inula.lazy(() => ({ then(cb) { - cb({ default: Horizon.memo(() => fnComp, false) }); + cb({ default: Inula.memo(() => fnComp, false) }); }, })); expect(() => { - Horizon.render( - Loading...
}> + Inula.render( + Loading...
}> - , + , container ); - Horizon.render( - Loading...
}> + Inula.render( + Loading...
}> - , + , container ); }).not.toThrow(); diff --git a/scripts/__tests__/ComponentTest/LifeCycle.test.js b/scripts/__tests__/ComponentTest/LifeCycle.test.js index 2ab719ef..8969e8a9 100644 --- a/scripts/__tests__/ComponentTest/LifeCycle.test.js +++ b/scripts/__tests__/ComponentTest/LifeCycle.test.js @@ -13,14 +13,14 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from '../jest/testUtils'; describe('LifeCycle Test', () => { const LogUtils = getLogUtils(); describe('LifeCycle function', () => { it('不能在componentWillMount里setState', () => { - class App extends Horizon.Component { + class App extends Inula.Component { state = {}; UNSAFE_componentWillMount() { @@ -34,13 +34,13 @@ describe('LifeCycle Test', () => { } } - const realNode = Horizon.render(, container); + const realNode = Inula.render(, container); // 不能在componentWillMount里setState expect(realNode.textContent).toBe(undefined); }); it('componentDidMount里调用setState()将触发额外渲染', () => { - class ChildApp extends Horizon.Component { + class ChildApp extends Inula.Component { constructor(props) { super(props); } @@ -54,7 +54,7 @@ describe('LifeCycle Test', () => { } } - class App extends Horizon.Component { + class App extends Inula.Component { constructor(props) { super(props); LogUtils.log('constructor'); @@ -75,7 +75,7 @@ describe('LifeCycle Test', () => { } } - const realNode = Horizon.render(, container); + const realNode = Inula.render(, container); // 确实触发了额外渲染 expect(LogUtils.getAndClear()).toEqual([ 'constructor', @@ -89,7 +89,7 @@ describe('LifeCycle Test', () => { }); it('调用 this.setState() 通常不会触发 UNSAFE_componentWillReceiveProps()', () => { - class App extends Horizon.Component { + class App extends Inula.Component { state = {}; update = () => { @@ -108,14 +108,14 @@ describe('LifeCycle Test', () => { } } - const realNode = Horizon.render(, container); + const realNode = Inula.render(, container); expect(realNode.textContent).toBe(undefined); realNode.update(); expect(LogUtils.getAndClear()).toEqual([]); }); it('不能在componentWillReceiveProps里setState', () => { - class ChildApp extends Horizon.Component { + class ChildApp extends Inula.Component { state = {}; UNSAFE_componentWillReceiveProps() { @@ -127,7 +127,7 @@ describe('LifeCycle Test', () => { return
{this.state.text}
; } } - class App extends Horizon.Component { + class App extends Inula.Component { state = {}; update = () => { @@ -139,7 +139,7 @@ describe('LifeCycle Test', () => { } } - const realNode = Horizon.render(, container); + const realNode = Inula.render(, container); expect(realNode.textContent).toBe(undefined); realNode.update(); expect(LogUtils.getAndClear()).toEqual([ @@ -151,7 +151,7 @@ describe('LifeCycle Test', () => { }); it('shouldComponentUpdate与getDerivedStateFromProps', () => { - class App extends Horizon.Component { + class App extends Inula.Component { constructor(props) { super(props); this.state = { @@ -175,24 +175,24 @@ describe('LifeCycle Test', () => { } } - Horizon.render(, container); + Inula.render(, container); // 初次渲染不会调用shouldComponentUpdate expect(LogUtils.getAndClear()).toEqual([]); expect(container.querySelector('p').innerHTML).toBe('1'); - Horizon.render(, container); + Inula.render(, container); // getDerivedStateFromProps判断state没有变化时,会调用shouldComponentUpdate expect(LogUtils.getAndClear()).toEqual(['shouldComponentUpdate']); expect(container.querySelector('p').innerHTML).toBe('1'); - Horizon.render(, container); + Inula.render(, container); // getDerivedStateFromProps判断state变化时,会调用shouldComponentUpdate expect(LogUtils.getAndClear()).toEqual(['shouldComponentUpdate']); expect(container.querySelector('p').innerHTML).toBe('2'); }); it('如果shouldComponentUpdate()返回值为false,则不会调用componentDidUpdate()', () => { - class App extends Horizon.Component { + class App extends Inula.Component { constructor(props) { super(props); this.state = { @@ -219,22 +219,22 @@ describe('LifeCycle Test', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('1'); - Horizon.render(, container); + Inula.render(, container); // 不会调用componentDidUpdate() expect(LogUtils.getAndClear()).toEqual([]); expect(container.querySelector('p').innerHTML).toBe('1'); - Horizon.render(, container); + Inula.render(, container); // 调用componentDidUpdate() expect(LogUtils.getAndClear()).toEqual(['componentDidUpdate']); expect(container.querySelector('p').innerHTML).toBe('2'); }); it('getSnapshotBeforeUpdate()的返回值会作为componentDidUpdate()的第三个参数', () => { - class App extends Horizon.Component { + class App extends Inula.Component { constructor(props) { super(props); this.state = { @@ -263,11 +263,11 @@ describe('LifeCycle Test', () => { return

{this.state.num}

; } } - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([]); expect(container.querySelector('p').innerHTML).toBe(''); - Horizon.render(, container); + Inula.render(, container); // Snapshot作为componentDidUpdate()的第三个参数 expect(LogUtils.getAndClear()).toEqual([ 'getSnapshotBeforeUpdate prevProps:undefined prevState:undefined', @@ -275,14 +275,14 @@ describe('LifeCycle Test', () => { ]); expect(container.querySelector('p').innerHTML).toBe('1'); - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([ 'getSnapshotBeforeUpdate prevProps:1 prevState:1', 'componentDidUpdate prevProps:1 prevState:1 snapshot:Snapshot', ]); expect(container.querySelector('p').innerHTML).toBe('1'); - Horizon.render(, container); + Inula.render(, container); expect(LogUtils.getAndClear()).toEqual([ 'getSnapshotBeforeUpdate prevProps:1 prevState:1', 'componentDidUpdate prevProps:1 prevState:1 snapshot:Snapshot', @@ -291,7 +291,7 @@ describe('LifeCycle Test', () => { }); it('无论什么原因触发了渲染,只要有渲染就会触发getDerivedStateFromProps', () => { - class App extends Horizon.Component { + class App extends Inula.Component { constructor(props) { super(props); this.state = { @@ -308,8 +308,8 @@ describe('LifeCycle Test', () => { return

{this.state.num}

; } } - let realNode = Horizon.render(, container); - realNode = Horizon.render(, container); + let realNode = Inula.render(, container); + realNode = Inula.render(, container); realNode.forceUpdate(); // 触发了3次渲染 expect(LogUtils.getAndClear()).toEqual([ @@ -321,7 +321,7 @@ describe('LifeCycle Test', () => { }); it('生命周期执行顺序', () => { - class ChildApp extends Horizon.Component { + class ChildApp extends Inula.Component { UNSAFE_componentWillMount() { LogUtils.log('Child componentWillMount'); } @@ -350,7 +350,7 @@ describe('LifeCycle Test', () => { } } - class App extends Horizon.Component { + class App extends Inula.Component { UNSAFE_componentWillMount() { LogUtils.log('componentWillMount'); } @@ -379,7 +379,7 @@ describe('LifeCycle Test', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container.textContent).toBe('1'); expect(LogUtils.getAndClear()).toEqual([ 'componentWillMount', @@ -387,7 +387,7 @@ describe('LifeCycle Test', () => { 'Child componentDidMount', 'componentDidMount' ]); - Horizon.render(, container); + Inula.render(, container); expect(container.textContent).toBe('2'); expect(LogUtils.getAndClear()).toEqual([ 'componentWillReceiveProps', @@ -399,7 +399,7 @@ describe('LifeCycle Test', () => { 'Child componentDidUpdate', 'componentDidUpdate' ]); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); expect(container.textContent).toBe(''); expect(LogUtils.getAndClear()).toEqual([ 'componentWillUnmount', @@ -408,7 +408,7 @@ describe('LifeCycle Test', () => { }); it('新生命周期执行顺序', () => { - class ChildApp extends Horizon.Component { + class ChildApp extends Inula.Component { static getDerivedStateFromProps(props, state) { LogUtils.log('Child getDerivedStateFromProps'); } @@ -434,7 +434,7 @@ describe('LifeCycle Test', () => { } } - class App extends Horizon.Component { + class App extends Inula.Component { static getDerivedStateFromProps(props, state) { LogUtils.log('getDerivedStateFromProps'); } @@ -460,7 +460,7 @@ describe('LifeCycle Test', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container.textContent).toBe('1'); expect(LogUtils.getAndClear()).toEqual([ 'getDerivedStateFromProps', @@ -468,7 +468,7 @@ describe('LifeCycle Test', () => { 'Child componentDidMount', 'componentDidMount' ]); - Horizon.render(, container); + Inula.render(, container); expect(container.textContent).toBe('2'); expect(LogUtils.getAndClear()).toEqual([ 'getDerivedStateFromProps', @@ -480,7 +480,7 @@ describe('LifeCycle Test', () => { 'Child componentDidUpdate', 'componentDidUpdate' ]); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); expect(container.textContent).toBe(''); expect(LogUtils.getAndClear()).toEqual([ 'componentWillUnmount', diff --git a/scripts/__tests__/ComponentTest/Memo.test.js b/scripts/__tests__/ComponentTest/Memo.test.js index 8ff8cd20..2af413b4 100644 --- a/scripts/__tests__/ComponentTest/Memo.test.js +++ b/scripts/__tests__/ComponentTest/Memo.test.js @@ -13,18 +13,18 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; describe('Memo Test', () => { it('Memo should not make the path wrong', function () { let updateApp; function Child() { - const [_, update] = Horizon.useState({}); + const [_, update] = Inula.useState({}); updateApp = () => update({}); return
; } - const MemoChild = Horizon.memo(Child); + const MemoChild = Inula.memo(Child); function App() { return ( @@ -33,14 +33,14 @@ describe('Memo Test', () => {
); } - const MemoApp = Horizon.memo(App); - Horizon.render( + const MemoApp = Inula.memo(App); + Inula.render(
, container ); - Horizon.render( + Inula.render(
diff --git a/scripts/__tests__/ComponentTest/PortalComponent.test.js b/scripts/__tests__/ComponentTest/PortalComponent.test.js index 808689f5..2fc35030 100755 --- a/scripts/__tests__/ComponentTest/PortalComponent.test.js +++ b/scripts/__tests__/ComponentTest/PortalComponent.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from '../jest/testUtils'; import dispatchChangeEvent from '../utils/dispatchChangeEvent'; @@ -23,23 +23,23 @@ describe('PortalComponent Test', () => { it('将子节点渲染到存在于父组件以外的 DOM 节点', () => { const portalRoot = document.createElement('div'); - class PortalApp extends Horizon.Component { + class PortalApp extends Inula.Component { constructor(props) { super(props); this.element = portalRoot; } render() { - return Horizon.createPortal(this.props.child, this.element); + return Inula.createPortal(this.props.child, this.element); } } - Horizon.render(PortalApp
} />, container); + Inula.render(PortalApp} />, container); expect(container.textContent).toBe(''); //
PortalApp
被渲染到了portalRoot而非container expect(portalRoot.textContent).toBe('PortalApp'); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); expect(container.textContent).toBe(''); expect(portalRoot.textContent).toBe(''); }); @@ -48,7 +48,7 @@ describe('PortalComponent Test', () => { const portalRoot1st = document.createElement('div'); const portalRoot2nd = document.createElement('div'); - class PortalApp extends Horizon.Component { + class PortalApp extends Inula.Component { constructor(props) { super(props); this.element = portalRoot1st; @@ -57,19 +57,19 @@ describe('PortalComponent Test', () => { render() { return [ - Horizon.createPortal(this.props.child, this.element), - Horizon.createPortal(this.props.child, this.newElement), + Inula.createPortal(this.props.child, this.element), + Inula.createPortal(this.props.child, this.newElement), ]; } } - Horizon.render(PortalApp} />, container); + Inula.render(PortalApp} />, container); expect(container.textContent).toBe(''); //
PortalApp
被渲染到了portalRoot而非container expect(portalRoot1st.textContent).toBe('PortalApp'); expect(portalRoot2nd.textContent).toBe('PortalApp'); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); expect(container.textContent).toBe(''); expect(portalRoot1st.textContent).toBe(''); expect(portalRoot2nd.textContent).toBe(''); @@ -80,7 +80,7 @@ describe('PortalComponent Test', () => { const portalRoot2nd = document.createElement('div'); const portalRoot3rd = document.createElement('div'); - class PortalApp extends Horizon.Component { + class PortalApp extends Inula.Component { constructor(props) { super(props); this.element = portalRoot1st; @@ -91,24 +91,24 @@ describe('PortalComponent Test', () => { render() { return [
PortalApp1st
, - Horizon.createPortal( - [
PortalApp4
, Horizon.createPortal(this.props.child, this.element3rd)], + Inula.createPortal( + [
PortalApp4
, Inula.createPortal(this.props.child, this.element3rd)], this.element ),
PortalApp2nd
, - Horizon.createPortal(this.props.child, this.newElement), + Inula.createPortal(this.props.child, this.newElement), ]; } } - Horizon.render(PortalApp} />, container); + Inula.render(PortalApp} />, container); expect(container.textContent).toBe('PortalApp1stPortalApp2nd'); //
PortalApp4
会挂载在this.element上 expect(portalRoot1st.textContent).toBe('PortalApp4'); expect(portalRoot2nd.textContent).toBe('PortalApp'); expect(portalRoot3rd.textContent).toBe('PortalApp'); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); expect(container.textContent).toBe(''); expect(portalRoot1st.textContent).toBe(''); expect(portalRoot2nd.textContent).toBe(''); @@ -117,50 +117,50 @@ describe('PortalComponent Test', () => { it('改变Portal的参数', () => { const portalRoot = document.createElement('div'); - class PortalApp extends Horizon.Component { + class PortalApp extends Inula.Component { constructor(props) { super(props); this.element = portalRoot; } render() { - return Horizon.createPortal(this.props.child, this.element); + return Inula.createPortal(this.props.child, this.element); } } - Horizon.render(PortalApp} />, container); + Inula.render(PortalApp} />, container); expect(container.textContent).toBe(''); expect(portalRoot.textContent).toBe('PortalApp'); - Horizon.render(AppPortal} />, container); + Inula.render(AppPortal} />, container); expect(container.textContent).toBe(''); expect(portalRoot.textContent).toBe('AppPortal'); - Horizon.render(, container); + Inula.render(, container); expect(container.textContent).toBe(''); expect(portalRoot.textContent).toBe('portal'); - Horizon.render(, container); + Inula.render(, container); expect(container.textContent).toBe(''); expect(portalRoot.textContent).toBe(''); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); expect(container.textContent).toBe(''); expect(portalRoot.textContent).toBe(''); }); it('通过Portal进行事件冒泡', () => { const portalRoot = document.createElement('div'); - const buttonRef = Horizon.createRef(); + const buttonRef = Inula.createRef(); - class PortalApp extends Horizon.Component { + class PortalApp extends Inula.Component { constructor(props) { super(props); this.element = portalRoot; } render() { - return Horizon.createPortal(this.props.child, this.element); + return Inula.createPortal(this.props.child, this.element); } } @@ -187,7 +187,7 @@ describe('PortalComponent Test', () => { ); }; - Horizon.render(, container); + Inula.render(, container); const event = document.createEvent('Event'); event.initEvent('click', true, true); buttonRef.current.dispatchEvent(event); @@ -200,21 +200,21 @@ describe('PortalComponent Test', () => { }); it('Create portal at app root should not add event listener multiple times', () => { - const btnRef = Horizon.createRef(); + const btnRef = Inula.createRef(); - class PortalApp extends Horizon.Component { + class PortalApp extends Inula.Component { constructor(props) { super(props); } render() { - return Horizon.createPortal(this.props.child, container); + return Inula.createPortal(this.props.child, container); } } const onClick = jest.fn(); - class App extends Horizon.Component { + class App extends Inula.Component { constructor(props) { super(props); } @@ -229,13 +229,13 @@ describe('PortalComponent Test', () => { } } - Horizon.render(, container); + Inula.render(, container); btnRef.current.click(); expect(onClick).toHaveBeenCalledTimes(1); }); it('#76 Portal onChange should activate', () => { - class Dialog extends Horizon.Component { + class Dialog extends Inula.Component { node; constructor(props) { @@ -245,20 +245,20 @@ describe('PortalComponent Test', () => { } render() { - return Horizon.createPortal(this.props.children, this.node); + return Inula.createPortal(this.props.children, this.node); } } let showPortalInput; const fn = jest.fn(); - const inputRef = Horizon.createRef(); + const inputRef = Inula.createRef(); function App() { const Input = () => { - const [show, setShow] = Horizon.useState(false); + const [show, setShow] = Inula.useState(false); showPortalInput = setShow; - Horizon.useEffect(() => { + Inula.useEffect(() => { setTimeout(() => { setShow(true); }, 0); @@ -280,7 +280,7 @@ describe('PortalComponent Test', () => { ); } - Horizon.render(, container); + Inula.render(, container); showPortalInput(true); jest.advanceTimersToNextTimer(); dispatchChangeEvent(inputRef.current, 'test'); diff --git a/scripts/__tests__/ComponentTest/SuspenseComponent.test.js b/scripts/__tests__/ComponentTest/SuspenseComponent.test.js index 753373bc..37279de8 100755 --- a/scripts/__tests__/ComponentTest/SuspenseComponent.test.js +++ b/scripts/__tests__/ComponentTest/SuspenseComponent.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { Text } from '../jest/commonComponents'; import { getLogUtils } from '../jest/testUtils'; @@ -42,18 +42,18 @@ describe('SuspenseComponent Test', () => { it('挂载lazy组件', async () => { // 用同步的代码来实现异步操作 - class LazyComponent extends Horizon.Component { + class LazyComponent extends Inula.Component { render() { return ; } } - const Lazy = Horizon.lazy(() => mockImport(LazyComponent)); + const Lazy = Inula.lazy(() => mockImport(LazyComponent)); - Horizon.render( - }> + Inula.render( + }> - , + , container ); @@ -61,10 +61,10 @@ describe('SuspenseComponent Test', () => { expect(container.textContent).toBe('Loading...'); await Promise.resolve(); - Horizon.render( - }> + Inula.render( + }> - , + , container ); expect(LogUtils.getAndClear()).toEqual([5]); diff --git a/scripts/__tests__/DomTest/Attribute.test.js b/scripts/__tests__/DomTest/Attribute.test.js index 06706ede..bf560e0e 100755 --- a/scripts/__tests__/DomTest/Attribute.test.js +++ b/scripts/__tests__/DomTest/Attribute.test.js @@ -13,48 +13,48 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; describe('Dom Attribute', () => { it('属性值为null或undefined时,不会设置此属性', () => { - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').getAttribute('id')).toBe('div'); - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').hasAttribute('id')).toBe(false); - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').hasAttribute('id')).toBe(false); }); it('可以设置未知的属性', () => { - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').hasAttribute('abcd')).toBe(true); expect(container.querySelector('div').getAttribute('abcd')).toBe('abcd'); }); it('未知属性的值为null或undefined时,不会设置此属性', () => { - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').hasAttribute('abcd')).toBe(false); - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').hasAttribute('abcd')).toBe(false); }); it('未知属性的值为数字时,属性值会转为字符串', () => { - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').getAttribute('abcd')).toBe('0'); - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').getAttribute('abcd')).toBe('-3'); - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').getAttribute('abcd')).toBe('123.45'); }); it('访问节点的标准属性时可以拿到属性值,访问节点的非标准属性时会得到undefined', () => { - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').id).toBe('div'); expect(container.querySelector('div').abcd).toBe(undefined); }); it('特性方法', () => { - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').hasAttribute('abcd')).toBe(true); expect(container.querySelector('div').getAttribute('abcd')).toBe('0'); container.querySelector('div').setAttribute('abcd', 4); @@ -64,7 +64,7 @@ describe('Dom Attribute', () => { }); it('特性大小写不敏感', () => { - Horizon.render(
, container); + Inula.render(
, container); expect(container.querySelector('div').hasAttribute('abcd')).toBe(true); expect(container.querySelector('div').hasAttribute('ABCD')).toBe(true); expect(container.querySelector('div').getAttribute('abcd')).toBe('0'); @@ -72,19 +72,19 @@ describe('Dom Attribute', () => { }); it('使用 data- 开头的特性时,会映射到DOM的dataset属性且中划线格式会变成驼峰格式', () => { - Horizon.render(
, container); + Inula.render(
, container); container.querySelector('div').setAttribute('data-first-name', 'Tom'); expect(container.querySelector('div').dataset.firstName).toBe('Tom'); }); it('style 自动加px', () => { - const div = Horizon.render(
, container); + const div = Inula.render(
, container); expect(window.getComputedStyle(div).getPropertyValue('width')).toBe('10px'); expect(window.getComputedStyle(div).getPropertyValue('height')).toBe('20px'); }); it('WebkitLineClamp和lineClamp样式不会把数字转换成字符串或者追加"px"', () => { - Horizon.render(
, container); + Inula.render(
, container); // 浏览器可以将WebkitLineClamp识别为-webkit-line-clamp,测试框架不可以 expect(container.querySelector('div').style.WebkitLineClamp).toBe(2); }); @@ -92,7 +92,7 @@ describe('Dom Attribute', () => { it('空字符串做属性名', () => { const emptyStringProps = { '': '' }; expect(() => { - Horizon.render(
, container); + Inula.render(
, container); }).not.toThrow(); }); }); diff --git a/scripts/__tests__/DomTest/DomInput.test.js b/scripts/__tests__/DomTest/DomInput.test.js index ef9964d2..98b79119 100755 --- a/scripts/__tests__/DomTest/DomInput.test.js +++ b/scripts/__tests__/DomTest/DomInput.test.js @@ -14,37 +14,37 @@ */ /* eslint-disable @typescript-eslint/no-empty-function */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from '../jest/testUtils'; describe('Dom Input', () => { - const { act } = Horizon; + const { act } = Inula; const LogUtils = getLogUtils(); describe('type checkbox', () => { it('没有设置checked属性时,控制台不会报错', () => { expect(() => - Horizon.render(, container), + Inula.render(, container), ).not.toThrow(); }); it('checked属性为undefined或null时且没有onChange属性或没有readOnly={true},控制台不会报错', () => { expect(() => - Horizon.render(, container), + Inula.render(, container), ).not.toThrow(); expect(() => - Horizon.render(, container), + Inula.render(, container), ).not.toThrow(); }); it('复选框的value属性值可以改变', () => { - Horizon.render( + Inula.render( { LogUtils.log('checkbox click'); }} />, container, ); - Horizon.render( + Inula.render( { LogUtils.log('checkbox click'); }} />, @@ -56,7 +56,7 @@ describe('Dom Input', () => { }); it('复选框不设置value属性值时会设置value为"on"', () => { - Horizon.render( + Inula.render( , container, ); @@ -65,21 +65,21 @@ describe('Dom Input', () => { }); it('测试defaultChecked与更改defaultChecked', () => { - Horizon.render( + Inula.render( , container, ); expect(container.querySelector('input').value).toBe('on'); expect(container.querySelector('input').checked).toBe(false); - Horizon.render( + Inula.render( , container, ); expect(container.querySelector('input').value).toBe('on'); expect(container.querySelector('input').checked).toBe(true); - Horizon.render( + Inula.render( , container, ); @@ -91,86 +91,86 @@ describe('Dom Input', () => { describe('type text', () => { it('value属性为undefined或null时且没有onChange属性或没有readOnly={true},控制台不会报错', () => { expect(() => - Horizon.render(, container), + Inula.render(, container), ).not.toThrow(); expect(() => - Horizon.render(, container), + Inula.render(, container), ).not.toThrow(); expect(() => - Horizon.render(, container), + Inula.render(, container), ).not.toThrow(); }); it('value值会转为字符串', () => { - const realNode = Horizon.render(, container); + const realNode = Inula.render(, container); expect(realNode.value).toBe('1'); }); it('value值可以被设置为true/false', () => { - let realNode = Horizon.render(, container); + let realNode = Inula.render(, container); expect(realNode.value).toBe('1'); - realNode = Horizon.render(, container); + realNode = Inula.render(, container); expect(realNode.value).toBe('true'); - realNode = Horizon.render(, container); + realNode = Inula.render(, container); expect(realNode.value).toBe('false'); }); it('value值可以被设置为object', () => { - let realNode = Horizon.render(, container); + let realNode = Inula.render(, container); expect(realNode.value).toBe('1'); const value = { toString: () => { return 'value'; } }; - realNode = Horizon.render(, container); + realNode = Inula.render(, container); expect(realNode.value).toBe('value'); }); it('设置defaultValue', () => { - let realNode = Horizon.render(, container); + let realNode = Inula.render(, container); expect(realNode.value).toBe('1'); expect(realNode.getAttribute('value')).toBe('1'); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); // 测试defaultValue为boolean类型 - realNode = Horizon.render(, container); + realNode = Inula.render(, container); expect(realNode.value).toBe('true'); expect(realNode.getAttribute('value')).toBe('true'); - Horizon.unmountComponentAtNode(container); - realNode = Horizon.render(, container); + Inula.unmountComponentAtNode(container); + realNode = Inula.render(, container); expect(realNode.value).toBe('false'); expect(realNode.getAttribute('value')).toBe('false'); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); const value = { toString: () => { return 'default'; } }; - realNode = Horizon.render(, container); + realNode = Inula.render(, container); expect(realNode.value).toBe('default'); expect(realNode.getAttribute('value')).toBe('default'); }); it('value为0、defaultValue为1,input 的value应该为0', () => { - const input = Horizon.render(, container); + const input = Inula.render(, container); expect(input.getAttribute('value')).toBe('0'); }); it('name属性', () => { - let realNode = Horizon.render(, container); + let realNode = Inula.render(, container); expect(realNode.name).toBe('name'); expect(realNode.getAttribute('name')).toBe('name'); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); // 没有设置name属性 - realNode = Horizon.render(, container); + realNode = Inula.render(, container); expect(realNode.name).toBe(''); expect(realNode.getAttribute('name')).toBe(null); }); it('受控input可以触发onChange', () => { - let realNode = Horizon.render(, container); + let realNode = Inula.render(, container); Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, 'value', @@ -189,19 +189,19 @@ describe('Dom Input', () => { describe('type radio', () => { it('radio的value可以更新', () => { - let realNode = Horizon.render(, container); + let realNode = Inula.render(, container); expect(realNode.value).toBe(''); expect(realNode.getAttribute('value')).toBe(''); - realNode = Horizon.render(, container); + realNode = Inula.render(, container); expect(realNode.value).toBe('false'); expect(realNode.getAttribute('value')).toBe('false'); - realNode = Horizon.render(, container); + realNode = Inula.render(, container); expect(realNode.value).toBe('true'); expect(realNode.getAttribute('value')).toBe('true'); }); it('相同name且在同一表单的radio互斥', () => { - Horizon.render( + Inula.render( <> @@ -231,9 +231,9 @@ describe('Dom Input', () => { }); it('name改变不影响相同name的radio', () => { - const inputRef = Horizon.createRef(); + const inputRef = Inula.createRef(); const App = () => { - const [isNum, setNum] = Horizon.useState(false); + const [isNum, setNum] = Inula.useState(false); const inputName = isNum ? 'secondName' : 'firstName'; const buttonClick = () => { @@ -259,7 +259,7 @@ describe('Dom Input', () => {
); }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').checked).toBe(false); expect(inputRef.current.checked).toBe(true); // 点击button,触发setNum @@ -271,15 +271,15 @@ describe('Dom Input', () => { describe('type submit', () => { it('type submit value', () => { - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(false); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(true); expect(container.querySelector('input').getAttribute('value')).toBe(''); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(true); expect(container.querySelector('input').getAttribute('value')).toBe('submit'); }); @@ -287,15 +287,15 @@ describe('Dom Input', () => { describe('type reset', () => { it('type reset value', () => { - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(false); - Horizon.unmountComponentAtNode(container); + Inula.unmountComponentAtNode(container); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(true); expect(container.querySelector('input').getAttribute('value')).toBe(''); - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(true); expect(container.querySelector('input').getAttribute('value')).toBe('reset'); }); @@ -303,13 +303,13 @@ describe('Dom Input', () => { describe('type number', () => { it('value值会把number类型转为字符串,且.xx转为0.xx', () => { - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(true); expect(container.querySelector('input').getAttribute('value')).toBe('0.12'); }); it('value值会把number类型转为字符串,且.xx转为0.xx', () => { - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(true); expect(container.querySelector('input').getAttribute('value')).toBe('0.12'); }); @@ -317,11 +317,11 @@ describe('Dom Input', () => { it('改变node.value值', () => { let setNum; const App = () => { - const [num, _setNum] = Horizon.useState(''); + const [num, _setNum] = Inula.useState(''); setNum = _setNum; return ; }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(true); expect(container.querySelector('input').getAttribute('value')).toBe(''); act(() => { @@ -333,11 +333,11 @@ describe('Dom Input', () => { it('node.value精度', () => { let setNum; const App = () => { - const [num, _setNum] = Horizon.useState(0.0000); + const [num, _setNum] = Inula.useState(0.0000); setNum = _setNum; return ; }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').getAttribute('value')).toBe('0'); act(() => { setNum(1.0000); @@ -350,7 +350,7 @@ describe('Dom Input', () => { const App = () => { return ; }; - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').getAttribute('value')).toBe('1'); expect(container.querySelector('input').value).toBe('1'); @@ -374,17 +374,17 @@ describe('Dom Input', () => { describe('type reset', () => { it('type reset的value值', () => { - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(true); expect(container.querySelector('input').getAttribute('value')).toBe('0.12'); - Horizon.unmountComponentAtNode(container); - Horizon.render(, container); + Inula.unmountComponentAtNode(container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(true); expect(container.querySelector('input').getAttribute('value')).toBe(''); - Horizon.unmountComponentAtNode(container); - Horizon.render(, container); + Inula.unmountComponentAtNode(container); + Inula.render(, container); expect(container.querySelector('input').hasAttribute('value')).toBe(false); }); }); diff --git a/scripts/__tests__/DomTest/DomSelect.test.js b/scripts/__tests__/DomTest/DomSelect.test.js index a126ab4c..4a718601 100755 --- a/scripts/__tests__/DomTest/DomSelect.test.js +++ b/scripts/__tests__/DomTest/DomSelect.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; describe('Dom Select', () => { it('设置value', () => { @@ -24,12 +24,12 @@ describe('Dom Select', () => { ); - const realNode = Horizon.render(selectNode, container); + const realNode = Inula.render(selectNode, container); expect(realNode.value).toBe('Vue'); expect(realNode.options[1].selected).toBe(true); realNode.value = 'React'; // 改变value会影响select的状态 - Horizon.render(selectNode, container); + Inula.render(selectNode, container); expect(realNode.options[0].selected).toBe(true); expect(realNode.value).toBe('React'); }); @@ -47,7 +47,7 @@ describe('Dom Select', () => { ); - const realNode = Horizon.render(selectNode, container); + const realNode = Inula.render(selectNode, container); expect(realNode.value).toBe('Vue'); expect(realNode.options[1].selected).toBe(true); selectValue = { @@ -63,7 +63,7 @@ describe('Dom Select', () => { ); // 改变value会影响select的状态 - Horizon.render(newSelectNode, container); + Inula.render(newSelectNode, container); expect(realNode.options[0].selected).toBe(true); expect(realNode.value).toBe('React'); }); @@ -76,7 +76,7 @@ describe('Dom Select', () => { ); - const realNode = Horizon.render(selectNode, container); + const realNode = Inula.render(selectNode, container); expect(realNode.value).toBe('Vue'); expect(realNode.options[1].selected).toBe(true); const newSelectNode = ( @@ -86,7 +86,7 @@ describe('Dom Select', () => { ); - Horizon.render(newSelectNode, container); + Inula.render(newSelectNode, container); // selected不变 expect(realNode.options[0].selected).toBe(false); expect(realNode.options[1].selected).toBe(true); @@ -102,13 +102,13 @@ describe('Dom Select', () => { ); - let realNode = Horizon.render(selectNode, container); + let realNode = Inula.render(selectNode, container); expect(realNode.value).toBe('Vue'); expect(realNode.options[1].selected).toBe(true); defaultVal = 'React'; // 改变defaultValue没有影响 - realNode = Horizon.render(selectNode, container); + realNode = Inula.render(selectNode, container); expect(realNode.value).toBe('Vue'); expect(realNode.options[0].selected).toBe(false); expect(realNode.options[1].selected).toBe(true); @@ -122,7 +122,7 @@ describe('Dom Select', () => { ); - let realNode = Horizon.render(selectNode, container); + let realNode = Inula.render(selectNode, container); expect(realNode.value).toBe('Vue'); expect(realNode.options[1].selected).toBe(true); @@ -139,7 +139,7 @@ describe('Dom Select', () => { }), ); // 鼠标改变受控select生效,select不受控 - Horizon.render(selectNode, container); + Inula.render(selectNode, container); // 'React'项没被选中 expect(realNode.options[0].selected).toBe(true); expect(realNode.options[1].selected).toBe(false); @@ -156,7 +156,7 @@ describe('Dom Select', () => { ); expect( - () => Horizon.render(selectNode, container) + () => Inula.render(selectNode, container) ).toThrowError('newValues.forEach is not a function'); }); @@ -169,7 +169,7 @@ describe('Dom Select', () => { ); expect( - () => Horizon.render(selectNode, container) + () => Inula.render(selectNode, container) ).not.toThrow(); expect(document.getElementById('se').options[0].selected).toBe(false); expect(document.getElementById('se').options[1].selected).toBe(true); @@ -183,7 +183,7 @@ describe('Dom Select', () => { ); - Horizon.render(selectNode, container); + Inula.render(selectNode, container); expect(document.getElementById('se').options[0].selected).toBe(false); expect(document.getElementById('se').options[1].selected).toBe(true); expect(document.getElementById('se').options[2].selected).toBe(true); @@ -198,7 +198,7 @@ describe('Dom Select', () => { ); expect( - () => Horizon.render(selectNode, container) + () => Inula.render(selectNode, container) ).not.toThrow(); expect(document.getElementById('se').options[0].selected).toBe(false); expect(document.getElementById('se').options[1].selected).toBe(true); @@ -212,7 +212,7 @@ describe('Dom Select', () => { ); - Horizon.render(selectNode, container); + Inula.render(selectNode, container); expect(document.getElementById('se').options[0].selected).toBe(true); expect(document.getElementById('se').options[1].selected).toBe(false); expect(document.getElementById('se').options[2].selected).toBe(false); @@ -226,7 +226,7 @@ describe('Dom Select', () => { ); - Horizon.render(selectNode, container); + Inula.render(selectNode, container); expect(document.getElementById('se').options[0].selected).toBe(false); expect(document.getElementById('se').options[1].selected).toBe(true); expect(document.getElementById('se').options[2].selected).toBe(true); @@ -239,7 +239,7 @@ describe('Dom Select', () => { ); - Horizon.render(selectNode, container); + Inula.render(selectNode, container); expect(document.getElementById('se').options[0].selected).toBe(true); expect(document.getElementById('se').options[1].selected).toBe(false); expect(document.getElementById('se').options[2].selected).toBe(false); @@ -253,7 +253,7 @@ describe('Dom Select', () => { ); - Horizon.render(selectNode, container); + Inula.render(selectNode, container); expect(document.getElementById('se').options[0].selected).toBe(true); expect(document.getElementById('se').options[1].selected).toBe(false); expect(document.getElementById('se').options[2].selected).toBe(false); @@ -266,7 +266,7 @@ describe('Dom Select', () => { ); - Horizon.render(selectNode, container); + Inula.render(selectNode, container); expect(document.getElementById('se').options[0].selected).toBe(false); expect(document.getElementById('se').options[1].selected).toBe(true); expect(document.getElementById('se').options[2].selected).toBe(true); @@ -280,7 +280,7 @@ describe('Dom Select', () => { ); - const realNode = Horizon.render(selectNode, container); + const realNode = Inula.render(selectNode, container); expect(realNode.options[0].selected).toBe(false); expect(realNode.options[1].selected).toBe(true); expect(realNode.options[2].selected).toBe(false); @@ -294,7 +294,7 @@ describe('Dom Select', () => { ); - const realNode = Horizon.render(selectNode, container); + const realNode = Inula.render(selectNode, container); expect(realNode.options[0].selected).toBe(false); expect(realNode.options[1].selected).toBe(true); expect(realNode.options[2].selected).toBe(false); @@ -305,7 +305,7 @@ describe('Dom Select', () => { ); - Horizon.render(newNode, container); + Inula.render(newNode, container); expect(realNode.options[0].selected).toBe(false); expect(realNode.options[1].selected).toBe(false); @@ -317,7 +317,7 @@ describe('Dom Select', () => { ); // 重新添加不会影响 - Horizon.render(newSelectNode, container); + Inula.render(newSelectNode, container); expect(realNode.options[0].selected).toBe(false); expect(realNode.options[1].selected).toBe(false); expect(realNode.options[2].selected).toBe(false); diff --git a/scripts/__tests__/DomTest/DomTextarea.test.js b/scripts/__tests__/DomTest/DomTextarea.test.js index 5944a92a..000e9644 100644 --- a/scripts/__tests__/DomTest/DomTextarea.test.js +++ b/scripts/__tests__/DomTest/DomTextarea.test.js @@ -13,20 +13,20 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; describe('Dom Textarea', () => { it('设置value', () => { - let realNode = Horizon.render(, container); + Inula.render(, container); expect(realNode.value).toBe('false'); }); @@ -92,14 +92,14 @@ describe('Dom Textarea', () => { const textareaNode = ( , container); + let realNode = Inula.render(, container); expect(realNode.value).toBe('1234'); - realNode = Horizon.render(, container); + realNode = Inula.render(, container); // realNode.value依旧为1234 expect(realNode.value).toBe('1234'); }); diff --git a/scripts/__tests__/EventTest/EventMain.test.js b/scripts/__tests__/EventTest/EventMain.test.js index e0341d72..c189f7de 100644 --- a/scripts/__tests__/EventTest/EventMain.test.js +++ b/scripts/__tests__/EventTest/EventMain.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import * as TestUtils from '../jest/testUtils'; function dispatchChangeEvent(input) { @@ -38,7 +38,7 @@ describe('事件', () => { ); }; - Horizon.render(, container); + Inula.render(, container); const a = container.querySelector('button'); a.click(); expect(LogUtils.getAndClear()).toEqual([ @@ -54,7 +54,7 @@ describe('事件', () => { it('returns 0', () => { let keyCode = null; - const node = Horizon.render( + const node = Inula.render( { keyCode = e.keyCode; @@ -87,7 +87,7 @@ describe('事件', () => { ); }; - Horizon.render(, container); + Inula.render(, container); container.querySelector('button').click(); expect(LogUtils.getAndClear()).toEqual([ @@ -114,7 +114,7 @@ describe('事件', () => { ); }; - Horizon.render(, container); + Inula.render(, container); container.querySelector('button').click(); expect(LogUtils.getAndClear()).toEqual([ @@ -133,7 +133,7 @@ describe('事件', () => {
); }; - Horizon.render(, container); + Inula.render(, container); container.querySelector('div').addEventListener( 'click', () => { @@ -162,15 +162,15 @@ describe('事件', () => { it('动态增加事件', () => { let update; - let inputRef = Horizon.createRef(); + let inputRef = Inula.createRef(); function Test() { - const [inputProps, setProps] = Horizon.useState({}); + const [inputProps, setProps] = Inula.useState({}); update = setProps; return ; } - Horizon.render(, container); + Inula.render(, container); update({ onChange: () => { LogUtils.log('change'); @@ -193,10 +193,10 @@ describe('事件', () => { radio2Called++; } - const radio1Ref = Horizon.createRef(); - const radio2Ref = Horizon.createRef(); + const radio1Ref = Inula.createRef(); + const radio2Ref = Inula.createRef(); - Horizon.render( + Inula.render( <> @@ -228,7 +228,7 @@ describe('事件', () => { let input1, input2, update1, update2; function App1() { - const [props, setProps] = Horizon.useState({}); + const [props, setProps] = Inula.useState({}); update1 = setProps; return ( { } function App2() { - const [props, setProps] = Horizon.useState({}); + const [props, setProps] = Inula.useState({}); update2 = setProps; return ( @@ -257,8 +257,8 @@ describe('事件', () => { } // 多根mount阶段挂载onChange事件 - Horizon.render(, root1); - Horizon.render(, root2); + Inula.render(, root1); + Inula.render(, root2); dispatchChangeEvent(input1); expect(LogUtils.getAndClear()).toEqual(['input1 changed']); diff --git a/scripts/__tests__/EventTest/FocusEvent.test.js b/scripts/__tests__/EventTest/FocusEvent.test.js index 10978d2d..b0f2954c 100644 --- a/scripts/__tests__/EventTest/FocusEvent.test.js +++ b/scripts/__tests__/EventTest/FocusEvent.test.js @@ -13,14 +13,14 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from '../jest/testUtils'; describe('合成焦点事件', () => { const LogUtils = getLogUtils(); it('onFocus', () => { - const realNode = Horizon.render( + const realNode = Inula.render( LogUtils.log(`onFocus: ${event.type}`)} onFocusCapture={event => LogUtils.log(`onFocusCapture: ${event.type}`)} @@ -40,7 +40,7 @@ describe('合成焦点事件', () => { }); it('onBlur', () => { - const realNode = Horizon.render( + const realNode = Inula.render( LogUtils.log(`onBlur: ${event.type}`)} onBlurCapture={event => LogUtils.log(`onBlurCapture: ${event.type}`)} diff --git a/scripts/__tests__/EventTest/KeyboardEvent.test.js b/scripts/__tests__/EventTest/KeyboardEvent.test.js index 675ef450..e6077aca 100644 --- a/scripts/__tests__/EventTest/KeyboardEvent.test.js +++ b/scripts/__tests__/EventTest/KeyboardEvent.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import {getLogUtils} from '../jest/testUtils'; describe('Keyboard Event', () => { @@ -29,7 +29,7 @@ describe('Keyboard Event', () => { }; it('keydown,keypress,keyup的keycode,charcode', () => { - const node = Horizon.render( + const node = Inula.render( { LogUtils.log('onKeyUp: keycode: ' + e.keyCode + ',charcode: ' + e.charCode); @@ -50,7 +50,7 @@ describe('Keyboard Event', () => { }); it('keypress的keycode,charcode', () => { - const node = Horizon.render( + const node = Inula.render( { LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode); @@ -66,7 +66,7 @@ describe('Keyboard Event', () => { }); it('当charcode为13,且不设置keycode的时候', () => { - const node = Horizon.render( + const node = Inula.render( { LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode); @@ -81,7 +81,7 @@ describe('Keyboard Event', () => { }); it('keydown,keypress,keyup的code', () => { - const node = Horizon.render( + const node = Inula.render( { LogUtils.log('onKeyUp: code: ' + e.code); @@ -124,7 +124,7 @@ describe('Keyboard Event', () => { expect(e.isPropagationStopped()).toBe(true); LogUtils.log(e.type + ' handle'); }; - const div = Horizon.render( + const div = Inula.render(
{ let container; @@ -39,7 +39,7 @@ describe('mouseenter和mouseleave事件测试', () => { iframeDocument.close(); const leaveEvents = []; - const node = Horizon.render( + const node = Inula.render(
{ e.persist(); @@ -72,7 +72,7 @@ describe('mouseenter和mouseleave事件测试', () => { iframeDocument.close(); const enterEvents = []; - const node = Horizon.render( + const node = Inula.render(
{ e.persist(); @@ -100,7 +100,7 @@ describe('mouseenter和mouseleave事件测试', () => { let childEnterCalls = 0; let parent = null; - class Parent extends Horizon.Component { + class Parent extends Inula.Component { render() { return (
{ } } - Horizon.render(, container); - Horizon.render(, container); + Inula.render(, container); + Inula.render(, container); parent.dispatchEvent( new MouseEvent('mouseout', { @@ -133,14 +133,14 @@ describe('mouseenter和mouseleave事件测试', () => { const mockFn2 = jest.fn(); const mockFn3 = jest.fn(); - class Parent extends Horizon.Component { + class Parent extends Inula.Component { constructor(props) { super(props); - this.parentEl = Horizon.createRef(); + this.parentEl = Inula.createRef(); } componentDidMount() { - Horizon.render(, this.parentEl.current); + Inula.render(, this.parentEl.current); } render() { @@ -148,11 +148,11 @@ describe('mouseenter和mouseleave事件测试', () => { } } - class MouseEnterDetect extends Horizon.Component { + class MouseEnterDetect extends Inula.Component { constructor(props) { super(props); - this.firstEl = Horizon.createRef(); - this.siblingEl = Horizon.createRef(); + this.firstEl = Inula.createRef(); + this.siblingEl = Inula.createRef(); } componentDidMount() { @@ -171,28 +171,28 @@ describe('mouseenter和mouseleave事件测试', () => { render() { return ( - +
- + ); } } - Horizon.render(, container); + Inula.render(, container); }); - it('未被horizon管理的节点触发mouseout事件,mouseenter事件也能正常触发', done => { + it('未被inula管理的节点触发mouseout事件,mouseenter事件也能正常触发', done => { const mockFn = jest.fn(); - class Parent extends Horizon.Component { + class Parent extends Inula.Component { constructor(props) { super(props); - this.parentEl = Horizon.createRef(); + this.parentEl = Inula.createRef(); } componentDidMount() { - Horizon.render(, this.parentEl.current); + Inula.render(, this.parentEl.current); } render() { @@ -200,11 +200,11 @@ describe('mouseenter和mouseleave事件测试', () => { } } - class MouseEnterDetect extends Horizon.Component { + class MouseEnterDetect extends Inula.Component { constructor(props) { super(props); - this.divRef = Horizon.createRef(); - this.siblingEl = Horizon.createRef(); + this.divRef = Inula.createRef(); + this.siblingEl = Inula.createRef(); } componentDidMount() { @@ -230,22 +230,22 @@ describe('mouseenter和mouseleave事件测试', () => { } } - Horizon.render(, container); + Inula.render(, container); }); it('外部portal节点触发的mouseout事件,根节点的mouseleave事件也能响应', () => { - const divRef = Horizon.createRef(); + const divRef = Inula.createRef(); const onMouseLeave = jest.fn(); function Component() { return (
- {Horizon.createPortal(
, document.body)} + {Inula.createPortal(
, document.body)}
); } - Horizon.render(, container); + Inula.render(, container); divRef.current.dispatchEvent( new MouseEvent('mouseout', { @@ -259,14 +259,14 @@ describe('mouseenter和mouseleave事件测试', () => { }); it('外部portal节点触发的mouseout事件,根节点的mouseEnter事件也能响应', () => { - const divRef = Horizon.createRef(); - const otherDivRef = Horizon.createRef(); + const divRef = Inula.createRef(); + const otherDivRef = Inula.createRef(); const onMouseEnter = jest.fn(); function Component() { return (
- {Horizon.createPortal( + {Inula.createPortal(
, document.body, )} @@ -274,7 +274,7 @@ describe('mouseenter和mouseleave事件测试', () => { ); } - Horizon.render(, container); + Inula.render(, container); divRef.current.dispatchEvent( new MouseEvent('mouseout', { diff --git a/scripts/__tests__/EventTest/MouseEvent.test.js b/scripts/__tests__/EventTest/MouseEvent.test.js index 62abb34c..46cc6522 100644 --- a/scripts/__tests__/EventTest/MouseEvent.test.js +++ b/scripts/__tests__/EventTest/MouseEvent.test.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from '../jest/testUtils'; describe('MouseEvent Test', () => { @@ -21,7 +21,7 @@ describe('MouseEvent Test', () => { describe('onClick Test', () => { it('绑定this', () => { - class App extends Horizon.Component { + class App extends Inula.Component { constructor(props) { super(props); this.state = { @@ -52,7 +52,7 @@ describe('MouseEvent Test', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container.querySelector('p').innerHTML).toBe('0'); expect(container.querySelector('#p').innerHTML).toBe('100'); // 点击按钮触发num加1 @@ -65,7 +65,7 @@ describe('MouseEvent Test', () => { it('点击触发', () => { const handleClick = jest.fn(); - Horizon.render(, container); + Inula.render(, container); container.querySelector('button').click(); expect(handleClick).toHaveBeenCalledTimes(1); for (let i = 0; i < 5; i++) { @@ -76,8 +76,8 @@ describe('MouseEvent Test', () => { it('disable不触发click', () => { const handleClick = jest.fn(); - const spanRef = Horizon.createRef(); - Horizon.render( + const spanRef = Inula.createRef(); + Inula.render( , @@ -90,7 +90,7 @@ describe('MouseEvent Test', () => { }); const test = (name, config) => { - const node = Horizon.render(config, container); + const node = Inula.render(config, container); let event = new MouseEvent(name, { relatedTarget: null, bubbles: true, @@ -163,7 +163,7 @@ describe('MouseEvent Test', () => { }); it('KeyboardEvent.getModifierState should not fail', () => { - const input = Horizon.render( { e.getModifierState('CapsLock'); }} diff --git a/scripts/__tests__/EventTest/WheelEvent.test.js b/scripts/__tests__/EventTest/WheelEvent.test.js index e563d94c..13f8bada 100644 --- a/scripts/__tests__/EventTest/WheelEvent.test.js +++ b/scripts/__tests__/EventTest/WheelEvent.test.js @@ -13,14 +13,14 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from '../jest/testUtils'; describe('合成滚轮事件', () => { const LogUtils = getLogUtils(); it('onWheel', () => { - const realNode = Horizon.render( + const realNode = Inula.render(
LogUtils.log(`onWheel: ${event.type}`)} onWheelCapture={event => LogUtils.log(`onWheelCapture: ${event.type}`)} @@ -50,7 +50,7 @@ describe('合成滚轮事件', () => { expect(e.isPropagationStopped()).toBe(true); LogUtils.log(e.type + ' handle'); }; - const realNode = Horizon.render( + const realNode = Inula.render(
, container ); diff --git a/scripts/__tests__/HorizonIsTest/index.test.js b/scripts/__tests__/HorizonIsTest/index.test.js deleted file mode 100644 index ff33477d..00000000 --- a/scripts/__tests__/HorizonIsTest/index.test.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * InulaJS is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -import * as Horizon from '@cloudsop/horizon/index.ts'; - -function App() { - return <>; -} - -describe('HorizonIs', () => { - it('should identify horizon elements', () => { - expect(Horizon.isElement(
)).toBe(true); - expect(Horizon.isElement('span')).toBe(false); - expect(Horizon.isElement(111)).toBe(false); - expect(Horizon.isElement(false)).toBe(false); - expect(Horizon.isElement(null)).toBe(false); - expect(Horizon.isElement([])).toBe(false); - expect(Horizon.isElement({})).toBe(false); - expect(Horizon.isElement(undefined)).toBe(false); - - const TestContext = Horizon.createContext(false); - expect(Horizon.isElement()).toBe(true); - expect(Horizon.isElement()).toBe(true); - expect(Horizon.isElement(<>)).toBe(true); - expect(Horizon.isElement()).toBe(true); - }); - - it('should identify Fragment', () => { - expect(Horizon.isFragment(<>)).toBe(true); - }); - - it('should identify memo component', () => { - const MemoComp = Horizon.memo(App); - expect(Horizon.isMemo()).toBe(true); - }); - - it('should identify forwardRef', () => { - const ForwardRefComp = Horizon.forwardRef(App); - expect(Horizon.isForwardRef()).toBe(true); - }); - - it('should identify lazy', () => { - const LazyComp = Horizon.lazy(() => App); - expect(Horizon.isLazy()).toBe(true); - }); - - it('should identify portal', () => { - const portal = Horizon.createPortal(
, container); - expect(Horizon.isPortal(portal)).toBe(true); - }); - - it('should identify ContextProvider', () => { - const TestContext = Horizon.createContext(false); - expect(Horizon.isContextProvider()).toBe(true); - expect(Horizon.isContextProvider()).toBe(false); - expect(Horizon.isContextConsumer()).toBe(false); - expect(Horizon.isContextConsumer()).toBe(true); - }); -}); diff --git a/scripts/__tests__/HorizonXTest/StateManager/StateArray.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateArray.test.tsx index 9ffad653..ca4a9e08 100644 --- a/scripts/__tests__/HorizonXTest/StateManager/StateArray.test.tsx +++ b/scripts/__tests__/HorizonXTest/StateManager/StateArray.test.tsx @@ -14,9 +14,9 @@ */ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import { clearStore, createStore, useStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; @@ -49,7 +49,7 @@ const useUserStore = createStore({ }); describe('测试store中的Array', () => { - const { unmountComponentAtNode } = Horizon; + const { unmountComponentAtNode } = Inula; let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 @@ -102,17 +102,17 @@ describe('测试store中的Array', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 2'); // 在Array中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 3'); // 在Array中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 2'); @@ -138,7 +138,7 @@ describe('测试store中的Array', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // push @@ -188,7 +188,7 @@ describe('测试store中的Array', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // push diff --git a/scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx index e91e0e13..ddbf7589 100644 --- a/scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx +++ b/scripts/__tests__/HorizonXTest/StateManager/StateMap.test.tsx @@ -14,9 +14,9 @@ */ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import { clearStore, createStore, useStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; @@ -49,7 +49,7 @@ const useUserStore = createStore({ }); describe('测试store中的Map', () => { - const { unmountComponentAtNode } = Horizon; + const { unmountComponentAtNode } = Inula; let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 @@ -110,23 +110,23 @@ describe('测试store中的Map', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 0'); @@ -149,23 +149,23 @@ describe('测试store中的Map', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); @@ -188,23 +188,23 @@ describe('测试store中的Map', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2 3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: '); @@ -227,23 +227,23 @@ describe('测试store中的Map', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); @@ -265,23 +265,23 @@ describe('测试store中的Map', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); @@ -298,11 +298,11 @@ describe('测试store中的Map', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); @@ -324,23 +324,23 @@ describe('测试store中的Map', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); diff --git a/scripts/__tests__/HorizonXTest/StateManager/StateMixType.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateMixType.test.tsx index 2bb360af..52e9b610 100644 --- a/scripts/__tests__/HorizonXTest/StateManager/StateMixType.test.tsx +++ b/scripts/__tests__/HorizonXTest/StateManager/StateMixType.test.tsx @@ -14,13 +14,13 @@ */ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import { clearStore, createStore, useStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; describe('测试store中的混合类型变化', () => { - const { unmountComponentAtNode } = Horizon; + const { unmountComponentAtNode } = Inula; let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 @@ -98,10 +98,10 @@ describe('测试store中的混合类型变化', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#dayList')?.innerHTML).toBe('love: 1 3 5'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#dayList')?.innerHTML).toBe('love: 1 3 5 7'); @@ -168,7 +168,7 @@ describe('测试store中的混合类型变化', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('p1 p2'); diff --git a/scripts/__tests__/HorizonXTest/StateManager/StateSet.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateSet.test.tsx index 7c1c8656..84213b7d 100644 --- a/scripts/__tests__/HorizonXTest/StateManager/StateSet.test.tsx +++ b/scripts/__tests__/HorizonXTest/StateManager/StateSet.test.tsx @@ -14,9 +14,9 @@ */ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import { clearStore, createStore, useStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; @@ -49,7 +49,7 @@ const useUserStore = createStore({ }); describe('测试store中的Set', () => { - const { unmountComponentAtNode } = Horizon; + const { unmountComponentAtNode } = Inula; let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 @@ -116,24 +116,24 @@ describe('测试store中的Set', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); expect(container?.querySelector('#lastAge')?.innerHTML).toBe('last person age: 2'); // 在set中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 3'); // 在set中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // clear set - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 0'); @@ -158,23 +158,23 @@ describe('测试store中的Set', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在set中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在set中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear set - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); @@ -197,23 +197,23 @@ describe('测试store中的Set', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在set中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在set中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear set - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); @@ -235,23 +235,23 @@ describe('测试store中的Set', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在set中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在set中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear set - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); @@ -268,11 +268,11 @@ describe('测试store中的Set', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在set中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); @@ -294,23 +294,23 @@ describe('测试store中的Set', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在set中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在set中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear set - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); diff --git a/scripts/__tests__/HorizonXTest/StateManager/StateWeakMap.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateWeakMap.test.tsx index c6e2fc09..9e847d18 100644 --- a/scripts/__tests__/HorizonXTest/StateManager/StateWeakMap.test.tsx +++ b/scripts/__tests__/HorizonXTest/StateManager/StateWeakMap.test.tsx @@ -14,9 +14,9 @@ */ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import { clearStore, createStore, useStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; @@ -49,7 +49,7 @@ const useUserStore = createStore({ }); describe('测试store中的WeakMap', () => { - const { unmountComponentAtNode } = Horizon; + const { unmountComponentAtNode } = Inula; let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 @@ -110,17 +110,17 @@ describe('测试store中的WeakMap', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在WeakMap中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); // 在WeakMap中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); @@ -137,11 +137,11 @@ describe('测试store中的WeakMap', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: undefined'); // 在WeakMap中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 3'); diff --git a/scripts/__tests__/HorizonXTest/StateManager/StateWeakSet.test.tsx b/scripts/__tests__/HorizonXTest/StateManager/StateWeakSet.test.tsx index 962e73c3..9986b941 100644 --- a/scripts/__tests__/HorizonXTest/StateManager/StateWeakSet.test.tsx +++ b/scripts/__tests__/HorizonXTest/StateManager/StateWeakSet.test.tsx @@ -14,9 +14,9 @@ */ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import { clearStore, createStore, useStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; @@ -49,7 +49,7 @@ const useUserStore = createStore({ }); describe('测试store中的WeakSet', () => { - const { unmountComponentAtNode } = Horizon; + const { unmountComponentAtNode } = Inula; let container: HTMLElement | null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 @@ -102,17 +102,17 @@ describe('测试store中的WeakSet', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在WeakSet中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); // 在WeakSet中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); diff --git a/scripts/__tests__/HorizonXTest/StoreFunctionality/async.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/async.test.tsx index a215e3be..da9e1ca6 100644 --- a/scripts/__tests__/HorizonXTest/StoreFunctionality/async.test.tsx +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/async.test.tsx @@ -14,12 +14,12 @@ */ //@ts-ignore -import * as Horizon from '../../../../libs/horizon'; -import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import * as Inula from '../../../../libs/inula/index'; +import { createStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; import { triggerClickEvent } from '../../jest/commonComponents'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; -const { unmountComponentAtNode } = Horizon; +const { unmountComponentAtNode } = Inula; function postpone(timer, func) { return new Promise(resolve => { diff --git a/scripts/__tests__/HorizonXTest/StoreFunctionality/basicAccess.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/basicAccess.test.tsx index a626551b..277a5bad 100644 --- a/scripts/__tests__/HorizonXTest/StoreFunctionality/basicAccess.test.tsx +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/basicAccess.test.tsx @@ -14,13 +14,13 @@ */ //@ts-ignore -import Horizon from '@cloudsop/horizon/index.ts'; +import Inula from '../../../../libs/inula/index'; import { triggerClickEvent } from '../../jest/commonComponents'; import { useLogStore } from './store'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; -import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import { createStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; -const { unmountComponentAtNode } = Horizon; +const { unmountComponentAtNode } = Inula; describe('Basic store manipulation', () => { let container: HTMLElement | null = null; @@ -46,7 +46,7 @@ describe('Basic store manipulation', () => { return
{logStore.length}
; } - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1'); }); @@ -70,9 +70,9 @@ describe('Basic store manipulation', () => { ); } - Horizon.render(, container); + Inula.render(, container); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); @@ -98,9 +98,9 @@ describe('Basic store manipulation', () => { ); } - Horizon.render(, container); + Inula.render(, container); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); @@ -142,9 +142,9 @@ describe('Basic store manipulation', () => { ); } - Horizon.render(, container); + Inula.render(, container); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); @@ -187,9 +187,9 @@ describe('Basic store manipulation', () => { ); } - Horizon.render(, container); + Inula.render(, container); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); diff --git a/scripts/__tests__/HorizonXTest/StoreFunctionality/cloneDeep.test.js b/scripts/__tests__/HorizonXTest/StoreFunctionality/cloneDeep.test.js index d2b159d8..e1c0231d 100644 --- a/scripts/__tests__/HorizonXTest/StoreFunctionality/cloneDeep.test.js +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/cloneDeep.test.js @@ -13,13 +13,13 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; -import { clearStore, createStore, useStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; -import { OBSERVER_KEY } from '../../../../libs/horizon/src/horizonx/Constants'; +import * as Inula from '../../../../libs/inula/index'; +import { clearStore, createStore, useStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; +import { OBSERVER_KEY } from '../../../../libs/inula/src/inulax/Constants'; import { App, Text, triggerClickEvent } from '../../jest/commonComponents'; describe('测试对store.state对象进行深度克隆', () => { - const { unmountComponentAtNode } = Horizon; + const { unmountComponentAtNode } = Inula; let container = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 @@ -81,7 +81,7 @@ describe('测试对store.state对象进行深度克隆', () => { ); } - it('The observer object of symbol (\'_horizonObserver\') cannot be accessed to from Proxy', () => { + it('The observer object of symbol (\'_inulaObserver\') cannot be accessed to from Proxy', () => { let userStore = null; function Child(props) { userStore = useStore('user'); @@ -93,13 +93,13 @@ describe('测试对store.state对象进行深度克隆', () => { ); } - Horizon.render(, container); + Inula.render(, container); - // The observer object of symbol ('_horizonObserver') cannot be accessed to from Proxy prevent errors caused by clonedeep. + // The observer object of symbol ('_inulaObserver') cannot be accessed to from Proxy prevent errors caused by clonedeep. expect(userStore.persons[0][OBSERVER_KEY]).toBe(undefined); }); - it('The observer object of symbol (\'_horizonObserver\') cannot be accessed to from Proxy', () => { + it('The observer object of symbol (\'_inulaObserver\') cannot be accessed to from Proxy', () => { let userStore = null; function Child(props) { userStore = useStore('user'); @@ -111,7 +111,7 @@ describe('测试对store.state对象进行深度克隆', () => { ); } - Horizon.render(, container); + Inula.render(, container); // NO throw this Exception, TypeError: 'get' on proxy: property 'prototype' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value const proxyObj = userStore.persons[0].constructor; diff --git a/scripts/__tests__/HorizonXTest/StoreFunctionality/dollarAccess.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/dollarAccess.test.tsx index 0ecd9fca..54c211f5 100644 --- a/scripts/__tests__/HorizonXTest/StoreFunctionality/dollarAccess.test.tsx +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/dollarAccess.test.tsx @@ -14,12 +14,12 @@ */ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import { triggerClickEvent } from '../../jest/commonComponents'; import { useLogStore } from './store'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; -const { unmountComponentAtNode } = Horizon; +const { unmountComponentAtNode } = Inula; describe('Dollar store access', () => { let container: HTMLElement | null = null; @@ -45,7 +45,7 @@ describe('Dollar store access', () => { return
{logStore.$c.length()}
; } - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1'); }); @@ -69,9 +69,9 @@ describe('Dollar store access', () => { ); } - Horizon.render(, container); + Inula.render(, container); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); diff --git a/scripts/__tests__/HorizonXTest/StoreFunctionality/otherCases.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/otherCases.test.tsx index 46f5198b..8d473e8b 100644 --- a/scripts/__tests__/HorizonXTest/StoreFunctionality/otherCases.test.tsx +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/otherCases.test.tsx @@ -14,12 +14,12 @@ */ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; -import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import * as Inula from '../../../../libs/inula/index'; +import { createStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; import { triggerClickEvent } from '../../jest/commonComponents'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; -const { unmountComponentAtNode } = Horizon; +const { unmountComponentAtNode } = Inula; describe('Self referencing', () => { let container: HTMLElement | null = null; @@ -69,17 +69,17 @@ describe('Self referencing', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('4'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('6'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); @@ -110,11 +110,11 @@ describe('Self referencing', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('5'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); @@ -154,10 +154,10 @@ describe('Self referencing', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('abc'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('def'); diff --git a/scripts/__tests__/HorizonXTest/StoreFunctionality/reset.js b/scripts/__tests__/HorizonXTest/StoreFunctionality/reset.js index 6fa0a639..4df7add5 100644 --- a/scripts/__tests__/HorizonXTest/StoreFunctionality/reset.js +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/reset.js @@ -13,11 +13,11 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; -import {createStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import * as Inula from '../../../../libs/inula/index'; +import {createStore} from '../../../../libs/inula/src/inulax/store/StoreHandler'; import {triggerClickEvent} from '../../jest/commonComponents'; -const {unmountComponentAtNode} = Horizon; +const {unmountComponentAtNode} = Inula; describe('Reset', () => { it('RESET NOT IMPLEMENTED', async () => { @@ -69,25 +69,25 @@ describe('Reset', () => {
} - Horizon.render(, container); + Inula.render(, container); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); expect(document.getElementById(RESULT_ID).innerHTML).toBe('2'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, RESET_ID); }); expect(document.getElementById(RESULT_ID).innerHTML).toBe('0'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); diff --git a/scripts/__tests__/HorizonXTest/StoreFunctionality/store.ts b/scripts/__tests__/HorizonXTest/StoreFunctionality/store.ts index 4831afd3..f37d4547 100644 --- a/scripts/__tests__/HorizonXTest/StoreFunctionality/store.ts +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/store.ts @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import { createStore } from '../../../../libs/inula/src/inulax/store/StoreHandler'; export const useLogStore = createStore({ id: 'logStore', // you do not need to specify ID for local store diff --git a/scripts/__tests__/HorizonXTest/StoreFunctionality/utils.test.js b/scripts/__tests__/HorizonXTest/StoreFunctionality/utils.test.js index be07b4c4..ff9f0629 100644 --- a/scripts/__tests__/HorizonXTest/StoreFunctionality/utils.test.js +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/utils.test.js @@ -1,4 +1,4 @@ -import { resolveMutation } from '../../../../libs/horizon/src/horizonx/CommonUtils'; +import { resolveMutation } from '../../../../libs/inula/src/inulax/CommonUtils'; describe('Mutation resolve', () => { it('should resolve mutation different types', () => { diff --git a/scripts/__tests__/HorizonXTest/StoreFunctionality/watch.test.tsx b/scripts/__tests__/HorizonXTest/StoreFunctionality/watch.test.tsx index b6e44453..5d51aa76 100644 --- a/scripts/__tests__/HorizonXTest/StoreFunctionality/watch.test.tsx +++ b/scripts/__tests__/HorizonXTest/StoreFunctionality/watch.test.tsx @@ -13,8 +13,8 @@ * See the Mulan PSL v2 for more details. */ -import { createStore } from '@cloudsop/horizon/src/horizonx/store/StoreHandler'; -import { watch } from '@cloudsop/horizon/src/horizonx/proxy/watch'; +import { createStore } from '../../../../libs/inula'; +import { watch } from '../../../../libs/inula'; describe('watch', () => { it('shouhld watch primitive state variable', async () => { diff --git a/scripts/__tests__/HorizonXTest/adapters/ReduxAdapter.test.tsx b/scripts/__tests__/HorizonXTest/adapters/ReduxAdapter.test.tsx index 16e5bbe7..431719f5 100644 --- a/scripts/__tests__/HorizonXTest/adapters/ReduxAdapter.test.tsx +++ b/scripts/__tests__/HorizonXTest/adapters/ReduxAdapter.test.tsx @@ -14,13 +14,13 @@ */ //@ts-ignore -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import { createStore, applyMiddleware, combineReducers, bindActionCreators, -} from '../../../../libs/horizon/src/horizonx/adapters/redux'; +} from '../../../../libs/inula/src/inulax/adapters/redux'; import { describe, it, expect } from '@jest/globals'; describe('Redux adapter', () => { diff --git a/scripts/__tests__/HorizonXTest/adapters/ReduxAdapterThunk.test.tsx b/scripts/__tests__/HorizonXTest/adapters/ReduxAdapterThunk.test.tsx index c7ccf8b2..2507f28a 100644 --- a/scripts/__tests__/HorizonXTest/adapters/ReduxAdapterThunk.test.tsx +++ b/scripts/__tests__/HorizonXTest/adapters/ReduxAdapterThunk.test.tsx @@ -13,8 +13,8 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; -import { createStore, applyMiddleware, thunk } from '../../../../libs/horizon/src/horizonx/adapters/redux'; +import * as Inula from '../../../../libs/inula/index'; +import { createStore, applyMiddleware, thunk } from '../../../../libs/inula/src/inulax/adapters/redux'; import {describe, it, expect} from '@jest/globals'; describe('Redux thunk', () => { diff --git a/scripts/__tests__/HorizonXTest/adapters/ReduxReactAdapter.test.tsx b/scripts/__tests__/HorizonXTest/adapters/ReduxReactAdapter.test.tsx index bf31c13a..b2402c90 100644 --- a/scripts/__tests__/HorizonXTest/adapters/ReduxReactAdapter.test.tsx +++ b/scripts/__tests__/HorizonXTest/adapters/ReduxReactAdapter.test.tsx @@ -14,7 +14,7 @@ */ //@ts-ignore -import horizon, * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import { batch, connect, @@ -25,10 +25,10 @@ import { useStore, createSelectorHook, createDispatchHook -} from '../../../../libs/horizon/src/horizonx/adapters/redux'; +} from '../../../../libs/inula/src/inulax/adapters/redux'; import {triggerClickEvent} from '../../jest/commonComponents'; import {describe, it, beforeEach, afterEach, expect} from '@jest/globals'; -import { ReduxStoreHandler } from '@cloudsop/horizon/src/horizonx/types'; +import { ReduxStoreHandler } from '../../../../libs/inula/src/inulax/adapters/redux'; const BUTTON = 'button'; const BUTTON2 = 'button2'; @@ -64,7 +64,7 @@ describe('Redux/React binding adapter', () => { ; }; - Horizon.render(, getE(CONTAINER)); + Inula.render(, getE(CONTAINER)); expect(getE(RESULT).innerHTML).toBe('state'); }); @@ -92,11 +92,11 @@ describe('Redux/React binding adapter', () => { ; }; - Horizon.render(, getE(CONTAINER)); + Inula.render(, getE(CONTAINER)); expect(reduxStore.getState()).toBe(0); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(getE(CONTAINER), BUTTON); }); @@ -127,11 +127,11 @@ describe('Redux/React binding adapter', () => { ; }; - Horizon.render(, getE(CONTAINER)); + Inula.render(, getE(CONTAINER)); expect(getE(RESULT).innerHTML).toBe('0'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(getE(CONTAINER), BUTTON); triggerClickEvent(getE(CONTAINER), BUTTON); }); @@ -181,7 +181,7 @@ describe('Redux/React binding adapter', () => { const Wrapper = () => { //@ts-ignore - const [amount, setAmount] = Horizon.useState(5); + const [amount, setAmount] = Inula.useState(5); return
; } - Horizon.render(, getE(CONTAINER)); + Inula.render(, getE(CONTAINER)); expect(getE(RESULT).innerHTML).toBe('0'); expect(renderCounter).toBe(1); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(getE(CONTAINER), BUTTON); }); @@ -262,8 +262,8 @@ describe('Redux/React binding adapter', () => { return state; }); - const counterContext = horizon.createContext(); - const toggleContext = horizon.createContext(); + const counterContext = Inula.createContext(); + const toggleContext = Inula.createContext(); function Counter() { const count = createSelectorHook(counterContext)(); @@ -295,12 +295,12 @@ describe('Redux/React binding adapter', () => {
; } - Horizon.render(, getE(CONTAINER)); + Inula.render(, getE(CONTAINER)); expect(getE(BUTTON).innerHTML).toBe('0'); expect(getE(BUTTON2).innerHTML).toBe('false'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(getE(CONTAINER), BUTTON); triggerClickEvent(getE(CONTAINER), BUTTON2); }); diff --git a/scripts/__tests__/HorizonXTest/adapters/connectTest.tsx b/scripts/__tests__/HorizonXTest/adapters/connectTest.tsx index a11b8249..59e42396 100644 --- a/scripts/__tests__/HorizonXTest/adapters/connectTest.tsx +++ b/scripts/__tests__/HorizonXTest/adapters/connectTest.tsx @@ -1,7 +1,7 @@ -import { createElement } from '../../../../libs/horizon/src/external/JSXElement'; -import { createDomTextVNode } from '../../../../libs/horizon/src/renderer/vnode/VNodeCreator'; -import { createStore } from '../../../../libs/horizon/src/horizonx/adapters/redux'; -import { connect } from '../../../../libs/horizon/src/horizonx/adapters/reduxReact'; +import { createElement } from '../../../../libs/inula/src/external/JSXElement'; +import { createDomTextVNode } from '../../../../libs/inula/src/renderer/vnode/VNodeCreator'; +import { createStore } from '../../../../libs/inula/src/inulax/adapters/redux'; +import { connect } from '../../../../libs/inula/src/inulax/adapters/reduxReact'; createStore((state: number = 0, action): number => { if (action.type === 'add') return state + 1; diff --git a/scripts/__tests__/HorizonXTest/class/ClassException.test.tsx b/scripts/__tests__/HorizonXTest/class/ClassException.test.tsx index 4feb4635..1a9aed46 100644 --- a/scripts/__tests__/HorizonXTest/class/ClassException.test.tsx +++ b/scripts/__tests__/HorizonXTest/class/ClassException.test.tsx @@ -13,15 +13,15 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {clearStore, createStore, useStore} from '../../../../libs/inula/src/inulax/store/StoreHandler'; import {Text, triggerClickEvent} from '../../jest/commonComponents'; -import {getObserver} from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; +import {getObserver} from '../../../../libs/inula/src/inulax/proxy/ProxyHandler'; import {describe, beforeEach, afterEach, it, expect} from '@jest/globals'; describe('测试 Class VNode 清除时,对引用清除', () => { - const {unmountComponentAtNode} = Horizon; + const {unmountComponentAtNode} = Inula; let container:HTMLElement|null = null; let globalState = { name: 'bing dun dun', @@ -62,7 +62,7 @@ describe('测试 Class VNode 清除时,对引用清除', () => { }); it('test observer.clearByNode', () => { - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useStore('user'); render() { @@ -78,7 +78,7 @@ describe('测试 Class VNode 清除时,对引用清除', () => { } expect(() => { - Horizon.render(, container); + Inula.render(, container); }).toThrow('The number of updates exceeds the upper limit 50.\n' + ' A component maybe repeatedly invokes setState on componentWillUpdate or componentDidUpdate.'); diff --git a/scripts/__tests__/HorizonXTest/class/ClassStateArray.test.tsx b/scripts/__tests__/HorizonXTest/class/ClassStateArray.test.tsx index fccc922f..c1dd15a3 100644 --- a/scripts/__tests__/HorizonXTest/class/ClassStateArray.test.tsx +++ b/scripts/__tests__/HorizonXTest/class/ClassStateArray.test.tsx @@ -13,9 +13,9 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {clearStore, createStore, useStore} from '../../../../libs/inula/src/inulax/store/StoreHandler'; import {App, Text, triggerClickEvent} from '../../jest/commonComponents'; import {describe, beforeEach, afterEach, it, expect} from '@jest/globals'; @@ -42,7 +42,7 @@ let useUserStore = createStore({ }); describe('在Class组件中,测试store中的Array', () => { - const { unmountComponentAtNode } = Horizon; + const { unmountComponentAtNode } = Inula; let container:HTMLElement|null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 @@ -61,7 +61,7 @@ describe('在Class组件中,测试store中的Array', () => { }); const newPerson = { name: 'p3', age: 3 }; - class Parent extends Horizon.Component { + class Parent extends Inula.Component { userStore = useUserStore(); props:{ children:any[] @@ -96,7 +96,7 @@ describe('在Class组件中,测试store中的Array', () => { } it('测试Array方法: push()、pop()', () => { - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); render() { @@ -108,17 +108,17 @@ describe('在Class组件中,测试store中的Array', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 2'); // 在Array中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 3'); // 在Array中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: 2'); @@ -127,7 +127,7 @@ describe('在Class组件中,测试store中的Array', () => { it('测试Array方法: entries()、push()、shift()、unshift、直接赋值', () => { let globalStore = useUserStore(); - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); constructor(props) { @@ -152,7 +152,7 @@ describe('在Class组件中,测试store中的Array', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // push @@ -188,7 +188,7 @@ describe('在Class组件中,测试store中的Array', () => { it('测试Array方法: forEach()', () => { let globalStore = useUserStore(); globalStore.$s.persons.push({ name: 'p2', age: 2 }); - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); constructor(props) { @@ -210,7 +210,7 @@ describe('在Class组件中,测试store中的Array', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // push diff --git a/scripts/__tests__/HorizonXTest/class/ClassStateMap.test.tsx b/scripts/__tests__/HorizonXTest/class/ClassStateMap.test.tsx index 50504d38..1ba27076 100644 --- a/scripts/__tests__/HorizonXTest/class/ClassStateMap.test.tsx +++ b/scripts/__tests__/HorizonXTest/class/ClassStateMap.test.tsx @@ -13,9 +13,9 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {clearStore, createStore, useStore} from '../../../../libs/inula/src/inulax/store/StoreHandler'; import {App, Text, triggerClickEvent} from '../../jest/commonComponents'; import {describe, beforeEach, afterEach, it, expect} from '@jest/globals'; @@ -42,7 +42,7 @@ const useUserStore = createStore({ }); describe('在Class组件中,测试store中的Map', () => { - const { unmountComponentAtNode } = Horizon; + const { unmountComponentAtNode } = Inula; let container:HTMLElement|null = null; beforeEach(() => { // 创建一个 DOM 元素作为渲染目标 @@ -64,7 +64,7 @@ describe('在Class组件中,测试store中的Map', () => { const newPerson = { name: 'p3', age: 3 }; - class Parent extends Horizon.Component { + class Parent extends Inula.Component { userStore = useUserStore(); props = {children:[]} @@ -104,7 +104,7 @@ describe('在Class组件中,测试store中的Map', () => { } it('测试Map方法: set()、delete()、clear()', () => { - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); render() { @@ -116,30 +116,30 @@ describe('在Class组件中,测试store中的Map', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#size')?.innerHTML).toBe('persons number: 0'); }); it('测试Map方法: keys()', () => { - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); render() { @@ -157,30 +157,30 @@ describe('在Class组件中,测试store中的Map', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Map方法: values()', () => { - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); render() { @@ -198,30 +198,30 @@ describe('在Class组件中,测试store中的Map', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2 3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: 1 2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#ageList')?.innerHTML).toBe('age list: '); }); it('测试Map方法: entries()', () => { - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); render() { @@ -239,30 +239,30 @@ describe('在Class组件中,测试store中的Map', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Map方法: forEach()', () => { - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); render() { @@ -279,30 +279,30 @@ describe('在Class组件中,测试store中的Map', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); }); it('测试Map方法: has()', () => { - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); render() { @@ -314,18 +314,18 @@ describe('在Class组件中,测试store中的Map', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: false'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#hasPerson')?.innerHTML).toBe('has new person: true'); }); it('测试Map方法: for of()', () => { - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useUserStore(); render() { @@ -342,23 +342,23 @@ describe('在Class组件中,测试store中的Map', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // 在Map中增加一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'addBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2 p3'); // 在Map中删除一个对象 - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'delBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: p1 p2'); // clear Map - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'clearBtn'); }); expect(container?.querySelector('#nameList')?.innerHTML).toBe('name list: '); diff --git a/scripts/__tests__/HorizonXTest/clear/ClassVNodeClear.test.tsx b/scripts/__tests__/HorizonXTest/clear/ClassVNodeClear.test.tsx index f87560c6..6de95373 100644 --- a/scripts/__tests__/HorizonXTest/clear/ClassVNodeClear.test.tsx +++ b/scripts/__tests__/HorizonXTest/clear/ClassVNodeClear.test.tsx @@ -13,15 +13,15 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {clearStore, createStore, useStore} from '../../../../libs/inula/src/inulax/store/StoreHandler'; import {Text, triggerClickEvent} from '../../jest/commonComponents'; -import {getObserver} from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; +import {getObserver} from '../../../../libs/inula/src/inulax/proxy/ProxyHandler'; import {describe, it, beforeEach, afterEach, expect} from '@jest/globals'; describe('测试 Class VNode 清除时,对引用清除', () => { - const {unmountComponentAtNode} = Horizon; + const {unmountComponentAtNode} = Inula; let container:HTMLElement|null = null; let globalState = { name: 'bing dun dun', @@ -62,7 +62,7 @@ describe('测试 Class VNode 清除时,对引用清除', () => { }); it('test observer.clearByNode', () => { - class App extends Horizon.Component { + class App extends Inula.Component { userStore = useStore('user'); render() { @@ -75,7 +75,7 @@ describe('测试 Class VNode 清除时,对引用清除', () => { } } - class Parent extends Horizon.Component { + class Parent extends Inula.Component { userStore = useStore('user'); setWin = () => { @@ -92,7 +92,7 @@ describe('测试 Class VNode 清除时,对引用清除', () => { } } - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useStore('user'); render() { @@ -105,24 +105,24 @@ describe('测试 Class VNode 清除时,对引用清除', () => { } } - Horizon.render(, container); + Inula.render(, container); // Parent and Child hold the isWin key expect(getObserver(globalState).keyVNodes.get('isWin').size).toBe(2); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'toggleBtn'); }); // Parent hold the isWin key expect(getObserver(globalState).keyVNodes.get('isWin').size).toBe(1); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'toggleBtn'); }); // Parent and Child hold the isWin key expect(getObserver(globalState).keyVNodes.get('isWin').size).toBe(2); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'hideBtn'); }); // no component hold the isWin key diff --git a/scripts/__tests__/HorizonXTest/clear/FunctionVNodeClear.test.tsx b/scripts/__tests__/HorizonXTest/clear/FunctionVNodeClear.test.tsx index d00752e6..37cdd2d0 100644 --- a/scripts/__tests__/HorizonXTest/clear/FunctionVNodeClear.test.tsx +++ b/scripts/__tests__/HorizonXTest/clear/FunctionVNodeClear.test.tsx @@ -13,15 +13,15 @@ * See the Mulan PSL v2 for more details. */ -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../../libs/inula/index'; import * as LogUtils from '../../jest/logUtils'; -import {clearStore, createStore, useStore} from '../../../../libs/horizon/src/horizonx/store/StoreHandler'; +import {clearStore, createStore, useStore} from '../../../../libs/inula/src/inulax/store/StoreHandler'; import {Text, triggerClickEvent} from '../../jest/commonComponents'; -import {getObserver} from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; +import {getObserver} from '../../../../libs/inula/src/inulax/proxy/ProxyHandler'; import {describe, it, beforeEach, afterEach, expect} from '@jest/globals'; describe('测试VNode清除时,对引用清除', () => { - const {unmountComponentAtNode} = Horizon; + const {unmountComponentAtNode} = Inula; let container:HTMLElement|null = null; let globalState = { name: 'bing dun dun', @@ -59,7 +59,7 @@ describe('测试VNode清除时,对引用清除', () => { }); it('test observer.clearByNode', () => { - class App extends Horizon.Component { + class App extends Inula.Component { userStore = useStore('user'); render() { @@ -72,7 +72,7 @@ describe('测试VNode清除时,对引用清除', () => { } } - class Parent extends Horizon.Component { + class Parent extends Inula.Component { userStore = useStore('user'); setWin = () => { @@ -89,7 +89,7 @@ describe('测试VNode清除时,对引用清除', () => { } } - class Child extends Horizon.Component { + class Child extends Inula.Component { userStore = useStore('user'); render() { @@ -100,24 +100,24 @@ describe('测试VNode清除时,对引用清除', () => { } } - Horizon.render(, container); + Inula.render(, container); // Parent and Child hold the isWin key expect(getObserver(globalState).keyVNodes.get('isWin').size).toBe(2); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'toggleBtn'); }); // Parent hold the isWin key expect(getObserver(globalState).keyVNodes.get('isWin').size).toBe(1); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'toggleBtn'); }); // Parent and Child hold the isWin key expect(getObserver(globalState).keyVNodes.get('isWin').size).toBe(2); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, 'hideBtn'); }); // no component hold the isWin key diff --git a/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx b/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx index 6fff3531..6e7495a1 100644 --- a/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx +++ b/scripts/__tests__/HorizonXTest/edgeCases/deepVariableObserver.test.tsx @@ -1,4 +1,4 @@ -import { createStore, useStore } from '@cloudsop/horizon/src/horizonx/store/StoreHandler'; +import { createStore, useStore } from '../../../../libs/inula'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; describe('Using deep variables', () => { diff --git a/scripts/__tests__/HorizonXTest/edgeCases/multipleStores.test.tsx b/scripts/__tests__/HorizonXTest/edgeCases/multipleStores.test.tsx index 12599ba8..9373861d 100644 --- a/scripts/__tests__/HorizonXTest/edgeCases/multipleStores.test.tsx +++ b/scripts/__tests__/HorizonXTest/edgeCases/multipleStores.test.tsx @@ -14,11 +14,11 @@ */ //@ts-ignore -import Horizon, { createStore } from '@cloudsop/horizon/index.ts'; +import Inula, { createStore } from '../../../../libs/inula/index'; import { triggerClickEvent } from '../../jest/commonComponents'; import { describe, beforeEach, afterEach, it, expect } from '@jest/globals'; -const { unmountComponentAtNode } = Horizon; +const { unmountComponentAtNode } = Inula; const useStore1 = createStore({ state: { counter: 1 }, @@ -57,7 +57,7 @@ describe('Using multiple stores', () => { }); it('Should use multiple stores in class component', () => { - class App extends Horizon.Component { + class App extends Inula.Component { render() { const { counter, add } = useStore1(); const { counter2, add2 } = useStore2(); @@ -88,15 +88,15 @@ describe('Using multiple stores', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1 1'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 1'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID2); }); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 2'); @@ -107,7 +107,7 @@ describe('Using multiple stores', () => { store: any; store2: any; } - class App extends Horizon.Component { + class App extends Inula.Component { constructor() { super(); this.store = useStore1(); @@ -151,15 +151,15 @@ describe('Using multiple stores', () => { } } - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1 1'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 1'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID2); }); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 2'); @@ -196,15 +196,15 @@ describe('Using multiple stores', () => { ); } - Horizon.render(, container); + Inula.render(, container); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1 1'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID); }); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 1'); - Horizon.act(() => { + Inula.act(() => { triggerClickEvent(container, BUTTON_ID2); }); expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 2'); diff --git a/scripts/__tests__/HorizonXTest/edgeCases/proxy.test.tsx b/scripts/__tests__/HorizonXTest/edgeCases/proxy.test.tsx index e02f5967..ca3d8fa6 100644 --- a/scripts/__tests__/HorizonXTest/edgeCases/proxy.test.tsx +++ b/scripts/__tests__/HorizonXTest/edgeCases/proxy.test.tsx @@ -13,8 +13,8 @@ * See the Mulan PSL v2 for more details. */ -import {createProxy} from '../../../../libs/horizon/src/horizonx/proxy/ProxyHandler'; -import {readonlyProxy} from '../../../../libs/horizon/src/horizonx/proxy/readonlyProxy'; +import {createProxy} from '../../../../libs/inula/src/inulax/proxy/ProxyHandler'; +import {readonlyProxy} from '../../../../libs/inula/src/inulax/proxy/readonlyProxy'; import {describe, beforeEach, afterEach, it, expect} from '@jest/globals'; describe('Proxy', () => { diff --git a/scripts/__tests__/InulaIsTest/index.test.js b/scripts/__tests__/InulaIsTest/index.test.js new file mode 100644 index 00000000..5afbcc7c --- /dev/null +++ b/scripts/__tests__/InulaIsTest/index.test.js @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * InulaJS is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import * as Inula from '../../../libs/inula/index'; + +function App() { + return <>; +} + +describe('InulaIs', () => { + it('should identify inula elements', () => { + expect(Inula.isElement(
)).toBe(true); + expect(Inula.isElement('span')).toBe(false); + expect(Inula.isElement(111)).toBe(false); + expect(Inula.isElement(false)).toBe(false); + expect(Inula.isElement(null)).toBe(false); + expect(Inula.isElement([])).toBe(false); + expect(Inula.isElement({})).toBe(false); + expect(Inula.isElement(undefined)).toBe(false); + + const TestContext = Inula.createContext(false); + expect(Inula.isElement()).toBe(true); + expect(Inula.isElement()).toBe(true); + expect(Inula.isElement(<>)).toBe(true); + expect(Inula.isElement()).toBe(true); + }); + + it('should identify Fragment', () => { + expect(Inula.isFragment(<>)).toBe(true); + }); + + it('should identify memo component', () => { + const MemoComp = Inula.memo(App); + expect(Inula.isMemo()).toBe(true); + }); + + it('should identify forwardRef', () => { + const ForwardRefComp = Inula.forwardRef(App); + expect(Inula.isForwardRef()).toBe(true); + }); + + it('should identify lazy', () => { + const LazyComp = Inula.lazy(() => App); + expect(Inula.isLazy()).toBe(true); + }); + + it('should identify portal', () => { + const portal = Inula.createPortal(
, container); + expect(Inula.isPortal(portal)).toBe(true); + }); + + it('should identify ContextProvider', () => { + const TestContext = Inula.createContext(false); + expect(Inula.isContextProvider()).toBe(true); + expect(Inula.isContextProvider()).toBe(false); + expect(Inula.isContextConsumer()).toBe(false); + expect(Inula.isContextConsumer()).toBe(true); + }); +}); diff --git a/scripts/__tests__/jest/commonComponents.js b/scripts/__tests__/jest/commonComponents.js index 99832e5b..f9af6745 100644 --- a/scripts/__tests__/jest/commonComponents.js +++ b/scripts/__tests__/jest/commonComponents.js @@ -14,7 +14,7 @@ */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as Inula from '../../../libs/inula/index'; import { getLogUtils } from './testUtils'; export const App = props => { diff --git a/scripts/__tests__/jest/jestEnvironment.js b/scripts/__tests__/jest/jestEnvironment.js index 92d696b2..f4ed610c 100644 --- a/scripts/__tests__/jest/jestEnvironment.js +++ b/scripts/__tests__/jest/jestEnvironment.js @@ -20,4 +20,4 @@ global.MessageChannel = function MessageChannel() { postMessage() { } }; }; -global.__VERSION__ = require('../../../libs/horizon/package.json').version; +global.__VERSION__ = require('../../../libs/inula/package.json').version; diff --git a/scripts/__tests__/jest/jestSetting.js b/scripts/__tests__/jest/jestSetting.js index 8af40471..94f4ab6c 100644 --- a/scripts/__tests__/jest/jestSetting.js +++ b/scripts/__tests__/jest/jestSetting.js @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. */ -import { unmountComponentAtNode } from '../../../libs/horizon/src/dom/DOMExternal'; +import { unmountComponentAtNode } from '../../../libs/inula/src/dom/DOMExternal'; import { getLogUtils } from './testUtils'; const LogUtils = getLogUtils(); diff --git a/scripts/gen3rdLib.js b/scripts/gen3rdLib.js index 1f3c51d0..10ce63cd 100644 --- a/scripts/gen3rdLib.js +++ b/scripts/gen3rdLib.js @@ -18,16 +18,16 @@ const path = require('path'); const fs = require('fs'); const childProcess = require('child_process'); -const horizonEcoPath = path.resolve(__dirname, '../../horizon-ecosystem'); -if (!fs.existsSync(horizonEcoPath)) { - throw Error('horizon-ecosystem not found, put horizon-core and horizon-ecosystem in same folder plz!'); +const inulaEcoPath = path.resolve(__dirname, '../../inula-ecosystem'); +if (!fs.existsSync(inulaEcoPath)) { + throw Error('inula-ecosystem not found, put inula-core and inula-ecosystem in same folder plz!'); } const cmd = process.argv[2]; childProcess.exec( `npm run ${cmd}`, { - cwd: horizonEcoPath, + cwd: inulaEcoPath, }, function (error, stdout) { if (error) { diff --git a/scripts/rollup/rollup.config.js b/scripts/rollup/rollup.config.js index 33ca0c34..f9e41654 100644 --- a/scripts/rollup/rollup.config.js +++ b/scripts/rollup/rollup.config.js @@ -21,13 +21,13 @@ import replace from '@rollup/plugin-replace'; import copy from './copy-plugin'; import execute from 'rollup-plugin-execute'; import {terser} from 'rollup-plugin-terser'; -import {version as horizonVersion} from '@cloudsop/horizon/package.json'; +import {version as inulaVersion} from '../../package.json'; const extensions = ['.js', '.ts']; -const libDir = path.join(__dirname, '../../libs/horizon'); +const libDir = path.join(__dirname, '../../libs/inula'); const rootDir = path.join(__dirname, '../..'); -const outDir = path.join(rootDir, 'build', 'horizon'); +const outDir = path.join(rootDir, 'build', 'inula'); if (!fs.existsSync(path.join(rootDir, 'build'))) { fs.mkdirSync(path.join(rootDir, 'build')); @@ -59,7 +59,7 @@ const getBasicPlugins = (mode) => { 'process.env.NODE_ENV': `"${mode}"`, isDev: isDev(mode).toString(), isTest: false, - __VERSION__: `"${horizonVersion}"`, + __VERSION__: `"${inulaVersion}"`, }, preventAssignment: true, }), @@ -68,7 +68,7 @@ const getBasicPlugins = (mode) => { function getOutputName(mode) { - return mode === 'production' ? `horizon.${mode}.min.js` : `horizon.${mode}.js`; + return mode === 'production' ? `inula.${mode}.min.js` : `inula.${mode}.js`; } function genConfig(mode) { @@ -84,7 +84,7 @@ function genConfig(mode) { { file: outputResolve('umd', getOutputName(mode)), sourcemap, - name: 'Horizon', + name: 'Inula', format: 'umd', }, ], diff --git a/tsconfig.json b/tsconfig.json index dbd77f67..4c6fbd07 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,7 +35,7 @@ "include": [ "./libs/**/src/**/*.ts", "./libs/**/*.ts", - "./libs/horizon/global.d.ts" + "./libs/inula/global.d.ts" ], "exclude": ["node_modules", "**/*.spec.ts", "dev"], "types": ["node"]