Match-id-c4c9af2f57d85de8d763757eebf4f9e8c0d1a595
This commit is contained in:
commit
c1aeba5e70
|
@ -20,7 +20,7 @@ if [ -n "${releaseVersion}" ] ; then
|
||||||
cd umd
|
cd umd
|
||||||
# umd生产包多暴露全局名HorizonDOM
|
# umd生产包多暴露全局名HorizonDOM
|
||||||
# 以解决webpack的externals react-dom和react都指向Horizon时,webpack随机使用key名造成源码交付问题
|
# 以解决webpack的externals react-dom和react都指向Horizon时,webpack随机使用key名造成源码交付问题
|
||||||
sed -i '$a window.HorizonDOM = window.Horizon;' horizon.production.js
|
sed -i '$a window.HorizonDOM = window.Horizon;' horizon.production.min.js
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
# 写入新版本号
|
# 写入新版本号
|
||||||
|
|
|
@ -22,8 +22,8 @@ steps:
|
||||||
- checkout:
|
- checkout:
|
||||||
path: horizon-core
|
path: horizon-core
|
||||||
- gitlab:
|
- 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
|
branch: master
|
||||||
path: horizon-test
|
path: horizon-test
|
||||||
BUILD:
|
BUILD:
|
||||||
- build_execute:
|
- build_execute:
|
||||||
|
|
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -1,3 +1,25 @@
|
||||||
|
## 0.0.38 (2023-02-01)
|
||||||
|
- **core**: 增加flushSync接口
|
||||||
|
|
||||||
|
## 0.0.37 (2023-01-31)
|
||||||
|
- **core**: 增加jsxs方法
|
||||||
|
|
||||||
|
## 0.0.36 (2023-01-30)
|
||||||
|
- **core**: #100 horizon从上层页面透传到iframe页面里使用,创建的dom元素instanceof HTMLElement为false
|
||||||
|
|
||||||
|
## 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错误
|
||||||
|
|
||||||
|
## 0.0.33 (2023-01-11)
|
||||||
|
- **horizonX-devtool**: 修复IE中报错
|
||||||
|
|
||||||
|
## 0.0.32 (2023-01-04)
|
||||||
|
- **CI**: 生成态输出文件改为horiozn.producion.min.js
|
||||||
|
|
||||||
## 0.0.26 (2022-11-09)
|
## 0.0.26 (2022-11-09)
|
||||||
- **CI**: 包信息同步CMC
|
- **CI**: 包信息同步CMC
|
||||||
|
|
||||||
|
|
|
@ -19,3 +19,4 @@
|
||||||
declare var isDev: boolean;
|
declare var isDev: boolean;
|
||||||
declare var isTest: boolean;
|
declare var isTest: boolean;
|
||||||
declare const __VERSION__: string;
|
declare const __VERSION__: string;
|
||||||
|
declare var setImmediate: Function;
|
||||||
|
|
|
@ -42,9 +42,6 @@ import {
|
||||||
useState,
|
useState,
|
||||||
useDebugValue,
|
useDebugValue,
|
||||||
} from './src/renderer/hooks/HookExternal';
|
} 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 {
|
import {
|
||||||
isContextProvider,
|
isContextProvider,
|
||||||
isContextConsumer,
|
isContextConsumer,
|
||||||
|
@ -59,13 +56,7 @@ import {
|
||||||
import { createStore, useStore, clearStore } from './src/horizonx/store/StoreHandler';
|
import { createStore, useStore, clearStore } from './src/horizonx/store/StoreHandler';
|
||||||
import * as reduxAdapter from './src/horizonx/adapters/redux';
|
import * as reduxAdapter from './src/horizonx/adapters/redux';
|
||||||
import { watch } from './src/horizonx/proxy/watch';
|
import { watch } from './src/horizonx/proxy/watch';
|
||||||
|
import { act } from './src/external/TestUtil';
|
||||||
// act用于测试,作用是:如果fun触发了刷新(包含了异步刷新),可以保证在act后面的代码是在刷新完成后才执行。
|
|
||||||
const act = fun => {
|
|
||||||
asyncUpdates(fun);
|
|
||||||
callRenderQueueImmediate();
|
|
||||||
runAsyncEffects();
|
|
||||||
};
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
render,
|
render,
|
||||||
|
@ -75,6 +66,8 @@ import {
|
||||||
unmountComponentAtNode,
|
unmountComponentAtNode,
|
||||||
} from './src/dom/DOMExternal';
|
} from './src/dom/DOMExternal';
|
||||||
|
|
||||||
|
import { syncUpdates as flushSync } from './src/renderer/TreeBuilder';
|
||||||
|
|
||||||
const Horizon = {
|
const Horizon = {
|
||||||
Children,
|
Children,
|
||||||
createRef,
|
createRef,
|
||||||
|
@ -107,6 +100,7 @@ const Horizon = {
|
||||||
findDOMNode,
|
findDOMNode,
|
||||||
unmountComponentAtNode,
|
unmountComponentAtNode,
|
||||||
act,
|
act,
|
||||||
|
flushSync,
|
||||||
createStore,
|
createStore,
|
||||||
useStore,
|
useStore,
|
||||||
clearStore,
|
clearStore,
|
||||||
|
@ -156,6 +150,7 @@ export {
|
||||||
findDOMNode,
|
findDOMNode,
|
||||||
unmountComponentAtNode,
|
unmountComponentAtNode,
|
||||||
act,
|
act,
|
||||||
|
flushSync,
|
||||||
// 状态管理器HorizonX接口
|
// 状态管理器HorizonX接口
|
||||||
createStore,
|
createStore,
|
||||||
useStore,
|
useStore,
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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, jsx as jsxs } from './src/external/JSXElement';
|
||||||
|
|
||||||
|
export {
|
||||||
|
jsx,
|
||||||
|
jsxs,
|
||||||
|
Fragment
|
||||||
|
};
|
|
@ -16,7 +16,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
module.exports = require('./cjs/horizon.production.js');
|
module.exports = require('./cjs/horizon.production.min.js');
|
||||||
} else {
|
} else {
|
||||||
module.exports = require('./cjs/horizon.development.js');
|
module.exports = require('./cjs/horizon.development.js');
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"horizon"
|
"horizon"
|
||||||
],
|
],
|
||||||
"version": "0.0.26",
|
"version": "0.0.38",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
"bugs": "",
|
"bugs": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
import { saveVNode, updateVNodeProps } from './DOMInternalKeys';
|
import { saveVNode, updateVNodeProps } from './DOMInternalKeys';
|
||||||
import { createDom } from './utils/DomCreator';
|
import { createDom } from './utils/DomCreator';
|
||||||
import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler';
|
import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler';
|
||||||
import { shouldAutoFocus } from './utils/Common';
|
import { isDocument, shouldAutoFocus } from './utils/Common';
|
||||||
import { NSS } from './utils/DomCreator';
|
import { NSS } from './utils/DomCreator';
|
||||||
import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler';
|
import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler';
|
||||||
import type { VNode } from '../renderer/Types';
|
import type { VNode } from '../renderer/Types';
|
||||||
|
@ -26,6 +26,7 @@ import { isNativeElement, validateProps } from './validators/ValidateProps';
|
||||||
import { watchValueChange } from './valueHandler/ValueChangeHandler';
|
import { watchValueChange } from './valueHandler/ValueChangeHandler';
|
||||||
import { DomComponent, DomText } from '../renderer/vnode/VNodeTags';
|
import { DomComponent, DomText } from '../renderer/vnode/VNodeTags';
|
||||||
import { updateCommonProp } from './DOMPropertiesHandler/UpdateCommonProp';
|
import { updateCommonProp } from './DOMPropertiesHandler/UpdateCommonProp';
|
||||||
|
import {getCurrentRoot} from '../renderer/RootStack';
|
||||||
|
|
||||||
export type Props = Record<string, any> & {
|
export type Props = Record<string, any> & {
|
||||||
autoFocus?: boolean;
|
autoFocus?: boolean;
|
||||||
|
@ -70,7 +71,12 @@ export function resetAfterSubmit(): void {
|
||||||
|
|
||||||
// 创建 DOM 对象
|
// 创建 DOM 对象
|
||||||
export function newDom(tagName: string, props: Props, parentNamespace: string, vNode: VNode): Element {
|
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 对象上
|
// 将 vNode 节点挂到 DOM 对象上
|
||||||
saveVNode(vNode, dom);
|
saveVNode(vNode, dom);
|
||||||
// 将属性挂到 DOM 对象上
|
// 将属性挂到 DOM 对象上
|
||||||
|
|
|
@ -20,15 +20,15 @@ export const NSS = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// 创建DOM元素
|
// 创建DOM元素
|
||||||
export function createDom(tagName: string, parentNamespace: string): Element {
|
export function createDom(tagName: string, parentNamespace: string, doc: Document): Element {
|
||||||
let dom: Element;
|
let dom: Element;
|
||||||
const selfNamespace = NSS[tagName] || NSS.html;
|
const selfNamespace = NSS[tagName] || NSS.html;
|
||||||
const ns = parentNamespace !== NSS.html ? parentNamespace : selfNamespace;
|
const ns = parentNamespace !== NSS.html ? parentNamespace : selfNamespace;
|
||||||
|
|
||||||
if (ns !== NSS.html) {
|
if (ns !== NSS.html) {
|
||||||
dom = document.createElementNS(ns, tagName);
|
dom = doc.createElementNS(ns, tagName);
|
||||||
} else {
|
} else {
|
||||||
dom = document.createElement(tagName);
|
dom = doc.createElement(tagName);
|
||||||
}
|
}
|
||||||
return dom;
|
return dom;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,10 @@ import { Source } from '../renderer/Types';
|
||||||
* props 其他常规属性
|
* props 其他常规属性
|
||||||
*/
|
*/
|
||||||
export function JSXElement(type, key, ref, vNode, props, source: Source | null) {
|
export function JSXElement(type, key, ref, vNode, props, source: Source | null) {
|
||||||
return {
|
const ele = {
|
||||||
// 元素标识符
|
// 元素标识符
|
||||||
vtype: TYPE_COMMON_ELEMENT,
|
vtype: TYPE_COMMON_ELEMENT,
|
||||||
src: isDev ? source : null,
|
src: null,
|
||||||
|
|
||||||
// 属于元素的内置属性
|
// 属于元素的内置属性
|
||||||
type: type,
|
type: type,
|
||||||
|
@ -37,8 +37,27 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null)
|
||||||
props: props,
|
props: props,
|
||||||
|
|
||||||
// 所属的class组件
|
// 所属的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
|
||||||
|
Object.defineProperty(ele, 'src', {
|
||||||
|
configurable: false,
|
||||||
|
enumerable: false,
|
||||||
|
writable: false,
|
||||||
|
value: source,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ele;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidKey(key) {
|
function isValidKey(key) {
|
||||||
|
@ -107,3 +126,12 @@ export function cloneElement(element, setting, ...children) {
|
||||||
export function isValidElement(element) {
|
export function isValidElement(element) {
|
||||||
return !!(element && element.vtype === TYPE_COMMON_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, []);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
interface Thenable {
|
||||||
|
then(resolve: (val?: any) => void, reject: (err: any) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// act用于测试,作用是:如果fun触发了刷新(包含了异步刷新),可以保证在act后面的代码是在刷新完成后才执行。
|
||||||
|
function act(fun: () => void | Thenable): Thenable {
|
||||||
|
const funRet = asyncUpdates(fun);
|
||||||
|
|
||||||
|
callRenderQueue();
|
||||||
|
|
||||||
|
// 如果fun返回的是Promise
|
||||||
|
if (isPromise(funRet)) {
|
||||||
|
// testing-library会返回Promise
|
||||||
|
return {
|
||||||
|
then(resolve, reject) {
|
||||||
|
funRet.then(
|
||||||
|
() => {
|
||||||
|
if (typeof setImmediate === 'function') {
|
||||||
|
// 通过setImmediate回调,用于等待业务的setTimeout完成
|
||||||
|
setImmediate(() => {
|
||||||
|
callRenderQueue();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callRenderQueue();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
reject(err);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
then(resolve) {
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callRenderQueue() {
|
||||||
|
callRenderQueueImmediate();
|
||||||
|
runAsyncEffects();
|
||||||
|
// effects可能产生刷新任务,这里再执行一次
|
||||||
|
callRenderQueueImmediate();
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
act
|
||||||
|
};
|
|
@ -53,6 +53,43 @@ export type ReduxMiddleware = (
|
||||||
|
|
||||||
type Reducer = (state: any, action: ReduxAction) => any;
|
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 {
|
export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): ReduxStoreHandler {
|
||||||
const store = createStoreX({
|
const store = createStoreX({
|
||||||
id: 'defaultStore',
|
id: 'defaultStore',
|
||||||
|
@ -69,6 +106,7 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
|
||||||
if (result === undefined) {
|
if (result === undefined) {
|
||||||
return;
|
return;
|
||||||
} // NOTE: reducer should never return undefined, in this case, do not change state
|
} // NOTE: reducer should never return undefined, in this case, do not change state
|
||||||
|
// mergeData(state,result);
|
||||||
state.stateWrapper = result;
|
state.stateWrapper = result;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -77,6 +115,10 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
|
||||||
},
|
},
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// store.$subscribe(()=>{
|
||||||
|
// console.log('changed');
|
||||||
|
// });
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
reducer,
|
reducer,
|
||||||
getState: function () {
|
getState: function () {
|
||||||
|
|
|
@ -175,7 +175,11 @@ window.addEventListener('message', messageEvent => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// executes store action
|
// executes store action
|
||||||
|
<<<<<<< HEAD
|
||||||
if (messageEvent.data.payload.type === 'horizonx executue action') {
|
if (messageEvent.data.payload.type === 'horizonx executue action') {
|
||||||
|
=======
|
||||||
|
if (messageEvent.data?.payload?.type === 'horizonx executue action') {
|
||||||
|
>>>>>>> master
|
||||||
const data = messageEvent.data.payload.data;
|
const data = messageEvent.data.payload.data;
|
||||||
const store = getStore(data.storeId);
|
const store = getStore(data.storeId);
|
||||||
if (!store?.[data.action]) return;
|
if (!store?.[data.action]) return;
|
||||||
|
|
|
@ -72,7 +72,7 @@ function createClassErrorUpdate(vNode: VNode, error: any): Update {
|
||||||
}
|
}
|
||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
function isPromise(error: any): error is PromiseType<any> {
|
export function isPromise(error: any): error is PromiseType<any> {
|
||||||
return error !== null && typeof error === 'object' && typeof error.then === 'function';
|
return error !== null && typeof error === 'object' && typeof error.then === 'function';
|
||||||
}
|
}
|
||||||
// 处理capture和bubble阶段抛出的错误
|
// 处理capture和bubble阶段抛出的错误
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { VNode } from './vnode/VNode';
|
import { VNode } from './vnode/VNode';
|
||||||
|
|
||||||
const currentRootStack: VNode[] = [];
|
const currentRootStack: VNode[] = [];
|
||||||
export function getCurrentRoot() {
|
export function getCurrentRoot() {
|
||||||
return currentRootStack[currentRootStack.length - 1];
|
return currentRootStack[currentRootStack.length - 1];
|
||||||
|
|
|
@ -77,3 +77,5 @@ export type Source = {
|
||||||
fileName: string;
|
fileName: string;
|
||||||
lineNumber: number;
|
lineNumber: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Callback = () => void;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* 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';
|
import { FlagUtils, ShouldCapture } from './vnode/VNodeFlags';
|
||||||
|
|
||||||
export type Update = {
|
export type Update = {
|
||||||
|
@ -22,8 +22,6 @@ export type Update = {
|
||||||
callback: Callback | null;
|
callback: Callback | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Callback = () => any;
|
|
||||||
|
|
||||||
export type Updates = Array<Update> | null;
|
export type Updates = Array<Update> | null;
|
||||||
|
|
||||||
export enum UpdateState {
|
export enum UpdateState {
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {Callback} from '../Types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component的api setState和forceUpdate在实例生成阶段实现
|
* Component的api setState和forceUpdate在实例生成阶段实现
|
||||||
*/
|
*/
|
||||||
|
@ -29,7 +31,7 @@ class Component<P, S, C> {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(state: S) {
|
setState(state: S, callback?: Callback) {
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
console.error('Cant not call `this.setState` in the constructor of class component, it will do nothing');
|
console.error('Cant not call `this.setState` in the constructor of class component, it will do nothing');
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
|
|
||||||
let isMessageLoopRunning = false;
|
let isMessageLoopRunning = false;
|
||||||
let browserCallback = null;
|
let browserCallback = null;
|
||||||
const { port1, port2 } = new MessageChannel();
|
let port1 = null;
|
||||||
|
let port2 = null;
|
||||||
|
let isTestRuntime = false;
|
||||||
|
|
||||||
export function isOverTime() {
|
export function isOverTime() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -41,21 +43,38 @@ const callRenderTasks = () => {
|
||||||
browserCallback = null;
|
browserCallback = null;
|
||||||
} else {
|
} else {
|
||||||
// 还有task,继续调用
|
// 还有task,继续调用
|
||||||
port2.postMessage(null);
|
asyncCall();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
port2.postMessage(null);
|
asyncCall();
|
||||||
throw error;
|
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) {
|
export function requestBrowserCallback(callback) {
|
||||||
browserCallback = callback;
|
browserCallback = callback;
|
||||||
|
|
||||||
if (!isMessageLoopRunning) {
|
if (!isMessageLoopRunning) {
|
||||||
isMessageLoopRunning = true;
|
isMessageLoopRunning = true;
|
||||||
port2.postMessage(null);
|
asyncCall();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,11 @@
|
||||||
"@babel/plugin-transform-object-super": "7.16.7",
|
"@babel/plugin-transform-object-super": "7.16.7",
|
||||||
"@babel/plugin-transform-parameters": "7.16.7",
|
"@babel/plugin-transform-parameters": "7.16.7",
|
||||||
"@babel/plugin-transform-react-jsx": "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-runtime": "7.16.7",
|
||||||
"@babel/plugin-transform-shorthand-properties": "7.16.7",
|
"@babel/plugin-transform-shorthand-properties": "7.16.7",
|
||||||
"@babel/plugin-transform-spread": "7.16.7",
|
"@babel/plugin-transform-spread": "7.16.7",
|
||||||
"@babel/plugin-transform-template-literals": "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-env": "7.16.7",
|
||||||
"@babel/preset-typescript": "7.16.7",
|
"@babel/preset-typescript": "7.16.7",
|
||||||
"@rollup/plugin-babel": "^5.3.1",
|
"@rollup/plugin-babel": "^5.3.1",
|
||||||
|
@ -67,5 +67,8 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.x",
|
"node": ">=10.x",
|
||||||
"npm": ">=7.x"
|
"npm": ">=7.x"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ejs": "^3.1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,44 +38,58 @@ if (!fs.existsSync(outDir)) {
|
||||||
|
|
||||||
const outputResolve = (...p) => path.resolve(outDir, ...p);
|
const outputResolve = (...p) => path.resolve(outDir, ...p);
|
||||||
|
|
||||||
|
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) {
|
function genConfig(mode) {
|
||||||
const isDev = mode === 'development';
|
const sourcemap = isDev(mode) ? 'inline' : false;
|
||||||
const sourcemap = isDev ? 'inline' : false;
|
|
||||||
return {
|
return {
|
||||||
input: path.resolve(libDir, 'index.ts'),
|
input: path.resolve(libDir, 'index.ts'),
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
file: outputResolve('cjs', `horizon.${mode}.js`),
|
file: outputResolve('cjs', getOutputName(mode)),
|
||||||
sourcemap,
|
sourcemap,
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
file: outputResolve('umd', `horizon.${mode}.js`),
|
file: outputResolve('umd', getOutputName(mode)),
|
||||||
sourcemap,
|
sourcemap,
|
||||||
name: 'Horizon',
|
name: 'Horizon',
|
||||||
format: 'umd',
|
format: 'umd',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
nodeResolve({
|
...getBasicPlugins(mode),
|
||||||
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.toString(),
|
|
||||||
isTest: false,
|
|
||||||
__VERSION__: `"${horizonVersion}"`,
|
|
||||||
},
|
|
||||||
preventAssignment: true,
|
|
||||||
}),
|
|
||||||
execute('npm run build-types'),
|
execute('npm run build-types'),
|
||||||
mode === 'production' && terser(),
|
mode === 'production' && terser(),
|
||||||
copy([
|
copy([
|
||||||
|
@ -92,4 +106,18 @@ function genConfig(mode) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default [genConfig('development'), genConfig('production')];
|
function genJSXRuntimeConfig(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('')];
|
||||||
|
|
Loading…
Reference in New Issue