Match-id-474b061fef13390c1c27879d396452c02602ca12
This commit is contained in:
commit
27a94beecc
|
@ -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
|
||||||
|
};
|
|
@ -3,10 +3,12 @@ import { OBSERVED_COMPONENTS } from './constants';
|
||||||
|
|
||||||
const sessionId = Date.now();
|
const sessionId = Date.now();
|
||||||
|
|
||||||
|
// this function is used to detect devtool connection
|
||||||
export function isPanelActive() {
|
export function isPanelActive() {
|
||||||
return window['__HORIZON_DEV_HOOK__'];
|
return window['__HORIZON_DEV_HOOK__'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serializes store and creates expanded object with baked-in containing current computed values
|
||||||
function makeStoreSnapshot({ type, data }) {
|
function makeStoreSnapshot({ type, data }) {
|
||||||
const expanded = {};
|
const expanded = {};
|
||||||
Object.keys(data.store.$c).forEach(key => {
|
Object.keys(data.store.$c).forEach(key => {
|
||||||
|
@ -21,6 +23,7 @@ function makeStoreSnapshot({ type, data }) {
|
||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// safely serializes variables containing values wrapped in Proxy object
|
||||||
function makeProxySnapshot(obj) {
|
function makeProxySnapshot(obj) {
|
||||||
let clone;
|
let clone;
|
||||||
try {
|
try {
|
||||||
|
@ -47,6 +50,7 @@ function makeProxySnapshot(obj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const devtools = {
|
export const devtools = {
|
||||||
|
// returns vNode id from horizon devtools
|
||||||
getVNodeId: vNode => {
|
getVNodeId: vNode => {
|
||||||
if (!isPanelActive()) return;
|
if (!isPanelActive()) return;
|
||||||
getVNodeId(vNode);
|
getVNodeId(vNode);
|
||||||
|
@ -61,6 +65,7 @@ export const devtools = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// collects components that are dependant on horizonx store and their ids
|
||||||
function getAffectedComponents() {
|
function getAffectedComponents() {
|
||||||
const allStores = getAllStores();
|
const allStores = getAllStores();
|
||||||
const keys = Object.keys(allStores);
|
const keys = Object.keys(allStores);
|
||||||
|
@ -87,8 +92,9 @@ function getAffectedComponents() {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listens to messages from background
|
||||||
window.addEventListener('message', messageEvent => {
|
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
|
// get observed components
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.postMessage({
|
window.postMessage({
|
||||||
|
@ -99,15 +105,15 @@ window.addEventListener('message', messageEvent => {
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageEvent.data.payload.type === 'horizonx executue action') {
|
// executes store action
|
||||||
|
if (messageEvent.data?.payload?.type === 'horizonx executue action') {
|
||||||
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]) {
|
if (!store?.[data.action]) return;
|
||||||
}
|
|
||||||
|
|
||||||
const action = store[data.action];
|
const action = store[data.action];
|
||||||
const params = data.params;
|
const params = data.params;
|
||||||
action(...params).bind(store);
|
action(...params);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,9 @@ export class Observer implements IObserver {
|
||||||
// 对象的属性被赋值时调用
|
// 对象的属性被赋值时调用
|
||||||
setProp(key: string | symbol, mutation: any): void {
|
setProp(key: string | symbol, mutation: any): void {
|
||||||
const vNodes = this.keyVNodes.get(key);
|
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) {
|
if (vNode.isStoreChange) {
|
||||||
// VNode已经被触发过,不再重复触发
|
// VNode已经被触发过,不再重复触发
|
||||||
return;
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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