wuji.xwt 9efa9ffa69 refactor: JS to TS
Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3677476

* chore: remove unnecessary code

* refactor: react-render using TypeScript

* chore: build-script

* refactor: editor-skeleton

* refactor: designer

* refactor: material-parser

* refactor: editor-setters

* refactor: js to ts for rax-provider 

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3678180

* refactor: rax-provider

* feat: add build command

* chore: compilerOptions for rax-provider

* refactor: JS to TS for Rax Renderer 

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3678935

* refactor: rax-renderer

* Merge remote-tracking branch 'origin/refactor/js-to-ts' into refactor/js2ts-rax-renderer

* Merge remote-tracking branch 'origin/refactor/js-to-ts' into refactor/js2ts-rax-renderer

* refactor: ts-nocheck

* chore: ts compile error

* fix: ts rootDir

* fix: compile error

* chore: using same tsconfig for rax component

* refactor: ts compile rax-renderer && rax-provider

* Merge remote-tracking branch 'origin/release/1.0.0' into refactor/js-to-ts

# Conflicts:
#	packages/rax-render/src/utils/appHelper.js
#	packages/rax-render/src/utils/appHelper.ts
#	packages/utils/src/appHelper.ts

* refactor: no JS file
2020-09-09 19:15:50 +08:00

725 lines
21 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @ts-nocheck
import Debug from 'debug';
import _keymaster from 'keymaster';
import { forEach as _forEach, shallowEqual as _shallowEqual } from '@ali/b3-one/lib/obj';
import { serialize as serializeParams } from '@ali/b3-one/lib/url';
import _moment from 'moment';
import 'moment/locale/zh-cn';
import _pick from 'lodash/pick';
import _deepEqual from 'lodash/isEqualWith';
import _clone from 'lodash/cloneDeep';
import _isEmpty from 'lodash/isEmpty';
import _throttle from 'lodash/throttle';
import _debounce from 'lodash/debounce';
import _serialize from 'serialize-javascript';
import * as _jsonuri from 'jsonuri';
import IntlMessageFormat from 'intl-messageformat';
import * as fs from 'fs-extra';
const sdkVersion = fs.readJSONSync(path.join(__dirname, '..', '..', 'package.json'));
window.sdkVersion = sdkVersion;
export const moment = _moment;
moment.locale('zh-cn');
export const forEach = _forEach;
export const shallowEqual = _shallowEqual;
export const keymaster = _keymaster;
export const pick = _pick;
export const deepEqual = _deepEqual;
export const clone = _clone;
export const isEmpty = _isEmpty;
export const throttle = _throttle;
export const debounce = _debounce;
export const serialize = _serialize;
export const jsonuri = _jsonuri;
export {
get, post, jsonp, mtop, request
} from './request';
const ReactIs = require('react-is');
const ReactPropTypesSecret = require('prop-types/lib/ReactPropTypesSecret');
const factoryWithTypeCheckers = require('prop-types/factoryWithTypeCheckers');
const PropTypes2 = factoryWithTypeCheckers(ReactIs.isElement, true);
const EXPRESSION_TYPE = {
JSEXPRESSION: 'JSExpression',
JSFUNCTION: 'JSFunction',
JSSLOT: 'JSSlot',
};
const EXPRESSION_REG = /^\{\{(\{.*\}|.*?)\}\}$/;
const hasSymbol = typeof Symbol === 'function' && Symbol.for;
const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
const debug = Debug('utils:index');
const ENV = {
TBE: 'TBE',
WEBIDE: 'WEB-IDE',
VSCODE: 'VSCODE',
WEB: 'WEB',
};
/**
* @name isSchema
* @description 判断是否是模型结构
*/
export function isSchema(schema, ignoreArr) {
if (isEmpty(schema)) return false;
if (!ignoreArr && Array.isArray(schema)) return schema.every((item) => isSchema(item));
return !!(schema.componentName && schema.props && (typeof schema.props === 'object' || isJSExpression(schema.props)));
}
export function isFileSchema(schema) {
if (isEmpty(schema)) return false;
return ['Page', 'Block', 'Component', 'Addon', 'Temp'].includes(schema.componentName);
}
// 判断当前页面是否被嵌入到同域的页面中
export function inSameDomain() {
try {
return window.parent !== window && window.parent.location.host === window.location.host;
} catch (e) {
return false;
}
}
export function getFileCssName(fileName) {
if (!fileName) return;
const name = fileName.replace(/([A-Z])/g, '-$1').toLowerCase();
return `luna-${name}`
.split('-')
.filter((p) => !!p)
.join('-');
}
export function isJSSlot(obj) {
return obj && typeof obj === 'object' && EXPRESSION_TYPE.JSSLOT === obj.type;
}
export function isJSFunction(obj) {
return obj && typeof obj === 'object' && EXPRESSION_TYPE.JSFUNCTION === obj.type;
}
export function isJSExpression(obj) {
// 兼容两种写法有js构造表达式的情况
const isJSExpressionObj = obj && typeof obj === 'object' && EXPRESSION_TYPE.JSEXPRESSION === obj.type && typeof obj.value === 'string';
const isJSExpressionStr = typeof obj === 'string' && EXPRESSION_REG.test(obj.trim());
return isJSExpressionObj || isJSExpressionStr;
}
/**
* @name wait
* @description 等待函数
*/
export function wait(ms) {
return new Promise((resolve) => setTimeout(() => resolve(true), ms));
}
export function curry(Comp, hocs = []) {
return hocs.reverse().reduce((pre, cur) => cur(pre), Comp);
}
/**
* 获取parent window上的值需要做同域判断
* @param {string} key
*/
export function getParentWinValue(key) {
if (inSameDomain()) {
return window.parent && window.parent[key];
}
}
export function getValue(obj, path, defaultValue) {
if (isEmpty(obj) || typeof obj !== 'object') return defaultValue;
const res = path.split('.').reduce((pre, cur) => pre && pre[cur], obj);
if (res === undefined) return defaultValue;
return res;
}
export function parseObj(schemaStr) {
if (typeof schemaStr !== 'string') return schemaStr;
// 默认调用顶层窗口的parseObj,保障new Function的window对象是顶层的window对象
try {
if (inSameDomain() && window.parent.__newFunc) {
return window.parent.__newFunc(`"use strict"; return ${schemaStr}`)();
}
return new Function(`"use strict"; return ${schemaStr}`)();
} catch (err) {
return undefined;
}
}
export function parseSearch(search) {
if (!search || typeof search !== 'string') {
return {};
}
const str = search.replace(/^\?/, '');
const paramStr = str.split('&');
const res = {};
paramStr.forEach((item) => {
const regRes = item.split('=');
if (regRes[0] && regRes[1]) {
res[regRes[0]] = decodeURIComponent(regRes[1]);
}
});
return res;
}
export function fastClone(obj) {
return parseObj(serialize(obj, { unsafe: true }));
}
// 更新obj的内容但不改变obj的指针
export function fillObj(receiver = {}, ...suppliers) {
Object.keys(receiver).forEach((item) => {
delete receiver[item];
});
Object.assign(receiver, ...suppliers);
return receiver;
}
// 中划线转驼峰
export function toHump(name) {
return name.replace(/\-(\w)/g, (all, letter) => letter.toUpperCase());
}
// 驼峰转中划线
export function toLine(name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase();
}
// 获取当前环境
export function getEnv() {
const { userAgent } = navigator;
const isVscode = /Electron\//.test(userAgent);
if (isVscode) return ENV.VSCODE;
const isTheia = window.is_theia === true;
if (isTheia) return ENV.WEBIDE;
return ENV.WEB;
}
/**
* 合并skeleton配置
* @param {*} 骨架默认配置
* @param {*} 用户自定义配置
*/
export function comboSkeletonConfig(defaultConfig = {}, customConfig) {
const {
skeleton, theme, addons, hooks, shortCuts, extensions, constants, utils, i18n
} = customConfig || {};
if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') {
return skeleton.handler({
skeleton,
...defaultConfig,
});
}
const defaultShortCuts = transformArrayToMap(defaultConfig.shortCuts || [], 'keyboard');
const customShortCuts = transformArrayToMap(shortCuts || [], 'keyboard');
const localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP'];
const i18nConfig = {};
localeList.forEach((key) => {
i18nConfig[key] = {
...(defaultConfig.i18n && defaultConfig.i18n[key]),
...(i18n && i18n[key]),
};
});
return {
skeleton,
theme: {
...defaultConfig.theme,
...theme,
},
addons: {
...defaultConfig.addons,
...addons,
},
hooks: [...(defaultConfig.hooks || []), ...(hooks || [])],
shortCuts: Object.values({
...defaultShortCuts,
...customShortCuts,
}),
extensions: {
...defaultConfig.extensions,
...extensions,
},
constants: {
...defaultConfig.constants,
...constants,
},
utils: [...(defaultConfig.utils || []), ...(utils || [])],
i18n: i18nConfig,
};
}
/**
* 用于构造国际化字符串处理函数
* @param {*} locale 国际化标识,例如 zh-CN、en-US
* @param {*} messages 国际化语言包
*/
export function generateI18n(locale = 'zh-CN', messages = {}) {
return (key, values = {}) => {
if (!messages || !messages[key]) return '';
const formater = new IntlMessageFormat(messages[key], locale);
return formater.format(values);
};
}
/**
* 判断当前组件是否能够设置ref
* @param {*} Comp 需要判断的组件
*/
export function acceptsRef(Comp) {
return Comp && Comp.prototype && Comp.prototype.setState;
}
/**
* 黄金令箭埋点
* @param {String} gmKey 为黄金令箭业务类型
* @param {Object} params 参数
* @param {String} logKey 属性串
*/
export function goldlog(gmKey, params = {}, logKey = 'other') {
// vscode 黄金令箭API
const sendIDEMessage = window.sendIDEMessage || getParentWinValue('sendIDEMessage');
const goKey = serializeParams({
sdkVersion,
env: getEnv(),
...params,
});
if (sendIDEMessage) {
sendIDEMessage({
action: 'goldlog',
data: {
logKey: `/iceluna.core.${logKey}`,
gmKey,
goKey,
},
});
}
window.goldlog && window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST');
}
// utils为编辑器打包生成的utils文件内容utilsConfig为数据库存放的utils配置
export function generateUtils(utils, utilsConfig) {
if (!Array.isArray(utilsConfig)) return { ...utils };
const res = {};
utilsConfig.forEach((item) => {
if (!item.name || !item.type || !item.content) return;
if (item.type === 'function' && typeof item.content === 'function') {
res[item.name] = item.content;
} else if (item.type === 'npm' && utils[item.name]) {
res[item.name] = utils[item.name];
}
});
return res;
}
// 复制到粘贴板
export function setClipboardData(str) {
return new Promise((resolve, reject) => {
if (typeof str !== 'string') reject('不支持拷贝');
if (navigator.clipboard) {
navigator.clipboard
.writeText(str)
.then(() => {
resolve();
})
.catch((err) => {
reject('复制失败,请重试!', err);
});
} else {
const textArea = document.createElement('textarea');
textArea.value = str;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand('copy');
if (successful) {
document.body.removeChild(textArea);
resolve();
}
} catch (err) {
document.body.removeChild(textArea);
reject('复制失败,请重试!', err);
}
}
});
}
// 获取粘贴板数据
export function getClipboardData() {
return new Promise((resolve, reject) => {
if (window.clipboardData) {
resolve(window.clipboardData.getData('text'));
} else if (navigator.clipboard) {
return navigator.clipboard
.readText()
.then((res) => {
resolve(res);
})
.catch((err) => {
reject('粘贴板获取失败', err);
});
} else {
reject('粘贴板获取失败');
}
});
}
// 将函数返回结果转成promise形式如果函数有返回值则根据返回值的bool类型判断是reject还是resolve若函数无返回值默认执行resolve
export function transformToPromise(input) {
if (input instanceof Promise) return input;
return new Promise((resolve, reject) => {
if (input || input === undefined) {
resolve();
} else {
reject();
}
});
}
export function moveArrayItem(arr, sourceIdx, distIdx, direction) {
if (
!Array.isArray(arr)
|| sourceIdx === distIdx
|| sourceIdx < 0
|| sourceIdx >= arr.length
|| distIdx < 0
|| distIdx >= arr.length
) {
return arr;
}
const item = arr[sourceIdx];
if (direction === 'after') {
arr.splice(distIdx + 1, 0, item);
} else {
arr.splice(distIdx, 0, item);
}
if (sourceIdx < distIdx) {
arr.splice(sourceIdx, 1);
} else {
arr.splice(sourceIdx + 1, 1);
}
return arr;
}
export function transformArrayToMap(arr, key, overwrite = true) {
if (isEmpty(arr) || !Array.isArray(arr)) return {};
const res = {};
arr.forEach((item) => {
const curKey = item[key];
if (item[key] === undefined) return;
if (res[curKey] && !overwrite) return;
res[curKey] = item;
});
return res;
}
export function checkPropTypes(value, name, rule, componentName) {
if (typeof rule === 'string') {
rule = new Function(`"use strict"; const PropTypes = arguments[0]; return ${rule}`)(PropTypes2);
}
if (!rule || typeof rule !== 'function') {
console.warn('checkPropTypes should have a function type rule argument');
return true;
}
const err = rule(
{
[name]: value,
},
name,
componentName,
'prop',
null,
ReactPropTypesSecret,
);
if (err) {
console.warn(err);
}
return !err;
}
export function transformSchemaToPure(obj) {
const pureObj = (obj) => {
if (Array.isArray(obj)) {
return obj.map((item) => pureObj(item));
}
if (typeof obj === 'object') {
// 对于undefined及null直接返回
if (!obj) return obj;
const res = {};
forEach(obj, (val, key) => {
if (key.startsWith('__') && key !== '__ignoreParse') return;
res[key] = pureObj(val);
});
return res;
}
return obj;
};
return pureObj(obj);
}
export function transformSchemaToStandard(obj) {
const standardObj = (obj) => {
if (Array.isArray(obj)) {
return obj.map((item) => standardObj(item));
}
if (typeof obj === 'object') {
// 对于undefined及null直接返回
if (!obj) return obj;
const res = {};
forEach(obj, (val, key) => {
if (key.startsWith('__') && key !== '__ignoreParse') return;
if (isSchema(val) && key !== 'children' && obj.type !== 'JSSlot') {
res[key] = {
type: 'JSSlot',
value: standardObj(val),
};
// table特殊处理
if (key === 'cell') {
res[key].params = ['value', 'index', 'record'];
}
} else {
res[key] = standardObj(val);
}
});
return res;
}
if (typeof obj === 'function') {
return {
type: 'JSFunction',
value: obj.toString(),
};
}
if (typeof obj === 'string' && EXPRESSION_REG.test(obj.trim())) {
const regRes = obj.trim().match(EXPRESSION_REG);
return {
type: 'JSExpression',
value: (regRes && regRes[1]) || '',
};
}
return obj;
};
return standardObj(obj, false);
}
export function transformStringToFunction(str) {
if (typeof str !== 'string') return str;
if (inSameDomain() && window.parent.__newFunc) {
return window.parent.__newFunc(`"use strict"; return ${str}`)();
}
return new Function(`"use strict"; return ${str}`)();
}
export function addCssTag(id, content) {
let styleTag = document.getElementById(id);
if (styleTag) {
styleTag.innerHTML = content;
return;
}
styleTag = document.createElement('style');
styleTag.id = id;
styleTag.class = 'luna-style';
styleTag.innerHTML = content;
document.head.appendChild(styleTag);
}
// 注册快捷
export function registShortCuts(config, appHelper) {
const keyboardFilter = (keymaster.filter = (event) => {
const eTarget = event.target || event.srcElement;
const { tagName } = eTarget;
const isInput = !!(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
const isContenteditable = (target) => {
while (target) {
if (target.contentEditable === 'true') return true;
target = target.parentNode;
}
return false;
};
if (isInput || isContenteditable(eTarget)) {
if (event.metaKey === true && [70, 83].includes(event.keyCode)) event.preventDefault(); // 禁止触发chrome原生的页面保存或查找
return false;
}
return true;
});
const ideMessage = appHelper.utils && appHelper.utils.ideMessage;
// 复制
if (!document.copyListener) {
document.copyListener = (e) => {
if (!keyboardFilter(e) || appHelper.isCopying) return;
const schema = appHelper.schemaHelper && appHelper.schemaHelper.schemaMap[appHelper.activeKey];
if (!schema || !isSchema(schema)) return;
appHelper.isCopying = true;
const schemaStr = serialize(transformSchemaToPure(schema), {
unsafe: true,
});
setClipboardData(schemaStr)
.then(() => {
ideMessage && ideMessage('success', '当前内容已复制到剪贴板请使用快捷键Command+v进行粘贴');
appHelper.emit('schema.copy', schemaStr, schema);
appHelper.isCopying = false;
})
.catch((errMsg) => {
ideMessage && ideMessage('error', errMsg);
appHelper.isCopying = false;
});
};
document.addEventListener('copy', document.copyListener);
if (getParentWinValue('vscode')) {
keymaster('command+c', document.copyListener);
}
}
// 粘贴
if (!document.pasteListener) {
const doPaste = (e, text) => {
if (!keyboardFilter(e) || appHelper.isPasting) return;
const { schemaHelper } = appHelper;
let targetKey = appHelper.activeKey;
let direction = 'after';
const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey;
if (!targetKey || topKey === targetKey) {
const { schemaHelper } = appHelper;
const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey;
if (!topKey) return;
targetKey = topKey;
direction = 'in';
}
appHelper.isPasting = true;
const schema = parseObj(text);
if (!isSchema(schema)) {
appHelper.emit('illegalSchema.paste', text);
// ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!');
console.warn('paste schema illegal');
appHelper.isPasting = false;
return;
}
appHelper.emit('material.add', {
schema,
targetKey,
direction,
});
appHelper.isPasting = false;
appHelper.emit('schema.paste', schema);
};
document.pasteListener = (e) => {
const clipboardData = e.clipboardData || window.clipboardData;
const text = clipboardData && clipboardData.getData('text');
doPaste(e, text);
};
document.addEventListener('paste', document.pasteListener);
if (getParentWinValue('vscode')) {
keymaster('command+v', (e) => {
const sendIDEMessage = getParentWinValue('sendIDEMessage');
sendIDEMessage
&& sendIDEMessage({
action: 'readClipboard',
})
.then((text) => {
doPaste(e, text);
})
.catch((err) => {
console.warn(err);
});
});
}
}
(config || []).forEach((item) => {
keymaster(item.keyboard, (ev) => {
ev.preventDefault();
item.handler(ev, appHelper, keymaster);
});
});
}
// 取消注册快捷
export function unRegistShortCuts(config) {
(config || []).forEach((item) => {
keymaster.unbind(item.keyboard);
});
if (getParentWinValue('vscode')) {
keymaster.unbind('command+c');
keymaster.unbind('command+v');
}
if (document.copyListener) {
document.removeEventListener('copy', document.copyListener);
delete document.copyListener;
}
if (document.pasteListener) {
document.removeEventListener('paste', document.pasteListener);
delete document.pasteListener;
}
}
export function parseData(schema, self) {
if (isJSExpression(schema)) {
return parseExpression(schema, self);
}
if (typeof schema === 'string') {
return schema.trim();
}
if (Array.isArray(schema)) {
return schema.map((item) => parseData(item, self));
}
if (typeof schema === 'function') {
return schema.bind(self);
}
if (typeof schema === 'object') {
// 对于undefined及null直接返回
if (!schema) return schema;
const res = {};
forEach(schema, (val, key) => {
if (key.startsWith('__')) return;
res[key] = parseData(val, self);
});
return res;
}
return schema;
}
/* 全匹配{{开头,}}结尾的变量表达式或者对象类型JSExpression且均不支持省略this */
export function parseExpression(str, self) {
try {
const contextArr = ['"use strict";', 'var __self = arguments[0];'];
contextArr.push('return ');
let tarStr;
// 向前兼容,支持标准协议新格式
if (typeof str === 'string') {
const regRes = str.trim().match(EXPRESSION_REG);
tarStr = regRes[1];
} else {
tarStr = (str.value || '').trim();
}
tarStr = tarStr.replace(/this(\W|$)/g, (a, b) => `__self${b}`);
tarStr = contextArr.join('\n') + tarStr;
// 默认调用顶层窗口的parseObj,保障new Function的window对象是顶层的window对象
if (inSameDomain() && window.parent.__newFunc) {
return window.parent.__newFunc(tarStr)(self);
}
return new Function(tarStr)(self);
} catch (err) {
debug('parseExpression.error', err, str, self);
return undefined;
}
}
/**
* 判断组件配置中是否有reactNode且type为function的props
* @param {*} componentInfo
*/
export function hasReactNodeFuncProps(componentInfo) {
const isReactNodeFuncProps = (config) => {
if (config.type === 'ReactNode') {
return config.props && config.props.type === 'function';
}
if (config.type === 'Mixin') {
return config.props && config.props.reactNodeProps && config.props.reactNodeProps.type === 'function';
}
};
return componentInfo && (componentInfo.props || []).some((item) => isReactNodeFuncProps(item));
}