Compare commits

...

12 Commits

157 changed files with 7387 additions and 3061 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ pnpm-lock.yaml
build
/packages/inula-router/connectRouter
/packages/inula-router/router
.inula-max

View File

@ -0,0 +1,4 @@
# openinula + vite
该模板提供了 `openinula` 工作在 `vite`的基础配置。
> 请注意由于Vite插件有node版本限制请使用`node -v`命令确认node版本大于等于node v18。

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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 Inula from 'openinula';
import './styles.css';
class App extends Inula.Component {
render() {
return (
<div class="container">
<div class="hero">
<h1 class="hero-title animate__animated animate__bounceInDown">欢迎来到 Inula 项目!</h1>
<p class="hero-subtitle animate__animated animate__bounceInUp">你已成功创建你的第一个 Inula 项目</p>
</div>
<div class="content">
<div class="card animate__animated animate__zoomIn">
<h2>开始吧</h2>
<p>
编辑 <code>src/App.js</code> 并保存以重新加载
</p>
</div>
<div class="card animate__animated animate__zoomIn">
<h2>了解更多</h2>
<p>
要了解 Inula查看{' '}
<a href="https://openinula.org" target="_blank">
Inula 官网
</a>
</p>
</div>
</div>
</div>
);
}
}
export default App;

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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 Inula from 'openinula';
import App from './App';
Inula.render(<App />, document.getElementById('root'));

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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.
*/
const BasicGenerator = require('../../BasicGenerator');
class Generator extends BasicGenerator {
prompting() {
return this.prompt([
{
type: 'list',
name: 'bundlerType',
message: 'Please select the build type',
choices: ['webpack', 'vite'],
},
]).then(props => {
this.prompts = props;
});
}
writing() {
const src = this.templatePath(this.prompts.bundlerType);
const dest = this.destinationPath();
this.writeFiles(src, dest, {
context: {
...this.prompts,
},
});
}
}
module.exports = Generator;

View File

@ -0,0 +1,3 @@
{
"description": "simple reactive app template."
}

View File

@ -0,0 +1,4 @@
# openinula + vite
该模板提供了 `openinula` 工作在 `vite`的基础配置。
> 请注意由于Vite插件有node版本限制请使用`node -v`命令确认node版本大于等于node v18。

View File

@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>My Inula App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.jsx"></script>
</body>
</html>

View File

@ -0,0 +1,25 @@
{
"name": "inula-vite-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "vite",
"build": "vite build"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"openinula": "0.0.0-experimental-20231201"
},
"devDependencies": {
"@babel/core": "^7.21.4",
"@babel/preset-env": "^7.21.4",
"@babel/preset-react": "^7.18.6",
"@vitejs/plugin-react": "^3.1.0",
"@vitejs/plugin-react-refresh": "^1.3.6",
"babel-plugin-import": "^1.13.6",
"vite": "^4.2.1"
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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 Inula, { useComputed, useReactive, useRef } from 'openinula';
function ReactiveComponent() {
const renderCount = ++useRef(0).current;
const data = useReactive({ count: 0 });
const countText = useComputed(() => {
return `计时: ${data.count.get()}`;
});
setInterval(() => {
data.count.set(c => c + 1);
}, 1000);
return (
<div>
<div>{countText}</div>
<div>组件渲染次数:{renderCount}</div>
</div>
);
}
export default ReactiveComponent;

View File

@ -0,0 +1,57 @@
* {
box-sizing: border-box;
}
body,
html {
margin: 0;
padding: 0;
font-family: 'Montserrat', sans-serif;
line-height: 1.6;
color: #fff;
background: linear-gradient(120deg, #6a11cb 0%, #2575fc 100%);
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
text-align: center;
}
.hero-title {
font-size: 3em;
margin-bottom: 20px;
}
.hero-subtitle {
font-size: 1.5em;
margin-bottom: 50px;
}
.content {
display: flex;
justify-content: space-between;
align-items: center;
margin: 1vh;
}
.card {
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
padding: 20px;
width: 100%;
box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(4px);
}
.card h2,
.card p {
color: #fff;
}
.card a {
color: #fff;
text-decoration: underline;
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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 Inula from 'openinula';
import ReactiveComponent from './ReactiveComponent';
import './index.css';
function App() {
return (
<div class="container">
<div class="hero">
<h1 class="hero-title animate__animated animate__bounceInDown">欢迎来到 Inula 项目!</h1>
<p class="hero-subtitle animate__animated animate__bounceInUp">你已成功创建你的第一个响应式 Inula 项目</p>
</div>
<div className="content">
<div className="card animate__animated animate__zoomIn">
<ReactiveComponent />
</div>
</div>
<div class="content">
<div class="card animate__animated animate__zoomIn">
<h2>了解更多</h2>
<p>
要了解 Inula查看{' '}
<a href="https://openinula.net/" target="_blank">
Inula 官网
</a>
</p>
</div>
</div>
</div>
);
}
Inula.render(<App />, document.getElementById('root'));

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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 react from '@vitejs/plugin-react';
let alias = {
react: 'openinula', // 新增
'react-dom': 'openinula', // 新增
'react/jsx-dev-runtime': 'openinula/jsx-dev-runtime',
};
export default {
plugins: [react()],
resolve: {
alias,
},
};

View File

@ -0,0 +1,29 @@
{
"name": "inula-webpack-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack serve --mode development",
"build": "webpack --mode production"
},
"author": "",
"license": "ISC",
"dependencies": {
"openinula": "0.0.0-experimental-20231201"
},
"devDependencies": {
"@babel/core": "^7.21.4",
"@babel/preset-env": "^7.21.4",
"@babel/preset-react": "^7.18.6",
"babel-loader": "^9.1.2",
"css-loader": "^6.7.3",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0",
"style-loader": "^3.3.2",
"url-loader": "^4.1.1",
"webpack": "^5.77.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.13.2"
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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 Inula from 'openinula';
import ReactiveComponent from './ReactiveComponent';
import './styles.css';
class App extends Inula.Component {
render() {
return (
<div class="container">
<div class="hero">
<h1 class="hero-title animate__animated animate__bounceInDown">欢迎来到 Inula 项目!</h1>
<p class="hero-subtitle animate__animated animate__bounceInUp">你已成功创建你的第一个响应式 Inula 项目</p>
</div>
<div className="content">
<div className="card animate__animated animate__zoomIn">
<ReactiveComponent />
</div>
</div>
<div class="content">
<div class="card animate__animated animate__zoomIn">
<h2>了解更多</h2>
<p>
要了解 Inula查看{' '}
<a href="https://openinula.org" target="_blank">
Inula 官网
</a>
</p>
</div>
</div>
</div>
);
}
}
export default App;

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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 Inula, { useComputed, useReactive, useRef } from 'openinula';
function ReactiveComponent() {
const renderCount = ++useRef(0).current;
const data = useReactive({ count: 0 });
const countText = useComputed(() => {
return `计时: ${data.count.get()}`;
});
setInterval(() => {
data.count.set(c => c + 1);
}, 1000);
return (
<div>
<div>{countText}</div>
<div>组件渲染次数:{renderCount}</div>
</div>
);
}
export default ReactiveComponent;

View File

@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Inula App</title>
</head>
<body>
<div id="root"></div>
<script src="../dist/bundle.js"></script>
</body>
</html>

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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 Inula from 'openinula';
import App from './App';
Inula.render(<App />, document.getElementById('root'));

View File

@ -0,0 +1,57 @@
* {
box-sizing: border-box;
}
body,
html {
margin: 0;
padding: 0;
font-family: 'Montserrat', sans-serif;
line-height: 1.6;
color: #fff;
background: linear-gradient(120deg, #6a11cb 0%, #2575fc 100%);
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
text-align: center;
}
.hero-title {
font-size: 3em;
margin-bottom: 20px;
}
.hero-subtitle {
font-size: 1.5em;
margin-bottom: 50px;
}
.content {
display: flex;
justify-content: space-between;
align-items: center;
margin: 1vh;
}
.card {
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
padding: 20px;
width: 100%;
box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(4px);
}
.card h2,
.card p {
color: #fff;
}
.card a {
color: #fff;
text-decoration: underline;
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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.
*/
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
[
'@babel/preset-react',
{
runtime: 'automatic', // 新增
importSource: 'openinula', // 新增
},
],
],
},
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
publicPath: 'images/',
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src/index.html'),
filename: 'index.html',
}),
],
devServer: {
static: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
open: true,
},
resolve: {
extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'],
},
};

View File

@ -57,7 +57,7 @@ export default (api: API) => {
api.applyHook({ name: 'afterStartDevServer' });
});
} else {
api.logger.error("Can't find config");
api.logger.error('Can\'t find config');
}
break;
case 'vite':
@ -70,7 +70,7 @@ export default (api: API) => {
server.printUrls();
});
} else {
api.logger.error("Can't find config");
api.logger.error('Can\'t find config');
}
break;
default:

View File

@ -33,7 +33,7 @@ export default (api: API) => {
args._.shift();
}
if (args._.length === 0) {
api.logger.warn("Can't find any generate options.");
api.logger.warn('Can\'t find any generate options.');
return;
}

View File

@ -35,8 +35,8 @@
"html-webpack-plugin": "^5.5.1",
"jest-environment-jsdom": "^29.5.0",
"jsdom": "^21.1.1",
"react": "18.2.0-h3",
"react-dom": "18.2.0-h3",
"react": "18.2.0",
"react-dom": "18.2.0",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-serve": "^1.1.0",
"rollup-plugin-visualizer": "^5.10.0",

View File

@ -21,7 +21,6 @@
],
"license": "MulanPSL2",
"devDependencies": {
"@cloudsop/horizon": "^0.0.58",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",

View File

@ -16,11 +16,6 @@
import fs from 'fs';
import path from 'path';
import dts from 'rollup-plugin-dts';
import { parse } from '@babel/parser';
import MagicString from 'magic-string';
import assert from 'node:assert/strict';
const LIB_NAME = 'Inula';
function deleteFolder(filePath) {
if (fs.existsSync(filePath)) {
@ -56,264 +51,6 @@ export function cleanUp(folders) {
};
}
/**
* 获取AST语法树节点的名称
*
* @param node AST语法树节点
* @returns 节点的名称
*/
function getNodeName(node) {
if (node.type === 'VariableDeclaration') {
node = node.declarations[0];
if (!node.id) {
return '';
}
return node.id.name;
} else if (
node.type === 'TSTypeAliasDeclaration' ||
node.type === 'TSInterfaceDeclaration' ||
node.type === 'TSDeclareFunction' ||
node.type === 'TSEnumDeclaration' ||
node.type === 'ClassDeclaration' ||
node.type === 'TSModuleDeclaration'
) {
if (!node.id) {
return '';
}
return node.id.name;
}
return '';
}
/**
* 判断AST语法树节点是否是Horizon变量节点
*
* @param node AST语法树节点
* @returns true false 不是
*/
function isHorizonVariable(node) {
if (node.type === 'VariableDeclaration') {
let tmpNode = node.declarations[0];
if (!tmpNode.id) {
return false;
}
let exoprtName = tmpNode.id.name;
if (exoprtName === LIB_NAME) {
return true;
}
}
return false;
}
/**
* 为导出的类型节点加上前缀export
*
* @param node AST语法树节点
* @param isExported 导出的类型名称集合
* @param hasAliasExport 导出的重命名类型的集合
* @param magicStr 原始的AST语法树对应的magic对象
* @param parentDecl 当节点为VariableDeclaration类型的子节点时该变量代表父亲节点其他类型该参数不传
*/
function processDeclaration(node, isExported, hasAliasExport, magicStr, parentDecl) {
if (!node.id) {
return;
}
assert(node.id.type === 'Identifier');
const name = node.id.name;
if (name.startsWith('_')) {
return;
}
if (isExported.has(name) && !hasAliasExport.has(name)) {
const start = (parentDecl || node).start;
assert(typeof start === 'number');
magicStr.prependLeft(start, 'export ');
}
}
/**
* 生成Horzion的namespace达到可以Horzion.foo的效果
*
* @example
* 原始文件内容
* type foo
* type f001
* type fooA$1
* const Horzion {
* version
* }
* export { typeof foo, type f001, type fooA$1 as fooA, default Horzion}
* 修改后的文件内容:
* export type foo
* export type f001
* type fooA$1
* declare namespace Horzion {
* export { type foo };
* export { type f001 };
* export { type fooA$1 as fooA };
* }
* export { type fooA$1 as fooA };
* export default Horzion;
* @returns 修改后的文件内容
*/
function patchNamespaceType() {
return {
name: 'patch-types',
renderChunk(code) {
const magicCodeStr = new MagicString(code);
const ast = parse(code, {
plugins: ['typescript'],
sourceType: 'module',
});
const exportedSet = new Set();
const hasAliasExportMap = new Map();
const aliasTypeArr = [];
const exportTypeArr = [];
const moduleSet = new Set();
/**
* 第一部分 遍历AST语法树获取所有的导出的类型名称放入isExported
* 获取所有的声明的namespace的名称放入moduleArr
*
* @example
* export { typeof foo } 会将foo放入isExported
* delcare namespace foo1 {} 会将foo1放入moduleArr
*/
for (const node of ast.program.body) {
if (node.type === 'TSModuleDeclaration') {
moduleSet.add(getNodeName(node));
}
if (node.type === 'ExportNamedDeclaration' && !node.source) {
for (let i = 0; i < node.specifiers.length; i++) {
const spec = node.specifiers[i];
if (spec.type === 'ExportSpecifier') {
exportedSet.add(spec.local.name);
}
}
}
}
/**
* 第二部分遍历AST语法树 去除最后的export声明部分并且删除Horizon变量的声明
* 将除了namespace的导出声明外的所有导出类型放入exportTypeArr
* 将所有被重名的导出类型的名称放入aliasTypeArr
* 将所有被重名的导出类型的名称和别名放入hasAliasExportkey是类型的名称 value是导出类型的别名
*/
for (const node of ast.program.body) {
if (node.type === 'VariableDeclaration') {
if (isHorizonVariable(node)) {
// 不导出Horizon变量需要将Horizon的变量重命名为namespace
assert(typeof node.start === 'number');
assert(typeof node.end === 'number');
magicCodeStr.remove(node.start, node.end);
}
} else if (node.type === 'ExportNamedDeclaration' && !node.source) {
for (let i = 0; i < node.specifiers.length; i++) {
const spec = node.specifiers[i];
if (spec.type === 'ExportSpecifier' && spec.local.name != LIB_NAME) {
assert(spec.exported.type === 'Identifier');
const exported = spec.exported.name;
if (!moduleSet.has(spec.local.name)) {
/**
* @example
* type foo
* namespace foo1{}
* export {type foo , foo1}
*
* 最后放入到exportTypeArr中的为type foo字符串
*/
exportTypeArr.push(magicCodeStr.slice(spec.start, spec.end));
}
if (exported !== spec.local.name) {
/**
* @example
* type foo
* type foo1
* export {type foo as fooalias, type foo1}
*
* 最后放入aliasTypeArr为type foo as fooalias
* 放入hasAliasExport的key为foo value为fooalias
*/
aliasTypeArr.push(magicCodeStr.slice(spec.start, spec.end));
hasAliasExportMap.set(spec.local.name, exported);
}
}
}
assert(typeof node.start === 'number');
assert(typeof node.end === 'number');
magicCodeStr.remove(node.start, node.end);
}
}
/**
* 第三部分遍历AST语法树为所有需要导出的类型加上前缀 export
*
* @example
*
* type foo
*
* @returns
*
* export type foo
*/
for (const node of ast.program.body) {
if (node.type === 'VariableDeclaration') {
if (isHorizonVariable(node)) {
continue;
}
processDeclaration(node.declarations[0], exportedSet, hasAliasExportMap, magicCodeStr, node);
if (node.declarations.length > 1) {
assert(typeof node.start === 'number');
assert(typeof node.end === 'number');
throw new Error(
`unhandled declare const with more than one declarators:\n${code.slice(node.start, node.end)}`
);
}
} else if (
node.type === 'TSTypeAliasDeclaration' ||
node.type === 'TSInterfaceDeclaration' ||
node.type === 'TSDeclareFunction' ||
node.type === 'TSEnumDeclaration' ||
node.type === 'ClassDeclaration' ||
node.type === 'TSModuleDeclaration'
) {
processDeclaration(node, exportedSet, hasAliasExportMap, magicCodeStr);
}
}
/**
* 第四部分拼接名称为${HORIZON_NAME} 的namespace并将它作为默认导出类型
* 将所有的重命名的导出类型添加到末尾
*
* @example
*
* 结果示例如下
* export typeof foo
* export typeof foo$1
* declare namespace Horizon {
* export {typeof foo}
* export {typeof foo$1 as fooalias}
* }
*
* export {typeof foo$1 as fooalias}
* export default Horizon
*/
magicCodeStr.append(`declare namespace ${LIB_NAME} {\n`);
exportTypeArr.forEach(ele => {
magicCodeStr.append(` export { ${ele} };\n`);
});
magicCodeStr.append('}\n');
aliasTypeArr.forEach(ele => {
magicCodeStr.append(`export { ${ele} };\n`);
});
magicCodeStr.append(`export default ${LIB_NAME};`);
code = magicCodeStr.toString();
return code;
},
};
}
function buildTypeConfig() {
return {
input: ['./build/@types/index.d.ts'],
@ -321,7 +58,7 @@ function buildTypeConfig() {
file: './build/@types/index.d.ts',
format: 'es',
},
plugins: [dts(), patchNamespaceType(), cleanUp(['./build/@types/'])],
plugins: [dts(), cleanUp(['./build/@types/'])],
};
}

View File

@ -1,90 +0,0 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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.
*/
/**
* dom节点赋 VNode
*/
import type { VNode } from '../renderer/Types';
import type { Container, Props } from './DOMOperator';
import { DomComponent, DomText, TreeRoot } from '../renderer/vnode/VNodeTags';
const randomKey = Math.random().toString(16).slice(2);
const INTERNAL_VNODE = `_inula_VNode_${randomKey}`;
const INTERNAL_PROPS = `_inula_Props_${randomKey}`;
const INTERNAL_NONDELEGATEEVENTS = `_inula_nonDelegatedEvents_${randomKey}`;
export const HANDLER_KEY = `_inula_valueChangeHandler_${randomKey}`;
export const EVENT_KEY = `_inula_ev_${randomKey}`;
// 通过 VNode 实例获取 DOM 节点
export function getDom(vNode: VNode): Element | Text | null {
const { tag } = vNode;
if (tag === DomComponent || tag === DomText) {
return vNode.realNode;
}
return null;
}
// 将 VNode 属性相关信息挂到 DOM 对象的特定属性上
export function saveVNode(vNode: VNode, dom: Element | Text | Container): void {
dom[INTERNAL_VNODE] = vNode;
}
// 用 DOM 节点,来找其对应的 VNode 实例
export function getVNode(dom: Node | Container): VNode | null {
const vNode = dom[INTERNAL_VNODE] || (dom as Container)._treeRoot;
if (vNode) {
const { tag } = vNode;
if (tag === DomComponent || tag === DomText || tag === TreeRoot) {
return vNode;
}
}
return null;
}
// 用 DOM 对象,来寻找其对应或者说是最近父级的 vNode
export function getNearestVNode(dom: Node): null | VNode {
let domNode: Node | null = dom;
// 寻找当前节点及其所有祖先节点是否有标记VNODE
while (domNode) {
const vNode = domNode[INTERNAL_VNODE];
if (vNode) {
return vNode;
}
domNode = domNode.parentNode;
}
return null;
}
// 获取 vNode 上的属性相关信息
export function getVNodeProps(dom: Element | Text): Props | null {
return dom[INTERNAL_PROPS] || null;
}
// 将 DOM 属性相关信息挂到 DOM 对象的特定属性上
export function updateVNodeProps(dom: Element | Text, props: Props): void {
dom[INTERNAL_PROPS] = props;
}
export function getNonDelegatedListenerMap(dom: Element | Text): Map<string, EventListener> {
let eventsMap = dom[INTERNAL_NONDELEGATEEVENTS];
if (!eventsMap) {
eventsMap = new Map();
dom[INTERNAL_NONDELEGATEEVENTS] = eventsMap;
}
return eventsMap;
}

View File

@ -1,33 +1,25 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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.
*/
// /*
// * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
// *
// * openInula 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 { saveVNode, updateVNodeProps } from './DOMInternalKeys';
import { createDom } from './utils/DomCreator';
import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler';
import { isDocument, shouldAutoFocus } from './utils/Common';
import { controlInputValue } from './valueHandler/ValueChangeHandler';
import { updateTextareaValue } from './valueHandler/TextareaValueHandler';
import { NSS } from './utils/DomCreator';
import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler';
import type { VNode } from '../renderer/Types';
import { setInitValue, getPropsWithoutValue, updateValue } from './valueHandler';
import { compareProps, setDomProps } from './DOMPropertiesHandler/DOMPropertiesHandler';
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';
import { type Container, CommonTags } from '../renderer/Types';
export type Props = Record<string, any> & {
autoFocus?: boolean;
children?: any;
@ -37,7 +29,6 @@ export type Props = Record<string, any> & {
style?: { display?: string };
};
export type Container = (Element & { _treeRoot?: VNode | null }) | (Document & { _treeRoot?: VNode | null });
let selectionInfo: null | SelectionData = null;
@ -57,7 +48,7 @@ function getChildNS(parentNS: string | null, tagName: string): string {
// 获取容器
export function getNSCtx(parentNS: string, type: string, dom?: Container): string {
return dom ? getChildNS(dom.namespaceURI ?? null, dom.nodeName) : getChildNS(parentNS, type);
return dom ? getChildNS((dom as Element).namespaceURI ?? null, dom.nodeName) : getChildNS(parentNS, type);
}
export function prepareForSubmit(): void {
@ -69,61 +60,28 @@ export function resetAfterSubmit(): void {
selectionInfo = null;
}
// 创建 DOM 对象
export function newDom(tagName: string, props: Props, parentNamespace: string, vNode: VNode): Element {
export function createElement(tagName: string, props: Props, parentNamespace: string, rootDom: Element): {element: any, props: Props} {
// document取值于treeRoot对应的DOM的ownerDocument。
// 解决在iframe中使用top的inula时inula在创建DOM时用到的document并不是iframe的document而是top中的document的问题。
const rootDom = getCurrentRoot()?.realNode;
const doc = isDocument(rootDom) ? rootDom : rootDom.ownerDocument;
const doc = rootDom instanceof Document ? rootDom : rootDom.ownerDocument;
const dom: Element = createDom(tagName, parentNamespace, doc);
// 将 vNode 节点挂到 DOM 对象上
saveVNode(vNode, dom);
// 将属性挂到 DOM 对象上
updateVNodeProps(dom, props);
return dom;
return {element: dom, props};
}
// 设置节点默认事件、属性
export function initDomProps(dom: Element, tagName: string, rawProps: Props): boolean {
validateProps(tagName, rawProps);
// 获取不包括valuedefaultValue的属性
const props: Record<string, any> = getPropsWithoutValue(tagName, dom, rawProps);
// 初始化DOM属性不包括valuedefaultValue
const isNativeTag = isNativeElement(tagName, props);
setDomProps(dom, props, isNativeTag, true);
if (tagName === 'input' || tagName === 'textarea') {
// 增加监听value和checked的set、get方法
watchValueChange(dom);
export function handleControledElements(target: Element , type: string, props: Props) {
switch (type) {
case 'input':
controlInputValue(<HTMLInputElement>target, props);
break;
case 'textarea':
updateTextareaValue(<HTMLTextAreaElement>target, props);
break;
default:
break;
}
// 设置dom.value值触发受控组件的set方法
setInitValue(tagName, dom, rawProps);
return shouldAutoFocus(tagName, rawProps);
}
// 准备更新之前进行一系列校验 DOM寻找属性差异等准备工作
export function getPropChangeList(
dom: Element,
type: string,
lastRawProps: Props,
nextRawProps: Props
): Record<string, any> {
// 校验两个对象的不同
validateProps(type, nextRawProps);
// 重新定义的属性不需要参与对比被代理的组件需要把这些属性覆盖到props中
const oldProps: Record<string, any> = getPropsWithoutValue(type, dom, lastRawProps);
const newProps: Record<string, any> = getPropsWithoutValue(type, dom, nextRawProps);
return compareProps(oldProps, newProps);
}
export function isTextChild(type: string, props: Props): boolean {
if (type === 'textarea' || type === 'option' || type === 'noscript') {
return true;
@ -140,52 +98,11 @@ export function isTextChild(type: string, props: Props): boolean {
);
}
}
export function newTextDom(text: string, processing: VNode): Text {
const textNode: Text = document.createTextNode(text);
saveVNode(processing, textNode);
return textNode;
export function createText(text: string) {
return document.createTextNode(text);
}
// 提交vNode的类型为DomComponent或者DomText的更新
export function submitDomUpdate(tag: string, vNode: VNode) {
const newProps = vNode.props;
const element: Element | null = vNode.realNode;
if (tag === DomComponent) {
// DomComponent类型
if (element !== null && element !== undefined) {
const type = vNode.type;
const changeList = vNode.changeList;
vNode.changeList = null;
if (changeList !== null) {
saveVNode(vNode, element);
updateVNodeProps(element, newProps);
// 应用diff更新Properties.
// 当一个选中的radio改变名称,浏览器使另一个radio的复选框为false.
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);
setDomProps(element, changeList, isNativeTag, false);
updateValue(type, element, newProps);
}
}
} else if (tag === DomText) {
if (element != null) {
// text类型
element.textContent = newProps;
}
}
}
export function clearText(dom: Element): void {
dom.innerHTML = '';
@ -197,28 +114,29 @@ export function appendChildElement(parent: Element | Container, child: Element |
}
// 插入dom元素
export function insertDomBefore(parent: Element | Container, child: Element | Text, beforeChild: Element | Text) {
export function insertElementBefore(parent: Element | Container, child: Element | Text, beforeChild: Element | Text) {
parent.insertBefore(child, beforeChild);
}
export function removeChildDom(parent: Element | Container, child: Element | Text) {
export function removeChildElement(parent: Element | Container, child: Element | Text) {
parent.removeChild(child);
}
// 隐藏元素
export function hideDom(tag: string, dom: Element | Text) {
if (tag === DomComponent) {
export function hideElement(tag, dom) {
if (tag === CommonTags.ComponentElement) {
(dom as HTMLElement).style.display = 'none';
} else if (tag === DomText) {
} else if (tag === CommonTags.TextElement) {
dom.textContent = '';
}
}
// 不隐藏元素
export function unHideDom(tag: string, dom: Element | Text, props?: Props) {
if (tag === DomComponent) {
(dom as HTMLElement).style.display = adjustStyleValue('display', props?.style?.display ?? '');
} else if (tag === DomText) {
export function unHideElement(tag, dom, props?: Props) {
if (tag === CommonTags.ComponentElement) {
(dom as HTMLElement).style.display = adjustStyleValue('display', props?.style?.display ?? '') as string;
} else if (tag === CommonTags.TextElement) {
dom.textContent = props as any;
}
}

View File

@ -13,11 +13,13 @@
* See the Mulan PSL v2 for more details.
*/
import { getPropDetails, PROPERTY_TYPE } from '../validators/PropertiesData';
import { isInvalidValue } from '../validators/ValidateProps';
//kb-tag
import { getPropDetails, PROPERTY_TYPE } from '../../renderer/props/PropertiesData';
import { isInvalidValue } from '../../renderer/props/ValidateProps';
import { getNamespaceCtx } from '../../renderer/ContextSaver';
import { NSS } from '../utils/DomCreator';
import { getDomTag } from '../utils/Common';
import { getTag } from '../../renderer/utils/common';
// 不需要装换的svg属性集合
const svgHumpAttr = new Set();
@ -111,7 +113,7 @@ export function updateCommonProp(dom: Element, attrName: string, value: any, isN
if (!isNativeTag || propDetails === null) {
// 特殊处理svg的属性把驼峰式的属性名称转成'-'
if (getDomTag(dom) === 'svg' || getNamespaceCtx() === NSS.svg) {
if (getTag(dom) === 'svg' || getNamespaceCtx() === NSS.svg) {
if (!svgHumpAttr.has(attrName)) {
attrName = convertToLowerCase(attrName);
}

View File

@ -17,8 +17,8 @@
*
*/
import { getIFrameFocusedDom, isText } from './utils/Common';
import { isElement } from './utils/Common';
import { getIFrameFocusedDom } from './utils/Common';
import { isElement, isText } from '../renderer/utils/common';
type SelectionRange = {
start: number | null;

View File

@ -0,0 +1,141 @@
import { ElementType, HostConfigType } from '../renderer/Types';
import {
createElement as createDom,
clearText,
appendChildElement,
removeChildElement,
insertElementBefore,
hideElement,
unHideElement,
isTextChild,
handleControledElements,
createText,
prepareForSubmit,
resetAfterSubmit,
} from './DOMOperator';
import { setStyles } from './DOMPropertiesHandler/StyleHandler';
import { updateCommonProp } from './DOMPropertiesHandler/UpdateCommonProp';
import { updateValue } from './valueHandler';
import { getPropsWithoutValue } from './valueHandler';
import { watchValueChange } from './valueHandler/ValueChangeHandler';
import { setInitValue } from './valueHandler';
import { InulaDom } from './utils/Interface';
import { updateInputHandlerIfChanged } from './valueHandler/ValueChangeHandler';
export const defaultHostConfig: Partial<HostConfigType> = {
elementConfig: {
common: document.createElement('div'),
text: document.createTextNode(''),
input: document.createElement('input'),
button: document.createElement('button'),
select: document.createElement('select'),
textarea: document.createElement('textarea'),
document: document,
},
addEventListener(element, eventName, handler, isCapture) {
element.addEventListener(eventName, handler, isCapture);
},
removeEventListener(element, eventName, handler) {
element.removeEventListener(eventName, handler);
},
createElement(tagName, props, parentNamespace, rootElement) {
return createDom(tagName, props, parentNamespace, rootElement as Element);
},
createText(text) {
return createText(text) as ElementType;
},
getProps(type, element, props) {
return getPropsWithoutValue(type, element as Element, props);
},
handleControledInputElements(target, type, props) {
return handleControledElements(target as Element, type, props);
},
isTextChild(type, props) {
return isTextChild(type, props);
},
setProps(element, propName, propVal, isNativeTag, isInit) {
if (propName === 'style') {
setStyles(element, propVal);
} else if (propName === 'children') {
// 只处理纯文本子节点其他children在VNode树中处理
const type = typeof propVal;
if (type === 'string' || type === 'number') {
element.textContent = propVal;
}
} else if (propName === 'dangerouslySetInnerHTML') {
element.innerHTML = propVal.__html;
} else if (!isInit || (propVal !== null && propVal !== undefined)) {
updateCommonProp(element as Element, propName, propVal, isNativeTag);
}
},
updateInputValue(type, element, props) {
updateValue(type, element as Element, props);
},
onSubmit(tag, type, element, newProps) {
if (tag === 'Component') {
if (
type === 'input' &&
newProps.type === 'radio' &&
newProps.name !== null &&
newProps.name !== undefined &&
newProps.checked !== null &&
newProps.checked !== undefined
) {
updateCommonProp(element as Element, 'checked', newProps.checked, true);
}
} else {
if (element != null) {
// text类型
element.textContent = newProps;
}
}
},
shouldTriggerChangeEvent(targetElement, elementTag, evtName) {
const {type} = targetElement
if (elementTag === 'select' || (elementTag === 'input' && type === 'file')) {
return evtName === 'change';
} else if (elementTag === 'input' && (type === 'checkbox' || type === 'radio')) {
if (evtName === 'click') {
return updateInputHandlerIfChanged(targetElement);
}
} else if (targetElement.nodeType === this.elementConfig?.input.nodeType) {
if (evtName === 'input' || evtName === 'change') {
return updateInputHandlerIfChanged(targetElement);
}
}
return false;
},
hideElement(tag, element) {
hideElement(tag, element);
},
unHideElement(tag, element, props) {
unHideElement(tag, element, props);
},
clearText(element) {
clearText(element as Element);
},
appendChildElement(parent, child) {
appendChildElement(parent as Element, child as Element | Text);
},
insertElementBefore(parent, child, beforeChild) {
insertElementBefore(parent as Element, child as Element | Text, beforeChild as Element | Text);
},
removeChildElement(parent, child) {
removeChildElement(parent as Element, child as Element | Text);
},
prepareForSubmit() {
prepareForSubmit();
},
resetAfterSubmit() {
resetAfterSubmit();
},
onPropInit(element, tagName, rawProps) {
if (tagName === this.elementConfig?.input.nodeName || tagName === this.elementConfig?.textarea.nodeName) {
// 增加监听value和checked的set、get方法
watchValueChange(element);
}
// 设置dom.value值触发受控组件的set方法
setInitValue(tagName, element as InulaDom, rawProps);
},
};

View File

@ -14,7 +14,6 @@
*/
import { InulaDom } from './Interface';
import { Props } from '../DOMOperator';
/**
* input textarea
@ -48,43 +47,4 @@ export function getIFrameFocusedDom() {
}
}
return focusedDom;
}
export function isElement(dom) {
return dom.nodeType === 1;
}
export function isText(dom) {
return dom.nodeType === 3;
}
export function isComment(dom) {
return dom.nodeType === 8;
}
export function isDocument(dom) {
return dom.nodeType === 9;
}
export function isDocumentFragment(dom) {
return dom.nodeType === 11;
}
export function getDomTag(dom) {
return dom.nodeName.toLowerCase();
}
export function isInputElement(dom: Element): dom is HTMLInputElement {
return getDomTag(dom) === 'input';
}
const types = ['button', 'input', 'select', 'textarea'];
// button、input、select、textarea、如果有 autoFocus 属性需要focus
export function shouldAutoFocus(tagName: string, props: Props): boolean {
return types.includes(tagName) ? Boolean(props.autoFocus) : false;
}
export function isNotNull(object: any): boolean {
return object !== null && object !== undefined;
}
}

View File

@ -14,7 +14,8 @@
*/
import { Children } from '../../external/ChildrenUtil';
import { Props } from '../utils/Interface';
// kb-tag
import { Props } from '../../renderer/Types';
// 把 const a = 'a'; <option>gir{a}ffe</option> 转成 giraffe
function concatChildren(children) {

View File

@ -13,12 +13,16 @@
* See the Mulan PSL v2 for more details.
*/
//kb-tag
import { isNotNull } from '../../renderer/utils/common';
import { updateInputValue } from './InputValueHandler';
/**
* Inula的输入框和文本框的change事件在原生的change事件上做了一层处理
* change事件
*/
import { HANDLER_KEY } from '../DOMInternalKeys';
import { HANDLER_KEY } from '../../renderer/utils/InternalKeys';
import { Props } from '../../renderer/Types';
// 判断是否是 check 类型
function isCheckType(dom: HTMLInputElement): boolean {
@ -91,3 +95,46 @@ export function updateInputHandlerIfChanged(dom) {
return false;
}
// export function controlInputValue(inputDom: Element, props: Props) {
// const { name, type } = props;
// // 如果是 radio找出同一form内name相同的Radio更新它们Handler的Value
// if (type === 'radio' && isNotNull(name)) {
// const radioList = document.querySelectorAll<HTMLInputElement>(`input[type="radio"][name="${name}"]`);
// for (let i = 0; i < radioList.length; i++) {
// const radio = radioList[i];
// if (radio === inputDom) {
// continue;
// }
// if (isNotNull(radio.form) && isNotNull(inputDom.form) && radio.form !== inputDom.form) {
// continue;
// }
// updateInputHandlerIfChanged(radio);
// }
// } else {
// updateInputValue(inputDom, props);
// }
// }
export function controlInputValue(inputDom: HTMLInputElement, props: Props) {
const { name, type } = props;
// 如果是 radio找出同一form内name相同的Radio更新它们Handler的Value
if (type === 'radio' && isNotNull(name)) {
const radioList = document.querySelectorAll<HTMLInputElement>(`input[type="radio"][name="${name}"]`);
for (let i = 0; i < radioList.length; i++) {
const radio = radioList[i];
if (radio === inputDom) {
continue;
}
if (isNotNull(radio.form) && isNotNull(inputDom.form) && radio.form !== inputDom.form) {
continue;
}
updateInputHandlerIfChanged(radio);
}
} else {
updateInputValue(inputDom, props);
}
}

View File

@ -17,12 +17,13 @@
*
*/
import { allDelegatedInulaEvents, portalDefaultDelegatedEvents, simulatedDelegatedEvents } from './EventHub';
import { isDocument } from '../dom/utils/Common';
import { EVENT_KEY, getNearestVNode, getNonDelegatedListenerMap } from '../dom/DOMInternalKeys';
import { EVENT_KEY, getNearestVNode, getNonDelegatedListenerMap } from '../renderer/utils/InternalKeys';
import { asyncUpdates, runDiscreteUpdates } from '../renderer/TreeBuilder';
import { handleEventMain } from './InulaEventMain';
import { decorateNativeEvent } from './EventWrapper';
import { VNode } from '../renderer/vnode/VNode';
import { ElementType } from '../renderer/Types';
import { InulaReconciler } from '../renderer';
// 触发委托事件
function triggerDelegatedEvent(
@ -41,15 +42,15 @@ function triggerDelegatedEvent(
}
// 监听委托事件
function listenToNativeEvent(nativeEvtName: string, delegatedElement: Element, isCapture: boolean) {
let dom: Element | Document = delegatedElement;
function listenToNativeEvent(nativeEvtName: string, delegatedElement: ElementType, isCapture: boolean) {
let element: ElementType = delegatedElement;
// document层次可能触发selectionchange事件为了捕获这类事件selectionchange事件绑定在document节点上
if (nativeEvtName === 'selectionchange' && !isDocument(delegatedElement)) {
dom = delegatedElement.ownerDocument;
if (nativeEvtName === 'selectionchange') {
element = delegatedElement.ownerDocument;
}
const listener = triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, dom);
dom.addEventListener(nativeEvtName, listener, isCapture);
const listener = triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, element);
InulaReconciler.hostConfig.addEventListener(element, nativeEvtName, listener, isCapture);
return listener;
}
@ -121,15 +122,15 @@ function getWrapperListener(inulaEventName, nativeEvtName, targetElement, listen
}
// 非委托事件单独监听到各自dom节点
export function listenNonDelegatedEvent(inulaEventName: string, domElement: Element, listener): void {
export function listenNonDelegatedEvent(inulaEventName: string, element: ElementType, listener): void {
const isCapture = isCaptureEvent(inulaEventName);
const nativeEvtName = getNativeEvtName(inulaEventName, isCapture);
// 先判断是否存在老的监听事件,若存在则移除
const nonDelegatedListenerMap = getNonDelegatedListenerMap(domElement);
const nonDelegatedListenerMap = getNonDelegatedListenerMap(element);
const currentListener = nonDelegatedListenerMap.get(inulaEventName);
if (currentListener) {
domElement.removeEventListener(nativeEvtName, currentListener);
InulaReconciler.hostConfig.removeEventListener(element, nativeEvtName, currentListener);
nonDelegatedListenerMap.delete(inulaEventName);
}
@ -138,8 +139,8 @@ export function listenNonDelegatedEvent(inulaEventName: string, domElement: Elem
}
// 为了和委托事件对外行为一致将事件对象封装成CustomBaseEvent
const wrapperListener = getWrapperListener(inulaEventName, nativeEvtName, domElement, listener);
const wrapperListener = getWrapperListener(inulaEventName, nativeEvtName, element, listener);
// 添加新的监听
nonDelegatedListenerMap.set(inulaEventName, wrapperListener);
domElement.addEventListener(nativeEvtName, wrapperListener, isCapture);
InulaReconciler.hostConfig.addEventListener(element, nativeEvtName, wrapperListener, isCapture);
}

View File

@ -13,12 +13,10 @@
* See the Mulan PSL v2 for more details.
*/
import { getVNodeProps } from '../dom/DOMInternalKeys';
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';
import { updateInputValue } from '../dom/valueHandler/InputValueHandler';
import { getVNodeProps } from '../renderer/utils/InternalKeys';
import { getTag } from '../renderer/utils/common';
import { InulaReconciler } from '../renderer';
import { ElementType } from '../renderer/Types';
// 记录表单控件 input/textarea/select的onChange事件的targets
let changeEventTargets: Array<any> | null = null;
@ -37,43 +35,12 @@ export function shouldControlValue(): boolean {
return changeEventTargets !== null && changeEventTargets.length > 0;
}
function controlInputValue(inputDom: HTMLInputElement, props: Props) {
const { name, type } = props;
// 如果是 radio找出同一form内name相同的Radio更新它们Handler的Value
if (type === 'radio' && isNotNull(name)) {
const radioList = document.querySelectorAll<HTMLInputElement>(`input[type="radio"][name="${name}"]`);
for (let i = 0; i < radioList.length; i++) {
const radio = radioList[i];
if (radio === inputDom) {
continue;
}
if (isNotNull(radio.form) && isNotNull(inputDom.form) && radio.form !== inputDom.form) {
continue;
}
updateInputHandlerIfChanged(radio);
}
} else {
updateInputValue(inputDom, props);
}
}
// 受控组件值重新赋值
function controlValue(target: Element) {
function controlValue(target: ElementType) {
const props = getVNodeProps(target);
if (props) {
const type = getDomTag(target);
switch (type) {
case 'input':
controlInputValue(<HTMLInputElement>target, props);
break;
case 'textarea':
updateTextareaValue(<HTMLTextAreaElement>target, props);
break;
default:
break;
}
const type = getTag(target);
InulaReconciler.hostConfig.handleControledInputElements(target, type ?? '', props);
}
}

View File

@ -15,7 +15,7 @@
import { AnyNativeEvent, ListenerUnitList } from './Types';
import type { VNode } from '../renderer/Types';
import { isInputElement, setPropertyWritable } from './utils';
import { setPropertyWritable } from './utils';
import { decorateNativeEvent } from './EventWrapper';
import { getListenersFromTree } from './ListenerGetter';
import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer';
@ -27,11 +27,11 @@ import {
inulaEventToNativeMap,
transformToInulaEvent,
} from './EventHub';
import { getDomTag } from '../dom/utils/Common';
import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler';
import { getDom } from '../dom/DOMInternalKeys';
import { getTag } from '../renderer/utils/common';
import { getElement } from '../renderer/utils/InternalKeys';
import { recordChangeEventTargets, shouldControlValue, tryControlValue } from './FormValueController';
import { getMouseEnterListeners } from './MouseEvent';
import { InulaReconciler } from '../renderer';
// web规范鼠标右键key值
const RIGHT_MOUSE_BUTTON = 2;
@ -42,22 +42,9 @@ const RIGHT_MOUSE_BUTTON = 2;
// | <select/> / <input type="file/> | change | NO |
// | <input type="checkbox" /> <input type="radio" /> | click | YES |
// | <input type="input /> / <input type="text" /> | input / change | YES |
function shouldTriggerChangeEvent(targetDom, evtName) {
const { type } = targetDom;
const domTag = getDomTag(targetDom);
if (domTag === 'select' || (domTag === 'input' && type === 'file')) {
return evtName === 'change';
} else if (domTag === 'input' && (type === 'checkbox' || type === 'radio')) {
if (evtName === 'click') {
return updateInputHandlerIfChanged(targetDom);
}
} else if (isInputElement(targetDom)) {
if (evtName === 'input' || evtName === 'change') {
return updateInputHandlerIfChanged(targetDom);
}
}
return false;
function shouldTriggerChangeEvent(targetElement, evtName) {
const elementTag = getTag(targetElement);
return InulaReconciler.hostConfig.shouldTriggerChangeEvent(targetElement, elementTag ?? '', evtName);
}
/**
@ -73,7 +60,7 @@ function getChangeListeners(
if (!vNode) {
return [];
}
const targetDom = getDom(vNode);
const targetDom = getElement(vNode);
// 判断是否需要触发change事件
if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) {

View File

@ -14,7 +14,7 @@
*/
import { VNode } from '../renderer/Types';
import { DomComponent } from '../renderer/vnode/VNodeTags';
import { Component } from '../renderer/vnode/VNodeTags';
import { WrappedEvent } from './EventWrapper';
import { InulaEventListener, ListenerUnitList } from './Types';
import { EVENT_TYPE_ALL, EVENT_TYPE_BUBBLE, EVENT_TYPE_CAPTURE } from './EventHub';
@ -55,7 +55,7 @@ export function getListenersFromTree(
// 从目标节点到根节点遍历获取listener
while (vNode !== null) {
const { realNode, tag } = vNode;
if (tag === DomComponent && realNode !== null) {
if (tag === Component && realNode !== null) {
if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_CAPTURE) {
const captureName = inulaEvtName + EVENT_TYPE_CAPTURE;
const captureListener = getListenerFromVNode(vNode, captureName);
@ -94,7 +94,7 @@ function getParent(inst: VNode | null): VNode | null {
}
do {
inst = inst.parent;
} while (inst && inst.tag !== DomComponent);
} while (inst && inst.tag !== Component);
return inst || null;
}
@ -123,7 +123,7 @@ function getMouseListenersFromTree(event: WrappedEvent, target: VNode, commonPar
break;
}
const { realNode, tag } = vNode;
if (tag === DomComponent && realNode !== null) {
if (tag === Component && realNode !== null) {
const currentTarget = realNode;
const listener = getListenerFromVNode(vNode, registrationName);
if (listener) {

View File

@ -13,11 +13,11 @@
* See the Mulan PSL v2 for more details.
*/
import { getNearestVNode, getVNode } from '../dom/DOMInternalKeys';
import { getNearestVNode, getVNode } from '../renderer/utils/InternalKeys';
import { WrappedEvent } from './EventWrapper';
import { VNode } from '../renderer/vnode/VNode';
import { AnyNativeEvent, ListenerUnitList } from './Types';
import { DomComponent, DomText } from '../renderer/vnode/VNodeTags';
import { Component, Text } from '../renderer/vnode/VNodeTags';
import { collectMouseListeners } from './ListenerGetter';
import { getNearestMountedVNode } from './utils';
@ -79,7 +79,7 @@ function getEndpointVNode(
toVNode = related ? getNearestVNode(related) : null;
if (toVNode !== null) {
const nearestMounted = getNearestMountedVNode(toVNode);
if (toVNode !== nearestMounted || (toVNode.tag !== DomComponent && toVNode.tag !== DomText)) {
if (toVNode !== nearestMounted || (toVNode.tag !== Component && toVNode.tag !== Text)) {
toVNode = null;
}
}

View File

@ -17,30 +17,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';
import {
Attributes,
ClassAttributes,
ClassType,
ClassicComponent,
ClassicComponentClass,
ComponentClass,
ComponentState,
FunctionComponentElement,
InulaCElement,
InulaElement,
InulaNode,
KVObject,
} from '../types';
import { Component } from '../renderer/components/BaseClassComponent';
import { DOMAttributes, HTMLAttributes, InputHTMLAttributes, SVGAttributes } from '../jsx-type/baseAttr';
import {
DOMElement,
DetailedInulaHTMLElement,
InulaHTML,
InulaHTMLElement,
InulaSVG,
InulaSVGElement,
} from '../jsx-type';
import { InulaElement, KVObject } from '../types';
/**
* vtype element
@ -132,82 +109,11 @@ function buildElement(isClone, type, setting, children) {
return JSXElement(element, key, ref, vNode, props, src);
}
export function createElement(
type: 'input',
props?: (InputHTMLAttributes<HTMLInputElement> & ClassAttributes<HTMLInputElement>) | null,
...children: InulaNode[]
): DetailedInulaHTMLElement<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
export function createElement<P extends HTMLAttributes<T>, T extends HTMLElement>(
type: keyof InulaHTML,
props?: (ClassAttributes<T> & P) | null,
...children: InulaNode[]
): DetailedInulaHTMLElement<P, T>;
export function createElement<P extends SVGAttributes<T>, T extends SVGElement>(
type: keyof InulaSVG,
props?: (ClassAttributes<T> & P) | null,
...children: InulaNode[]
): InulaSVGElement;
export function createElement<P extends DOMAttributes<T>, T extends Element>(
type: string,
props?: (ClassAttributes<T> & P) | null,
...children: InulaNode[]
): DOMElement<P, T>;
export function createElement<P extends KVObject>(
type: ClassType<P, ClassicComponent<P, ComponentState>, ClassicComponentClass<P>>,
props?: (ClassAttributes<ClassicComponent<P, ComponentState>> & P) | null,
...children: InulaNode[]
): InulaCElement<P, ClassicComponent<P, ComponentState>>;
export function createElement<P extends KVObject, T extends Component<P, ComponentState>, C extends ComponentClass<P>>(
type: ClassType<P, T, C>,
props?: (ClassAttributes<T> & P) | null,
...children: InulaNode[]
): InulaCElement<P, T>;
// 创建Element结构体供JSX编译时调用
export function createElement(type, setting, ...children) {
return buildElement(false, type, setting, children);
}
export function cloneElement<P extends HTMLAttributes<T>, T extends HTMLElement>(
element: DetailedInulaHTMLElement<P, T>,
props?: P,
...children: InulaNode[]
): DetailedInulaHTMLElement<P, T>;
export function cloneElement<P extends HTMLAttributes<T>, T extends HTMLElement>(
element: InulaHTMLElement<T>,
props?: P,
...children: InulaNode[]
): InulaHTMLElement<T>;
export function cloneElement<P extends SVGAttributes<T>, T extends SVGElement>(
element: InulaSVGElement,
props?: P,
...children: InulaNode[]
): InulaSVGElement;
export function cloneElement<P extends DOMAttributes<T>, T extends Element>(
element: DOMElement<P, T>,
props?: DOMAttributes<T> & P,
...children: InulaNode[]
): DOMElement<P, T>;
export function cloneElement<P>(
element: FunctionComponentElement<P>,
props?: Partial<P> & Attributes,
...children: InulaNode[]
): FunctionComponentElement<P>;
export function cloneElement<P, T extends Component<P, ComponentState>>(
element: InulaCElement<P, T>,
props?: Partial<P> & ClassAttributes<T>,
...children: InulaNode[]
): InulaCElement<P, T>;
export function cloneElement<P>(
element: InulaElement<P>,
props?: Partial<P> & Attributes,
...children: InulaNode[]
): InulaElement<P>;
export function cloneElement(element, setting, ...children) {
return buildElement(true, element, setting, children);
}

View File

@ -17,7 +17,7 @@ import { travelVNodeTree } from '../renderer/vnode/VNodeUtils';
import { Hook, Reducer, MutableRef, Effect, CallBack, Memo } from '../renderer/hooks/HookType';
import { VNode } from '../renderer/vnode/VNode';
import { launchUpdateFromVNode } from '../renderer/TreeBuilder';
import { DomComponent } from '../renderer/vnode/VNodeTags';
import { Component } from '../renderer/vnode/VNodeTags';
import { getElementTag } from '../renderer/vnode/VNodeCreator';
import { JSXElement } from '../renderer/Types';
import { EffectConstant } from '../renderer/hooks/EffectConstant';
@ -108,7 +108,7 @@ export const helper = {
travelVNodeTree(
vNode,
(node: VNode) => {
if (node.tag === DomComponent) {
if (node.tag === Component) {
// 找到组件的第一个dom元素返回它所在父节点的全部子节点
const dom = node.realNode;
info['Nodes'] = dom?.parentNode?.childNodes;

View File

@ -59,20 +59,23 @@ import { createStore, useStore, clearStore } from './inulax/store/StoreHandler';
import * as reduxAdapter from './inulax/adapters/redux';
import { watch } from './inulax/proxy/watch';
import { act } from './external/TestUtil';
import { defaultHostConfig } from './dom';
import {
render,
createPortal,
unstable_batchedUpdates,
findDOMNode,
findNode as findDOMNode,
unmountComponentAtNode,
createRoot,
} from './dom/DOMExternal';
} from './renderer/External';
import { syncUpdates as flushSync } from './renderer/TreeBuilder';
import { toRaw } from './inulax/proxy/ProxyHandler';
import { InulaReconciler } from './renderer';
const version = __VERSION__;
// 使用默认renderer
InulaReconciler.setHostConfig(defaultHostConfig);
const Inula = {
Children,
@ -185,3 +188,4 @@ export {
export * from './types';
export default Inula;
export { InulaReconciler };

View File

@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details.
*/
import { unstable_batchedUpdates } from '../../dom/DOMExternal';
import { unstable_batchedUpdates } from '../../renderer/External';
import { ReduxStoreHandler } from './redux';
type LinkListNode<T> = {
@ -161,7 +161,7 @@ function createSubscription(store: ReduxStoreHandler, parentSub: Subscription |
function trySubscribe() {
if (!unsubscribe) {
// 尝试订阅store的变化。如果已经存在一个订阅那么它会添加一个嵌套的订阅。否则它会直接订阅store。
// <EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD>store<EFBFBD>ı<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ѿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD>Ƕ<EFBFBD>׵Ķ<EFBFBD><EFBFBD>ġ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֱ<EFBFBD>Ӷ<EFBFBD><EFBFBD><EFBFBD>store<EFBFBD><EFBFBD>
unsubscribe = parentSub ? parentSub.addNestedSub(storeChangeHandler) : store.subscribe(storeChangeHandler);
listenerStore = getListenerManager();
}

File diff suppressed because it is too large Load Diff

View File

@ -1,119 +0,0 @@
export interface HTMLElement extends Element {}
export interface HTMLAnchorElement extends HTMLElement {}
export interface HTMLAreaElement extends HTMLElement {}
export interface HTMLAudioElement extends HTMLElement {}
export interface HTMLBaseElement extends HTMLElement {}
export interface HTMLBodyElement extends HTMLElement {}
export interface HTMLBRElement extends HTMLElement {}
export interface HTMLButtonElement extends HTMLElement {}
export interface HTMLCanvasElement extends HTMLElement {}
export interface HTMLDataElement extends HTMLElement {}
export interface HTMLDataListElement extends HTMLElement {}
export interface HTMLDetailsElement extends HTMLElement {}
export interface HTMLDialogElement extends HTMLElement {}
export interface HTMLDivElement extends HTMLElement {}
export interface HTMLDListElement extends HTMLElement {}
export interface HTMLEmbedElement extends HTMLElement {}
export interface HTMLFieldSetElement extends HTMLElement {}
export interface HTMLFormElement extends HTMLElement {}
export interface HTMLHeadingElement extends HTMLElement {}
export interface HTMLHeadElement extends HTMLElement {}
export interface HTMLHRElement extends HTMLElement {}
export interface HTMLHtmlElement extends HTMLElement {}
export interface HTMLIFrameElement extends HTMLElement {}
export interface HTMLImageElement extends HTMLElement {}
export interface HTMLInputElement extends HTMLElement {}
export interface HTMLModElement extends HTMLElement {}
export interface HTMLLabelElement extends HTMLElement {}
export interface HTMLLegendElement extends HTMLElement {}
export interface HTMLLIElement extends HTMLElement {}
export interface HTMLLinkElement extends HTMLElement {}
export interface HTMLMapElement extends HTMLElement {}
export interface HTMLMetaElement extends HTMLElement {}
export interface HTMLMeterElement extends HTMLElement {}
export interface HTMLObjectElement extends HTMLElement {}
export interface HTMLOListElement extends HTMLElement {}
export interface HTMLOptGroupElement extends HTMLElement {}
export interface HTMLOptionElement extends HTMLElement {}
export interface HTMLOutputElement extends HTMLElement {}
export interface HTMLParagraphElement extends HTMLElement {}
export interface HTMLParamElement extends HTMLElement {}
export interface HTMLPreElement extends HTMLElement {}
export interface HTMLProgressElement extends HTMLElement {}
export interface HTMLQuoteElement extends HTMLElement {}
export interface HTMLSlotElement extends HTMLElement {}
export interface HTMLScriptElement extends HTMLElement {}
export interface HTMLSelectElement extends HTMLElement {}
export interface HTMLSourceElement extends HTMLElement {}
export interface HTMLSpanElement extends HTMLElement {}
export interface HTMLStyleElement extends HTMLElement {}
export interface HTMLTableElement extends HTMLElement {}
export interface HTMLTableColElement extends HTMLElement {}
export interface HTMLTableDataCellElement extends HTMLElement {}
export interface HTMLTableHeaderCellElement extends HTMLElement {}
export interface HTMLTableRowElement extends HTMLElement {}
export interface HTMLTableSectionElement extends HTMLElement {}
export interface HTMLTemplateElement extends HTMLElement {}
export interface HTMLTextAreaElement extends HTMLElement {}
export interface HTMLTimeElement extends HTMLElement {}
export interface HTMLTitleElement extends HTMLElement {}
export interface HTMLTrackElement extends HTMLElement {}
export interface HTMLUListElement extends HTMLElement {}
export interface HTMLVideoElement extends HTMLElement {}
export interface HTMLWebViewElement extends HTMLElement {}
export interface SVGElement extends Element {}
export interface SVGSVGElement extends SVGElement {}
export interface SVGCircleElement extends SVGElement {}
export interface SVGClipPathElement extends SVGElement {}
export interface SVGDefsElement extends SVGElement {}
export interface SVGDescElement extends SVGElement {}
export interface SVGEllipseElement extends SVGElement {}
export interface SVGFEBlendElement extends SVGElement {}
export interface SVGFEColorMatrixElement extends SVGElement {}
export interface SVGFEComponentTransferElement extends SVGElement {}
export interface SVGFECompositeElement extends SVGElement {}
export interface SVGFEConvolveMatrixElement extends SVGElement {}
export interface SVGFEDiffuseLightingElement extends SVGElement {}
export interface SVGFEDisplacementMapElement extends SVGElement {}
export interface SVGFEDistantLightElement extends SVGElement {}
export interface SVGFEDropShadowElement extends SVGElement {}
export interface SVGFEFloodElement extends SVGElement {}
export interface SVGFEFuncAElement extends SVGElement {}
export interface SVGFEFuncBElement extends SVGElement {}
export interface SVGFEFuncGElement extends SVGElement {}
export interface SVGFEFuncRElement extends SVGElement {}
export interface SVGFEGaussianBlurElement extends SVGElement {}
export interface SVGFEImageElement extends SVGElement {}
export interface SVGFEMergeElement extends SVGElement {}
export interface SVGFEMergeNodeElement extends SVGElement {}
export interface SVGFEMorphologyElement extends SVGElement {}
export interface SVGFEOffsetElement extends SVGElement {}
export interface SVGFEPointLightElement extends SVGElement {}
export interface SVGFESpecularLightingElement extends SVGElement {}
export interface SVGFESpotLightElement extends SVGElement {}
export interface SVGFETileElement extends SVGElement {}
export interface SVGFETurbulenceElement extends SVGElement {}
export interface SVGFilterElement extends SVGElement {}
export interface SVGForeignObjectElement extends SVGElement {}
export interface SVGGElement extends SVGElement {}
export interface SVGImageElement extends SVGElement {}
export interface SVGLineElement extends SVGElement {}
export interface SVGLinearGradientElement extends SVGElement {}
export interface SVGMarkerElement extends SVGElement {}
export interface SVGMaskElement extends SVGElement {}
export interface SVGMetadataElement extends SVGElement {}
export interface SVGPathElement extends SVGElement {}
export interface SVGPatternElement extends SVGElement {}
export interface SVGPolygonElement extends SVGElement {}
export interface SVGPolylineElement extends SVGElement {}
export interface SVGRadialGradientElement extends SVGElement {}
export interface SVGRectElement extends SVGElement {}
export interface SVGStopElement extends SVGElement {}
export interface SVGSwitchElement extends SVGElement {}
export interface SVGSymbolElement extends SVGElement {}
export interface SVGTextElement extends SVGElement {}
export interface SVGTextPathElement extends SVGElement {}
export interface SVGTSpanElement extends SVGElement {}
export interface SVGUseElement extends SVGElement {}
export interface SVGViewElement extends SVGElement {}

View File

@ -1,448 +0,0 @@
import { ClassAttributes, InulaElement, InulaNode, LegacyRef } from '../types';
import {
AllHTMLAttributes,
AnchorHTMLAttributes,
AreaHTMLAttributes,
AudioHTMLAttributes,
BaseHTMLAttributes,
BlockquoteHTMLAttributes,
ButtonHTMLAttributes,
CanvasHTMLAttributes,
ColHTMLAttributes,
ColgroupHTMLAttributes,
DOMAttributes,
DataHTMLAttributes,
DelHTMLAttributes,
DetailsHTMLAttributes,
DialogHTMLAttributes,
EmbedHTMLAttributes,
FieldsetHTMLAttributes,
FormHTMLAttributes,
HTMLAttributes,
HtmlHTMLAttributes,
IframeHTMLAttributes,
ImgHTMLAttributes,
InputHTMLAttributes,
InsHTMLAttributes,
KeygenHTMLAttributes,
LabelHTMLAttributes,
LiHTMLAttributes,
LinkHTMLAttributes,
MapHTMLAttributes,
MenuHTMLAttributes,
MetaHTMLAttributes,
MeterHTMLAttributes,
ObjectHTMLAttributes,
OlHTMLAttributes,
OptgroupHTMLAttributes,
OptionHTMLAttributes,
OutputHTMLAttributes,
ParamHTMLAttributes,
ProgressHTMLAttributes,
QuoteHTMLAttributes,
SVGAttributes,
InulaSVGProps,
ScriptHTMLAttributes,
SelectHTMLAttributes,
SlotHTMLAttributes,
SourceHTMLAttributes,
StyleHTMLAttributes,
TableHTMLAttributes,
TdHTMLAttributes,
TextareaHTMLAttributes,
ThHTMLAttributes,
TimeHTMLAttributes,
TrackHTMLAttributes,
VideoHTMLAttributes,
WebViewHTMLAttributes,
} from './baseAttr';
import { HTMLWebViewElement } from './baseElement';
type DOMFactory<P extends DOMAttributes<T>, T extends Element> = (
props?: (ClassAttributes<T> & P) | null,
...children: InulaNode[]
) => DOMElement<P, T>;
interface InulaHTMLFactory<P extends HTMLAttributes<T>, T extends HTMLElement> extends DOMFactory<P, T> {
(props?: (ClassAttributes<T> & P) | null, ...children: InulaNode[]): DetailedInulaHTMLElement<P, T>;
}
export interface InulaHTMLElement<T extends HTMLElement> extends DetailedInulaHTMLElement<AllHTMLAttributes<T>, T> {}
export interface DOMElement<P extends HTMLAttributes<T> | SVGAttributes<T>, T extends Element>
extends InulaElement<P, string> {
ref: LegacyRef<T>;
}
export interface InulaSVGElement extends DOMElement<SVGAttributes<SVGElement>, SVGElement> {
type: keyof InulaSVG;
}
export interface InulaSVG {
animate: InulaSVGFactory;
circle: InulaSVGFactory;
clipPath: InulaSVGFactory;
defs: InulaSVGFactory;
desc: InulaSVGFactory;
ellipse: InulaSVGFactory;
feBlend: InulaSVGFactory;
feColorMatrix: InulaSVGFactory;
feComponentTransfer: InulaSVGFactory;
feComposite: InulaSVGFactory;
feConvolveMatrix: InulaSVGFactory;
feDiffuseLighting: InulaSVGFactory;
feDisplacementMap: InulaSVGFactory;
feDistantLight: InulaSVGFactory;
feDropShadow: InulaSVGFactory;
feFlood: InulaSVGFactory;
feFuncA: InulaSVGFactory;
feFuncB: InulaSVGFactory;
feFuncG: InulaSVGFactory;
feFuncR: InulaSVGFactory;
feImage: InulaSVGFactory;
feGaussianBlur: InulaSVGFactory;
feMerge: InulaSVGFactory;
feMergeNode: InulaSVGFactory;
feMorphology: InulaSVGFactory;
feOffset: InulaSVGFactory;
fePointLight: InulaSVGFactory;
feSpecularLighting: InulaSVGFactory;
feSpotLight: InulaSVGFactory;
feTile: InulaSVGFactory;
feTurbulence: InulaSVGFactory;
filter: InulaSVGFactory;
foreignObject: InulaSVGFactory;
g: InulaSVGFactory;
image: InulaSVGFactory;
line: InulaSVGFactory;
linearGradient: InulaSVGFactory;
marker: InulaSVGFactory;
mask: InulaSVGFactory;
view: InulaSVGFactory;
metadata: InulaSVGFactory;
path: InulaSVGFactory;
pattern: InulaSVGFactory;
polygon: InulaSVGFactory;
polyline: InulaSVGFactory;
radialGradient: InulaSVGFactory;
rect: InulaSVGFactory;
stop: InulaSVGFactory;
svg: InulaSVGFactory;
switch: InulaSVGFactory;
symbol: InulaSVGFactory;
text: InulaSVGFactory;
textPath: InulaSVGFactory;
tspan: InulaSVGFactory;
use: InulaSVGFactory;
}
interface InulaSVGFactory extends DOMFactory<SVGAttributes<SVGElement>, SVGElement> {
(props?: (ClassAttributes<SVGElement> & SVGAttributes<SVGElement>) | null, ...children: InulaNode[]): InulaSVGElement;
}
export interface DetailedInulaHTMLElement<P extends HTMLAttributes<T>, T extends HTMLElement> extends DOMElement<P, T> {
type: keyof BaseElement;
}
export interface InulaHTML {
a: InulaHTMLFactory<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
abbr: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
address: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
area: InulaHTMLFactory<AreaHTMLAttributes<HTMLAreaElement>, HTMLAreaElement>;
article: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
aside: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
audio: InulaHTMLFactory<AudioHTMLAttributes<HTMLAudioElement>, HTMLAudioElement>;
b: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
base: InulaHTMLFactory<BaseHTMLAttributes<HTMLBaseElement>, HTMLBaseElement>;
bdi: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
bdo: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
big: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
blockquote: InulaHTMLFactory<BlockquoteHTMLAttributes<HTMLQuoteElement>, HTMLQuoteElement>;
body: InulaHTMLFactory<HTMLAttributes<HTMLBodyElement>, HTMLBodyElement>;
br: InulaHTMLFactory<HTMLAttributes<HTMLBRElement>, HTMLBRElement>;
button: InulaHTMLFactory<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
canvas: InulaHTMLFactory<CanvasHTMLAttributes<HTMLCanvasElement>, HTMLCanvasElement>;
caption: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
cite: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
code: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
col: InulaHTMLFactory<ColHTMLAttributes<HTMLTableColElement>, HTMLTableColElement>;
colgroup: InulaHTMLFactory<ColgroupHTMLAttributes<HTMLTableColElement>, HTMLTableColElement>;
data: InulaHTMLFactory<DataHTMLAttributes<HTMLDataElement>, HTMLDataElement>;
datalist: InulaHTMLFactory<HTMLAttributes<HTMLDataListElement>, HTMLDataListElement>;
dd: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
del: InulaHTMLFactory<DelHTMLAttributes<HTMLModElement>, HTMLModElement>;
details: InulaHTMLFactory<DetailsHTMLAttributes<HTMLDetailsElement>, HTMLDetailsElement>;
dfn: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
dialog: InulaHTMLFactory<DialogHTMLAttributes<HTMLDialogElement>, HTMLDialogElement>;
div: InulaHTMLFactory<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
dl: InulaHTMLFactory<HTMLAttributes<HTMLDListElement>, HTMLDListElement>;
dt: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
em: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
embed: InulaHTMLFactory<EmbedHTMLAttributes<HTMLEmbedElement>, HTMLEmbedElement>;
fieldset: InulaHTMLFactory<FieldsetHTMLAttributes<HTMLFieldSetElement>, HTMLFieldSetElement>;
figcaption: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
figure: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
footer: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
form: InulaHTMLFactory<FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>;
h1: InulaHTMLFactory<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h2: InulaHTMLFactory<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h3: InulaHTMLFactory<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h4: InulaHTMLFactory<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h5: InulaHTMLFactory<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h6: InulaHTMLFactory<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
head: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLHeadElement>;
header: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
hgroup: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
hr: InulaHTMLFactory<HTMLAttributes<HTMLHRElement>, HTMLHRElement>;
html: InulaHTMLFactory<HtmlHTMLAttributes<HTMLHtmlElement>, HTMLHtmlElement>;
i: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
iframe: InulaHTMLFactory<IframeHTMLAttributes<HTMLIFrameElement>, HTMLIFrameElement>;
img: InulaHTMLFactory<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>;
input: InulaHTMLFactory<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
ins: InulaHTMLFactory<InsHTMLAttributes<HTMLModElement>, HTMLModElement>;
kbd: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
keygen: InulaHTMLFactory<KeygenHTMLAttributes<HTMLElement>, HTMLElement>;
label: InulaHTMLFactory<LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>;
legend: InulaHTMLFactory<HTMLAttributes<HTMLLegendElement>, HTMLLegendElement>;
li: InulaHTMLFactory<LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>;
link: InulaHTMLFactory<LinkHTMLAttributes<HTMLLinkElement>, HTMLLinkElement>;
main: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
map: InulaHTMLFactory<MapHTMLAttributes<HTMLMapElement>, HTMLMapElement>;
mark: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
menu: InulaHTMLFactory<MenuHTMLAttributes<HTMLElement>, HTMLElement>;
menuitem: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
meta: InulaHTMLFactory<MetaHTMLAttributes<HTMLMetaElement>, HTMLMetaElement>;
meter: InulaHTMLFactory<MeterHTMLAttributes<HTMLMeterElement>, HTMLMeterElement>;
nav: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
noscript: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
object: InulaHTMLFactory<ObjectHTMLAttributes<HTMLObjectElement>, HTMLObjectElement>;
ol: InulaHTMLFactory<OlHTMLAttributes<HTMLOListElement>, HTMLOListElement>;
optgroup: InulaHTMLFactory<OptgroupHTMLAttributes<HTMLOptGroupElement>, HTMLOptGroupElement>;
option: InulaHTMLFactory<OptionHTMLAttributes<HTMLOptionElement>, HTMLOptionElement>;
output: InulaHTMLFactory<OutputHTMLAttributes<HTMLOutputElement>, HTMLOutputElement>;
p: InulaHTMLFactory<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>;
param: InulaHTMLFactory<ParamHTMLAttributes<HTMLParamElement>, HTMLParamElement>;
picture: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
pre: InulaHTMLFactory<HTMLAttributes<HTMLPreElement>, HTMLPreElement>;
progress: InulaHTMLFactory<ProgressHTMLAttributes<HTMLProgressElement>, HTMLProgressElement>;
q: InulaHTMLFactory<QuoteHTMLAttributes<HTMLQuoteElement>, HTMLQuoteElement>;
rp: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
rt: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
ruby: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
s: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
samp: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
slot: InulaHTMLFactory<SlotHTMLAttributes<HTMLSlotElement>, HTMLSlotElement>;
script: InulaHTMLFactory<ScriptHTMLAttributes<HTMLScriptElement>, HTMLScriptElement>;
section: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
select: InulaHTMLFactory<SelectHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>;
small: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
source: InulaHTMLFactory<SourceHTMLAttributes<HTMLSourceElement>, HTMLSourceElement>;
span: InulaHTMLFactory<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
strong: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
style: InulaHTMLFactory<StyleHTMLAttributes<HTMLStyleElement>, HTMLStyleElement>;
sub: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
summary: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
sup: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
table: InulaHTMLFactory<TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>;
template: InulaHTMLFactory<HTMLAttributes<HTMLTemplateElement>, HTMLTemplateElement>;
tbody: InulaHTMLFactory<HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>;
td: InulaHTMLFactory<TdHTMLAttributes<HTMLTableDataCellElement>, HTMLTableDataCellElement>;
textarea: InulaHTMLFactory<TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>;
tfoot: InulaHTMLFactory<HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>;
th: InulaHTMLFactory<ThHTMLAttributes<HTMLTableHeaderCellElement>, HTMLTableHeaderCellElement>;
thead: InulaHTMLFactory<HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>;
time: InulaHTMLFactory<TimeHTMLAttributes<HTMLTimeElement>, HTMLTimeElement>;
title: InulaHTMLFactory<HTMLAttributes<HTMLTitleElement>, HTMLTitleElement>;
tr: InulaHTMLFactory<HTMLAttributes<HTMLTableRowElement>, HTMLTableRowElement>;
track: InulaHTMLFactory<TrackHTMLAttributes<HTMLTrackElement>, HTMLTrackElement>;
u: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
ul: InulaHTMLFactory<HTMLAttributes<HTMLUListElement>, HTMLUListElement>;
var: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
video: InulaHTMLFactory<VideoHTMLAttributes<HTMLVideoElement>, HTMLVideoElement>;
wbr: InulaHTMLFactory<HTMLAttributes<HTMLElement>, HTMLElement>;
webview: InulaHTMLFactory<WebViewHTMLAttributes<HTMLElement>, HTMLElement>;
}
export type InulaHTMLProps<E extends HTMLAttributes<T>, T> = ClassAttributes<T> & E;
export interface BaseElement {
// HTML
a: InulaHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
abbr: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
address: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
area: InulaHTMLProps<AreaHTMLAttributes<HTMLAreaElement>, HTMLAreaElement>;
article: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
aside: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
audio: InulaHTMLProps<AudioHTMLAttributes<HTMLAudioElement>, HTMLAudioElement>;
b: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
base: InulaHTMLProps<BaseHTMLAttributes<HTMLBaseElement>, HTMLBaseElement>;
bdi: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
bdo: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
big: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
blockquote: InulaHTMLProps<BlockquoteHTMLAttributes<HTMLQuoteElement>, HTMLQuoteElement>;
body: InulaHTMLProps<HTMLAttributes<HTMLBodyElement>, HTMLBodyElement>;
br: InulaHTMLProps<HTMLAttributes<HTMLBRElement>, HTMLBRElement>;
button: InulaHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
canvas: InulaHTMLProps<CanvasHTMLAttributes<HTMLCanvasElement>, HTMLCanvasElement>;
caption: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
cite: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
code: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
col: InulaHTMLProps<ColHTMLAttributes<HTMLTableColElement>, HTMLTableColElement>;
colgroup: InulaHTMLProps<ColgroupHTMLAttributes<HTMLTableColElement>, HTMLTableColElement>;
data: InulaHTMLProps<DataHTMLAttributes<HTMLDataElement>, HTMLDataElement>;
datalist: InulaHTMLProps<HTMLAttributes<HTMLDataListElement>, HTMLDataListElement>;
dd: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
del: InulaHTMLProps<DelHTMLAttributes<HTMLModElement>, HTMLModElement>;
details: InulaHTMLProps<DetailsHTMLAttributes<HTMLDetailsElement>, HTMLDetailsElement>;
dfn: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
dialog: InulaHTMLProps<DialogHTMLAttributes<HTMLDialogElement>, HTMLDialogElement>;
div: InulaHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
dl: InulaHTMLProps<HTMLAttributes<HTMLDListElement>, HTMLDListElement>;
dt: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
em: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
embed: InulaHTMLProps<EmbedHTMLAttributes<HTMLEmbedElement>, HTMLEmbedElement>;
fieldset: InulaHTMLProps<FieldsetHTMLAttributes<HTMLFieldSetElement>, HTMLFieldSetElement>;
figcaption: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
figure: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
footer: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
form: InulaHTMLProps<FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>;
h1: InulaHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h2: InulaHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h3: InulaHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h4: InulaHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h5: InulaHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h6: InulaHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
head: InulaHTMLProps<HTMLAttributes<HTMLHeadElement>, HTMLHeadElement>;
header: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
hgroup: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
hr: InulaHTMLProps<HTMLAttributes<HTMLHRElement>, HTMLHRElement>;
html: InulaHTMLProps<HtmlHTMLAttributes<HTMLHtmlElement>, HTMLHtmlElement>;
i: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
iframe: InulaHTMLProps<IframeHTMLAttributes<HTMLIFrameElement>, HTMLIFrameElement>;
img: InulaHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>;
input: InulaHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
ins: InulaHTMLProps<InsHTMLAttributes<HTMLModElement>, HTMLModElement>;
kbd: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
keygen: InulaHTMLProps<KeygenHTMLAttributes<HTMLElement>, HTMLElement>;
label: InulaHTMLProps<LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>;
legend: InulaHTMLProps<HTMLAttributes<HTMLLegendElement>, HTMLLegendElement>;
li: InulaHTMLProps<LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>;
link: InulaHTMLProps<LinkHTMLAttributes<HTMLLinkElement>, HTMLLinkElement>;
main: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
map: InulaHTMLProps<MapHTMLAttributes<HTMLMapElement>, HTMLMapElement>;
mark: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
menu: InulaHTMLProps<MenuHTMLAttributes<HTMLElement>, HTMLElement>;
menuitem: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
meta: InulaHTMLProps<MetaHTMLAttributes<HTMLMetaElement>, HTMLMetaElement>;
meter: InulaHTMLProps<MeterHTMLAttributes<HTMLMeterElement>, HTMLMeterElement>;
nav: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
noindex: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
noscript: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
object: InulaHTMLProps<ObjectHTMLAttributes<HTMLObjectElement>, HTMLObjectElement>;
ol: InulaHTMLProps<OlHTMLAttributes<HTMLOListElement>, HTMLOListElement>;
optgroup: InulaHTMLProps<OptgroupHTMLAttributes<HTMLOptGroupElement>, HTMLOptGroupElement>;
option: InulaHTMLProps<OptionHTMLAttributes<HTMLOptionElement>, HTMLOptionElement>;
output: InulaHTMLProps<OutputHTMLAttributes<HTMLOutputElement>, HTMLOutputElement>;
p: InulaHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>;
param: InulaHTMLProps<ParamHTMLAttributes<HTMLParamElement>, HTMLParamElement>;
picture: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
pre: InulaHTMLProps<HTMLAttributes<HTMLPreElement>, HTMLPreElement>;
progress: InulaHTMLProps<ProgressHTMLAttributes<HTMLProgressElement>, HTMLProgressElement>;
q: InulaHTMLProps<QuoteHTMLAttributes<HTMLQuoteElement>, HTMLQuoteElement>;
rp: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
rt: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
ruby: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
s: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
samp: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
slot: InulaHTMLProps<SlotHTMLAttributes<HTMLSlotElement>, HTMLSlotElement>;
script: InulaHTMLProps<ScriptHTMLAttributes<HTMLScriptElement>, HTMLScriptElement>;
section: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
select: InulaHTMLProps<SelectHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>;
small: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
source: InulaHTMLProps<SourceHTMLAttributes<HTMLSourceElement>, HTMLSourceElement>;
span: InulaHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
strong: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
style: InulaHTMLProps<StyleHTMLAttributes<HTMLStyleElement>, HTMLStyleElement>;
sub: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
summary: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
sup: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
table: InulaHTMLProps<TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>;
template: InulaHTMLProps<HTMLAttributes<HTMLTemplateElement>, HTMLTemplateElement>;
tbody: InulaHTMLProps<HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>;
td: InulaHTMLProps<TdHTMLAttributes<HTMLTableDataCellElement>, HTMLTableDataCellElement>;
textarea: InulaHTMLProps<TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>;
tfoot: InulaHTMLProps<HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>;
th: InulaHTMLProps<ThHTMLAttributes<HTMLTableHeaderCellElement>, HTMLTableHeaderCellElement>;
thead: InulaHTMLProps<HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>;
time: InulaHTMLProps<TimeHTMLAttributes<HTMLTimeElement>, HTMLTimeElement>;
title: InulaHTMLProps<HTMLAttributes<HTMLTitleElement>, HTMLTitleElement>;
tr: InulaHTMLProps<HTMLAttributes<HTMLTableRowElement>, HTMLTableRowElement>;
track: InulaHTMLProps<TrackHTMLAttributes<HTMLTrackElement>, HTMLTrackElement>;
u: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
ul: InulaHTMLProps<HTMLAttributes<HTMLUListElement>, HTMLUListElement>;
var: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
video: InulaHTMLProps<VideoHTMLAttributes<HTMLVideoElement>, HTMLVideoElement>;
wbr: InulaHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
webview: InulaHTMLProps<WebViewHTMLAttributes<HTMLWebViewElement>, HTMLWebViewElement>;
// SVG
svg: InulaSVGProps<SVGSVGElement>;
animate: InulaSVGProps<SVGElement>;
animateMotion: InulaSVGProps<SVGElement>;
animateTransform: InulaSVGProps<SVGElement>;
circle: InulaSVGProps<SVGCircleElement>;
clipPath: InulaSVGProps<SVGClipPathElement>;
defs: InulaSVGProps<SVGDefsElement>;
desc: InulaSVGProps<SVGDescElement>;
ellipse: InulaSVGProps<SVGEllipseElement>;
feBlend: InulaSVGProps<SVGFEBlendElement>;
feColorMatrix: InulaSVGProps<SVGFEColorMatrixElement>;
feComponentTransfer: InulaSVGProps<SVGFEComponentTransferElement>;
feComposite: InulaSVGProps<SVGFECompositeElement>;
feConvolveMatrix: InulaSVGProps<SVGFEConvolveMatrixElement>;
feDiffuseLighting: InulaSVGProps<SVGFEDiffuseLightingElement>;
feDisplacementMap: InulaSVGProps<SVGFEDisplacementMapElement>;
feDistantLight: InulaSVGProps<SVGFEDistantLightElement>;
feDropShadow: InulaSVGProps<SVGFEDropShadowElement>;
feFlood: InulaSVGProps<SVGFEFloodElement>;
feFuncA: InulaSVGProps<SVGFEFuncAElement>;
feFuncB: InulaSVGProps<SVGFEFuncBElement>;
feFuncG: InulaSVGProps<SVGFEFuncGElement>;
feFuncR: InulaSVGProps<SVGFEFuncRElement>;
feGaussianBlur: InulaSVGProps<SVGFEGaussianBlurElement>;
feImage: InulaSVGProps<SVGFEImageElement>;
feMerge: InulaSVGProps<SVGFEMergeElement>;
feMergeNode: InulaSVGProps<SVGFEMergeNodeElement>;
feMorphology: InulaSVGProps<SVGFEMorphologyElement>;
feOffset: InulaSVGProps<SVGFEOffsetElement>;
fePointLight: InulaSVGProps<SVGFEPointLightElement>;
feSpecularLighting: InulaSVGProps<SVGFESpecularLightingElement>;
feSpotLight: InulaSVGProps<SVGFESpotLightElement>;
feTile: InulaSVGProps<SVGFETileElement>;
feTurbulence: InulaSVGProps<SVGFETurbulenceElement>;
filter: InulaSVGProps<SVGFilterElement>;
foreignObject: InulaSVGProps<SVGForeignObjectElement>;
g: InulaSVGProps<SVGGElement>;
image: InulaSVGProps<SVGImageElement>;
line: InulaSVGProps<SVGLineElement>;
linearGradient: InulaSVGProps<SVGLinearGradientElement>;
marker: InulaSVGProps<SVGMarkerElement>;
mask: InulaSVGProps<SVGMaskElement>;
metadata: InulaSVGProps<SVGMetadataElement>;
mpath: InulaSVGProps<SVGElement>;
path: InulaSVGProps<SVGPathElement>;
pattern: InulaSVGProps<SVGPatternElement>;
polygon: InulaSVGProps<SVGPolygonElement>;
polyline: InulaSVGProps<SVGPolylineElement>;
radialGradient: InulaSVGProps<SVGRadialGradientElement>;
rect: InulaSVGProps<SVGRectElement>;
stop: InulaSVGProps<SVGStopElement>;
switch: InulaSVGProps<SVGSwitchElement>;
symbol: InulaSVGProps<SVGSymbolElement>;
text: InulaSVGProps<SVGTextElement>;
textPath: InulaSVGProps<SVGTextPathElement>;
tspan: InulaSVGProps<SVGTSpanElement>;
use: InulaSVGProps<SVGUseElement>;
view: InulaSVGProps<SVGViewElement>;
}

View File

@ -19,7 +19,7 @@
*/
import type { VNode, ContextType } from './Types';
import type { Container } from '../dom/DOMOperator';
import { Container } from './Types';
import { getNSCtx } from '../dom/DOMOperator';

View File

@ -13,15 +13,17 @@
* See the Mulan PSL v2 for more details.
*/
import { asyncUpdates, getFirstCustomDom, syncUpdates, startUpdate, createTreeRootVNode } from '../renderer/Renderer';
import { createPortal } from '../renderer/components/CreatePortal';
import type { Container } from './DOMOperator';
import { isElement } from './utils/Common';
import { findDOMByClassInst } from '../renderer/vnode/VNodeUtils';
/* eslint-disable @typescript-eslint/no-unused-vars */
import { asyncUpdates, getFirstCustomElement, syncUpdates, startUpdate, createTreeRootVNode } from './Renderer';
import { createPortal } from './components/CreatePortal';
import type { Container, ElementType } from './Types';
import { isElement } from './utils/common';
import { findElementByClassInst } from './utils/InternalKeys';
import { listenSimulatedDelegatedEvents } from '../event/EventBinding';
import { Callback } from '../renderer/Types';
import { Callback } from './Types';
import { InulaNode } from '../types';
import { EVENT_KEY } from './DOMInternalKeys';
import { EVENT_KEY } from './utils/InternalKeys';
import { InulaReconciler } from '.';
function createRoot(children: any, container: Container, callback?: Callback) {
// 清空容器
@ -40,7 +42,7 @@ function createRoot(children: any, container: Container, callback?: Callback) {
if (typeof callback === 'function') {
const cb = callback;
callback = function () {
const instance = getFirstCustomDom(treeRoot);
const instance = getFirstCustomElement(treeRoot);
cb.call(instance);
};
}
@ -63,7 +65,7 @@ function executeRender(children: any, container: Container, callback?: Callback)
if (typeof callback === 'function') {
const cb = callback;
callback = function () {
const instance = getFirstCustomDom(treeRoot);
const instance = getFirstCustomElement(treeRoot);
cb.call(instance);
};
}
@ -71,21 +73,21 @@ function executeRender(children: any, container: Container, callback?: Callback)
startUpdate(children, treeRoot, callback);
}
return getFirstCustomDom(treeRoot);
return getFirstCustomElement(treeRoot);
}
function findDOMNode(domOrEle?: Element): null | Element | Text {
if (domOrEle === null || domOrEle === undefined) {
function findNode(Ele?: Element): null | Element | Text {
if (Ele === null || Ele === undefined) {
return null;
}
// 普通节点
if (isElement(domOrEle)) {
return domOrEle;
if (isElement(Ele as ElementType)) {
return Ele;
}
// class的实例
return findDOMByClassInst(domOrEle);
return findElementByClassInst(Ele);
}
// 情况根节点监听器
@ -96,7 +98,7 @@ function removeRootEventLister(container: Container) {
const listener = events[event];
if (listener) {
container.removeEventListener(event, listener);
InulaReconciler.hostConfig.removeEventListener(container, event, listener);
events[event] = null;
}
});
@ -140,7 +142,7 @@ function createRootElement(container: Container, option?: Record<string, any>):
export {
createPortal,
asyncUpdates as unstable_batchedUpdates,
findDOMNode,
findNode,
executeRender as render,
createRootElement as createRoot,
destroy as unmountComponentAtNode,

View File

@ -37,7 +37,7 @@ export function startUpdate(element: any, treeRoot: VNode, callback?: Callback)
launchUpdateFromVNode(treeRoot);
}
export function getFirstCustomDom(treeRoot?: VNode | null): Element | Text | null {
export function getFirstCustomElement(treeRoot?: VNode | null): Element | Text | null {
if (treeRoot?.child) {
return treeRoot.child.realNode;
}

View File

@ -17,7 +17,7 @@ import type { VNode } from './Types';
import { callRenderQueueImmediate, pushRenderCallback } from './taskExecutor/RenderQueue';
import { updateVNode } from './vnode/VNodeCreator';
import { ContextProvider, DomComponent, DomPortal, TreeRoot } from './vnode/VNodeTags';
import { ContextProvider, Component, Portal, TreeRoot } from './vnode/VNodeTags';
import { FlagUtils, InitFlag, Interrupted } from './vnode/VNodeFlags';
import { captureVNode } from './render/BaseComponent';
import { checkLoopingUpdateLimit, submitToRender } from './submit/Submit';
@ -250,7 +250,7 @@ function recoverTreeContext(vNode: VNode) {
while (parent !== null) {
if (parent.tag === ContextProvider) {
contextProviders.unshift(parent);
} else if (parent.tag === DomPortal) {
} else if (parent.tag === Portal) {
portalRoots.unshift(parent);
}
parent = parent.parent;
@ -271,7 +271,7 @@ function resetTreeContext(vNode: VNode) {
if (parent.tag === ContextProvider) {
resetContext(parent);
}
if (parent.tag === DomPortal) {
if (parent.tag === Portal) {
popCurrentRoot();
}
parent = parent.parent;
@ -297,9 +297,9 @@ function buildVNodeTree(treeRoot: VNode) {
let parent = startVNode.parent;
while (parent !== null) {
const tag = parent.tag;
if (tag === DomComponent) {
if (tag === Component) {
break;
} else if (tag === TreeRoot || tag === DomPortal) {
} else if (tag === TreeRoot || tag === Portal) {
break;
}
parent = parent.parent;

View File

@ -13,10 +13,20 @@
* See the Mulan PSL v2 for more details.
*/
import { BELONG_CLASS_VNODE_KEY } from './vnode/VNode';
import { BELONG_CLASS_VNODE_KEY, VNode } from './vnode/VNode';
import { Component, Text } from './vnode/VNodeTags';
export { VNode } from './vnode/VNode';
// import { Props } from './utils/InternalKeys';
export type Props = Record<string, any> & {
autoFocus?: boolean;
children?: any;
disabled?: boolean;
hidden?: boolean;
style?: { display?: string };
};
/* eslint-disable @typescript-eslint/no-unused-vars */
type Trigger<A> = (A) => void;
export type UseStateHookType = {
@ -82,3 +92,75 @@ export type Source = {
};
export type Callback = () => void;
export type Container = any & { _treeRoot?: VNode | null };
export interface ReconcilerType {
render: (...args: any) => void;
}
export type ElementType = {
parentNode?: ElementType | null;
nodeName?: string;
nodeType?: number;
[key: string]: any;
};
export type CommonTagType = typeof Component | typeof Text;
export enum CommonTags {
ComponentElement = Component,
TextElement = Text,
}
// `hostConfig` 接口定义了与 DOM 操作相关的方法
export interface HostConfigType {
// 定义元素的name和type值
elementConfig: {
common: ElementType;
text: ElementType;
input: ElementType;
button: ElementType;
select: ElementType;
textarea: ElementType;
[key: string]: ElementType;
};
// 节点操作
createElement(tagName: string, props: Props, parentNamespace: string, rootElement: ElementType): ElementType;
createText(text: string): ElementType;
isTextChild(type: string, props: Props): boolean;
hideElement(tag: CommonTagType, element: ElementType): void;
unHideElement(tag: CommonTagType, element: ElementType, props?: Props): void;
removeChildElement(parent: ElementType | Container, child: ElementType): void;
insertElementBefore(parent: ElementType | Container, child: ElementType, beforeChild: ElementType): void;
appendChildElement(parent: ElementType | Container, child: ElementType): void;
clearText(element: ElementType): void;
// 监听器相关
addEventListener(element: ElementType, eventName: string, handler: (...args) => void, isCapture: boolean): void;
removeEventListener(element: ElementType, eventName: string, handler: (...args) => void): void;
// 生命周期相关
prepareForSubmit(): void;
resetAfterSubmit(): void;
/* submit 前对元素预处理,常用于 input 类元素将value值去掉避免重复刷新 */
onSubmit(tag: CommonTagType, type: any, element: ElementType, newProps: any, changeList: any): void;
// input 相关
updateInputValue(type: string, element: ElementType, props: Props): void;
shouldTriggerChangeEvent(targetElement: ElementType, elementTag: string, evtName: string): boolean;
/* 受控 input 传值处理 */
handleControledInputElements(target: ElementType, type: string, props: Props): void;
// prop 相关
/* 如何更新 props */
setProps(element: ElementType, propName: string, value: any, isNativeTag: boolean, isInit: boolean): void;
/* 如何处理 props */
getProps(
type: string,
element: ElementType,
rawProps: Record<string, any>,
): Record<string, any>;
onPropInit(element: ElementType, tagName: string, Props: Record<string, any>): void;
}
export interface InulaReconcilerType {
hostConfig: HostConfigType;
setHostConfig(config: Partial<HostConfigType>): void;
}

View File

@ -17,7 +17,7 @@ import type { ContextType } from '../../Types';
import { TYPE_PROVIDER, TYPE_CONTEXT } from '../../../external/JSXElementType';
import { Context } from '../../../types';
export function createContext<T>(val: T): Context<T>;
// export function createContext<T>(val: T): Context<T>;
export function createContext<T>(val: T): ContextType<T> | Context<T> {
const context: ContextType<T> = {
vtype: TYPE_CONTEXT,

View File

@ -16,7 +16,7 @@
import type { VNode } from '../Types';
import { FlagUtils } from '../vnode/VNodeFlags';
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL, TYPE_STRICT_MODE } from '../../external/JSXElementType';
import { DomText, DomPortal, Fragment, DomComponent } from '../vnode/VNodeTags';
import { Text, Portal, Fragment, Component } from '../vnode/VNodeTags';
import {
updateVNode,
createVNodeFromElement,
@ -137,7 +137,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
let resultNode: VNode | null = null;
switch (newNodeType) {
case DiffCategory.TEXT_NODE: {
if (oldNode === null || oldNode.tag !== DomText) {
if (oldNode === null || oldNode.tag !== Text) {
resultNode = createDomTextVNode(String(newChild));
} else {
resultNode = updateVNode(oldNode, String(newChild));
@ -175,7 +175,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
}
break;
} else if (newChild.vtype === TYPE_PORTAL) {
if (oldNode === null || oldNode.tag !== DomPortal || oldNode.realNode !== newChild.realNode) {
if (oldNode === null || oldNode.tag !== Portal || oldNode.realNode !== newChild.realNode) {
resultNode = createPortalVNode(newChild);
} else {
resultNode = updateVNode(oldNode, newChild.children || []);
@ -364,7 +364,7 @@ function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newC
* PP从DOM树中删除P节点添加回DOM树中
* select时option子节点时会导致原父节点child变化deleteVNodes
*/
if (firstChild && parentNode.tag === DomComponent && parentNode.type !== 'select' && newChildren.length === 0) {
if (firstChild && parentNode.tag === Component && parentNode.type !== 'select' && newChildren.length === 0) {
FlagUtils.markClear(parentNode);
parentNode.clearChild = firstChild;
} else {
@ -385,13 +385,13 @@ function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newC
// 是否可以扩大至非dom类型节点待确认
// 如果dom节点在上次添加前没有节点说明本次添加时可以直接添加到最后不需要通过 getSiblingDom 函数找到 before 节点
if (
parentNode.tag === DomComponent &&
parentNode.tag === Component &&
parentNode.oldProps?.children?.length === 0 &&
rightIdx - leftIdx === newChildren.length
) {
isDirectAdd = true;
}
const isAddition = parentNode.tag === DomPortal || !parentNode.isCreated;
const isAddition = parentNode.tag === Portal || !parentNode.isCreated;
for (; leftIdx < rightIdx; leftIdx++) {
newNode = getNewNode(parentNode, newChildren[leftIdx], null);
@ -525,7 +525,7 @@ function diffStringNodeHandler(parentNode: VNode, newChild: any, firstChildVNode
let newTextNode: VNode | null = null;
// 第一个vNode是Text则复用
if (firstChildVNode !== null && firstChildVNode.tag === DomText) {
if (firstChildVNode !== null && firstChildVNode.tag === Text) {
newTextNode = updateVNode(firstChildVNode, String(newChild));
deleteVNodes(parentNode, firstChildVNode.next);
newTextNode.next = null;
@ -598,7 +598,7 @@ function diffObjectNodeHandler(
} else if (newChild.vtype === TYPE_PORTAL) {
if (canReuseNode) {
// 可以复用
if (canReuseNode.tag === DomPortal && canReuseNode.realNode === newChild.realNode) {
if (canReuseNode.tag === Portal && canReuseNode.realNode === newChild.realNode) {
resultNode = updateVNode(canReuseNode, newChild.children || []);
startDelVNode = canReuseNode.next;
resultNode.next = null;

View File

@ -17,7 +17,7 @@ import { useLayoutEffectImpl } from './UseEffectHook';
import { getHookStage } from './HookStage';
import { throwNotInFuncError } from './BaseHook';
import type { MutableRef } from './HookType';
import { isNotNull } from '../../dom/utils/Common';
import { isNotNull } from '../../renderer/utils/common';
function effectFunc<R>(func: () => R, ref: MutableRef<R> | ((any) => any) | null): (() => void) | void {
if (typeof ref === 'function') {

View File

@ -0,0 +1,9 @@
import { defaultHostConfig } from '../dom';
import { HostConfigType, InulaReconcilerType } from './Types';
export const InulaReconciler: InulaReconcilerType = {
hostConfig: defaultHostConfig as HostConfigType,
setHostConfig(config: HostConfigType) {
this.hostConfig = { ...this.hostConfig, ...config };
},
};

View File

@ -1,27 +1,28 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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 { isEventProp } from './ValidateProps';
import { getCurrentRoot } from '../RootStack';
import { allDelegatedInulaEvents } from '../../event/EventHub';
import { updateCommonProp } from './UpdateCommonProp';
import { setStyles } from './StyleHandler';
import { lazyDelegateOnRoot, listenNonDelegatedEvent } from '../../event/EventBinding';
import { isEventProp } from '../validators/ValidateProps';
import { getCurrentRoot } from '../../renderer/RootStack';
import { InulaReconciler } from '../../renderer';
import { ElementType } from '../../renderer/Types';
import { Props } from '../utils/InternalKeys';
import { shouldAutoFocus } from '../utils/common';
import { validateProps } from './ValidateProps';
export function initElementProps(element: Element, tagName: string, rawProps: Props): boolean {
validateProps(tagName, rawProps);
// 获取不包括valuedefaultValue的属性
const props: Record<string, any> = InulaReconciler.hostConfig.getProps(tagName, element, rawProps);
// 初始化DOM属性不包括valuedefaultValue
const isNativeTag = isNativeElement(tagName, props);
setElementProps(element as ElementType, props, isNativeTag, true);
InulaReconciler.hostConfig.onPropInit(element, tagName, rawProps)
return shouldAutoFocus(tagName, rawProps);
}
// 初始化DOM属性和更新 DOM 属性
export function setDomProps(dom: Element, props: Record<string, any>, isNativeTag: boolean, isInit: boolean): void {
export function setElementProps(element: ElementType, props: Record<string, any>, isNativeTag: boolean, isInit: boolean): void {
const keysOfProps = Object.keys(props);
let propName;
let propVal;
@ -29,31 +30,23 @@ export function setDomProps(dom: Element, props: Record<string, any>, isNativeTa
for (let i = 0; i < keyLength; i++) {
propName = keysOfProps[i];
propVal = props[propName];
if (propName === 'style') {
setStyles(dom, propVal);
} else if (isEventProp(propName)) {
if (isEventProp(propName)) {
// 事件监听属性处理
const currentRoot = getCurrentRoot();
if (!allDelegatedInulaEvents.has(propName)) {
listenNonDelegatedEvent(propName, dom, propVal);
listenNonDelegatedEvent(propName, element, propVal);
} else if (currentRoot && !currentRoot.delegatedEvents.has(propName)) {
lazyDelegateOnRoot(currentRoot, propName);
}
} else if (propName === 'children') {
// 只处理纯文本子节点其他children在VNode树中处理
const type = typeof propVal;
if (type === 'string' || type === 'number') {
dom.textContent = propVal;
}
} else if (propName === 'dangerouslySetInnerHTML') {
dom.innerHTML = propVal.__html;
} else if (!isInit || (propVal !== null && propVal !== undefined)) {
updateCommonProp(dom, propName, propVal, isNativeTag);
continue;
}
InulaReconciler.hostConfig.setProps(element, propName, propVal, isNativeTag, isInit);
}
}
// 是内置元素
export function isNativeElement(tagName: string, props: Record<string, any>) {
return !tagName.includes('-') && props.is === undefined;
}
// 找出两个 DOM 属性的差别,生成需要更新的属性集合
export function compareProps(oldProps: Record<string, any>, newProps: Record<string, any>): Record<string, any> {
let updatesForStyle = {};

View File

@ -14,7 +14,7 @@
*/
import { getPropDetails, PropDetails, PROPERTY_TYPE } from './PropertiesData';
import { isNativeElement } from './PropHandler';
const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
const voidTagElements = [
@ -36,11 +36,6 @@ const voidTagElements = [
'menuitem',
];
// 是内置元素
export function isNativeElement(tagName: string, props: Record<string, any>) {
return !tagName.includes('-') && props.is === undefined;
}
function isInvalidBoolean(attributeName: string, value: any, propDetails: PropDetails): boolean {
if (propDetails.type === PROPERTY_TYPE.SPECIAL) {
return false;

View File

@ -15,7 +15,7 @@
import type { VNode } from '../Types';
import { ContextProvider, DomComponent, DomPortal, TreeRoot, SuspenseComponent } from '../vnode/VNodeTags';
import { ContextProvider, Component, Portal, TreeRoot, SuspenseComponent } from '../vnode/VNodeTags';
import { setContext, setNamespaceCtx } from '../ContextSaver';
import { FlagUtils } from '../vnode/VNodeFlags';
import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator';
@ -30,10 +30,10 @@ function setTreeContextValue(processing: VNode) {
case TreeRoot:
setNamespaceCtx(processing, processing.realNode);
break;
case DomComponent:
case Component:
setNamespaceCtx(processing);
break;
case DomPortal:
case Portal:
setNamespaceCtx(processing, processing.realNode);
pushCurrentRoot(processing);
break;

View File

@ -13,18 +13,36 @@
* See the Mulan PSL v2 for more details.
*/
import type { VNode } from '../Types';
import type { Props } from '../../dom/DOMOperator';
import type { ElementType, VNode } from '../Types';
import { Props } from '../Types';
import { getNamespaceCtx, setNamespaceCtx, resetNamespaceCtx } from '../ContextSaver';
import { appendChildElement, newDom, initDomProps, getPropChangeList, isTextChild } from '../../dom/DOMOperator';
import { saveVNode, updateVNodeProps } from '../../renderer/utils/InternalKeys';
import { FlagUtils } from '../vnode/VNodeFlags';
import { markRef } from './BaseComponent';
import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags';
import { Component, Portal, Text } from '../vnode/VNodeTags';
import { travelVNodeTree } from '../vnode/VNodeUtils';
import { createChildrenByDiff } from '../diff/nodeDiffComparator';
import { InulaReconciler } from '..';
import { getCurrentRoot } from '../RootStack';
import { compareProps } from '../props/PropHandler';
import { validateProps } from '../props/ValidateProps';
import { initElementProps } from '../props/PropHandler';
// 准备更新之前进行一系列校验,寻找属性差异等准备工作
export function getPropChangeList(
element: ElementType,
type: string,
lastRawProps: Props,
nextRawProps: Props
): Record<string, any> {
// 校验两个对象的不同
validateProps(type, nextRawProps);
function updateDom(processing: VNode, type: any, newProps: Props) {
// 重新定义的属性不需要参与对比被代理的组件需要把这些属性覆盖到props中
const oldProps = InulaReconciler.hostConfig.getProps(type, element, lastRawProps);
const newProps = InulaReconciler.hostConfig.getProps(type, element, nextRawProps);
return compareProps(oldProps, newProps);
}
function updateElement(processing: VNode, type: any, newProps: Props) {
// 如果oldProps !== newProps意味着存在更新并且需要处理其相关的副作用
const oldProps = processing.oldProps;
if (oldProps === newProps) {
@ -32,9 +50,9 @@ function updateDom(processing: VNode, type: any, newProps: Props) {
return;
}
const dom: Element = processing.realNode;
const element: ElementType = processing.realNode;
const changeList = getPropChangeList(dom, type, oldProps, newProps);
const changeList = getPropChangeList(element, type, oldProps, newProps);
// 输入类型的直接标记更新
if (type === 'input' || type === 'textarea' || type === 'select' || type === 'option') {
@ -51,12 +69,12 @@ function updateDom(processing: VNode, type: any, newProps: Props) {
export function bubbleRender(processing: VNode) {
resetNamespaceCtx(processing);
const {createElement} = InulaReconciler.hostConfig;
const type = processing.type;
const newProps = processing.props;
if (!processing.isCreated && processing.realNode !== null) {
// 更新dom属性
updateDom(processing, type, newProps);
updateElement(processing, type, newProps);
if (processing.oldRef !== processing.ref) {
FlagUtils.markRef(processing);
@ -65,30 +83,34 @@ export function bubbleRender(processing: VNode) {
const parentNamespace = getNamespaceCtx();
// 创建dom
const dom = newDom(type, newProps, parentNamespace, processing);
// 把dom类型的子节点append到parent dom中
const rootElement = getCurrentRoot()?.realNode;
const {element, props} = createElement(type, newProps, parentNamespace, rootElement);
// 将 vNode 节点挂到 element 对象上
saveVNode(processing, element);
// 将属性挂到 element 对象上
updateVNodeProps(element, props);
// 把dom类型的子节点append到parent element中
const vNode = processing.child;
if (vNode !== null) {
// 向下递归它的子节点,查找所有终端节点。
travelVNodeTree(
vNode,
node => {
if (node.tag === DomComponent || node.tag === DomText) {
appendChildElement(dom, node.realNode);
if (node.tag === Component || node.tag === Text) {
InulaReconciler.hostConfig.appendChildElement(element, node.realNode);
}
},
node =>
// 已经append到父节点或者是DomPortal都不需要处理child了
node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal,
// 已经append到父节点或者是Portal都不需要处理child了
node.tag === Component || node.tag === Text || node.tag === Portal,
processing,
null
);
}
processing.realNode = dom;
processing.realNode = element;
if (initDomProps(dom, type, newProps)) {
if (initElementProps(element, type, newProps)) {
FlagUtils.markUpdate(processing);
}
@ -107,12 +129,12 @@ export function captureRender(processing: VNode): VNode | null {
const oldProps = !processing.isCreated ? processing.oldProps : null;
let nextChildren = newProps.children;
const isDirectTextChild = isTextChild(type, newProps);
const isDirectTextChild = InulaReconciler.hostConfig.isTextChild(type, newProps);
if (isDirectTextChild) {
// 如果为文本节点,则认为没有子节点
nextChildren = null;
} else if (oldProps !== null && isTextChild(type, oldProps)) {
} else if (oldProps !== null && InulaReconciler.hostConfig.isTextChild(type, oldProps)) {
// 将纯文本的子节点改为vNode节点
FlagUtils.markContentReset(processing);
}

View File

@ -14,10 +14,10 @@
*/
import type { VNode } from '../Types';
import { saveVNode } from '../utils/InternalKeys';
import { throwIfTrue } from '../utils/throwIfTrue';
import { newTextDom } from '../../dom/DOMOperator';
import { FlagUtils } from '../vnode/VNodeFlags';
import { InulaReconciler } from '..';
export function captureRender(): VNode | null {
return null;
@ -44,6 +44,8 @@ export function bubbleRender(processing: VNode) {
);
}
// 获得对应节点
processing.realNode = newTextDom(newText, processing);
const newTextNode = InulaReconciler.hostConfig.createText(newText);
saveVNode(processing, newTextNode);
processing.realNode = newTextNode;
}
}

View File

@ -20,10 +20,10 @@ import * as ContextProviderRender from './ContextProvider';
import * as ForwardRefRender from './ForwardRef';
import * as FragmentRender from './Fragment';
import * as FunctionComponentRender from './FunctionComponent';
import * as DomComponentRender from './DomComponent';
import * as DomPortalRender from './DomPortal';
import * as DomComponentRender from './Component';
import * as DomPortalRender from './Portal';
import * as TreeRootRender from './TreeRoot';
import * as DomTextRender from './DomText';
import * as DomTextRender from './Text';
import * as LazyComponentRender from './LazyComponent';
import * as MemoComponentRender from './MemoComponent';
import * as SuspenseComponentRender from './SuspenseComponent';
@ -35,10 +35,10 @@ import {
ForwardRef,
Fragment,
FunctionComponent,
DomComponent,
DomPortal,
Component,
Portal,
TreeRoot,
DomText,
Text,
LazyComponent,
MemoComponent,
SuspenseComponent,
@ -53,10 +53,10 @@ export default {
[ForwardRef]: ForwardRefRender,
[Fragment]: FragmentRender,
[FunctionComponent]: FunctionComponentRender,
[DomComponent]: DomComponentRender,
[DomPortal]: DomPortalRender,
[Component]: DomComponentRender,
[Portal]: DomPortalRender,
[TreeRoot]: TreeRootRender,
[DomText]: DomTextRender,
[Text]: DomTextRender,
[LazyComponent]: LazyComponentRender,
[MemoComponent]: MemoComponentRender,
[SuspenseComponent]: SuspenseComponentRender,

View File

@ -17,32 +17,22 @@
*
*/
import type { Container } from '../../dom/DOMOperator';
import type { RefType, VNode } from '../Types';
import { Container } from '../Types';
import type { ElementType, RefType, VNode } from '../Types';
import { listenToPromise, SuspenseChildStatus } from '../render/SuspenseComponent';
import {
FunctionComponent,
ForwardRef,
ClassComponent,
TreeRoot,
DomComponent,
DomText,
DomPortal,
Component,
Text,
Portal,
SuspenseComponent,
MemoComponent,
} from '../vnode/VNodeTags';
import { FlagUtils, ResetText, Clear, Update, DirectAddition } from '../vnode/VNodeFlags';
import { mergeDefaultProps } from '../render/LazyComponent';
import {
submitDomUpdate,
clearText,
appendChildElement,
insertDomBefore,
removeChildDom,
hideDom,
unHideDom,
} from '../../dom/DOMOperator';
import {
callEffectRemove,
callUseEffects,
@ -51,8 +41,37 @@ import {
} from './HookEffectHandler';
import { handleSubmitError } from '../ErrorHandler';
import { travelVNodeTree, clearVNode, isDomVNode, getSiblingDom } from '../vnode/VNodeUtils';
import { shouldAutoFocus } from '../../dom/utils/Common';
import { shouldAutoFocus } from '../../renderer/utils/common';
import { BELONG_CLASS_VNODE_KEY } from '../vnode/VNode';
import { saveVNode, updateVNodeProps } from '../utils/InternalKeys';
import { InulaReconciler } from '..';
import { isNativeElement } from '../props/PropHandler';
import { setElementProps } from '../props/PropHandler';
function submitDomUpdate(tag: string, vNode: VNode) {
const newProps = vNode.props;
const element: ElementType | null = vNode.realNode;
if (tag === Component) {
// DomComponent类型
if (element !== null && element !== undefined) {
const type = vNode.type;
const changeList = vNode.changeList;
vNode.changeList = null;
if (changeList !== null) {
saveVNode(vNode, element);
updateVNodeProps(element, newProps);
InulaReconciler.hostConfig.onSubmit(tag, type, element, newProps, changeList);
const isNativeTag = isNativeElement(type, newProps);
setElementProps(element, changeList, isNativeTag, false);
InulaReconciler.hostConfig.updateInputValue(type, element, newProps);
}
}
} else if (tag === Text) {
InulaReconciler.hostConfig.onSubmit('Text', '', element as ElementType, newProps, []);
}
}
function callComponentWillUnmount(vNode: VNode, instance: any) {
try {
@ -122,7 +141,7 @@ function callAfterSubmitLifeCycles(vNode: VNode): void {
callStateCallback(vNode, instance);
return;
}
case DomComponent: {
case Component: {
if (vNode.isCreated && (vNode.flags & Update) === Update) {
// button、input、select、textarea、如果有 autoFocus 属性需要focus
if (shouldAutoFocus(vNode.type, vNode.props)) {
@ -141,11 +160,11 @@ function hideOrUnhideAllChildren(vNode, isHidden) {
(node: VNode) => {
const instance = node.realNode;
if (node.tag === DomComponent || node.tag === DomText) {
if (node.tag === Component || node.tag === Text) {
if (isHidden) {
hideDom(node.tag, instance);
InulaReconciler.hostConfig.hideElement(node.tag, instance);
} else {
unHideDom(node.tag, instance, node.props);
InulaReconciler.hostConfig.unHideElement(node.tag, instance, node.props);
}
}
},
@ -192,7 +211,7 @@ function unmountNestedVNodes(vNode: VNode): void {
},
node =>
// 如果是DomPortal不需要遍历child
node.tag === DomPortal,
node.tag === Portal,
vNode,
null
);
@ -213,7 +232,7 @@ function unmountDomComponents(vNode: VNode): void {
let tag;
while (parent !== null) {
tag = parent.tag;
if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) {
if (tag === Component || tag === TreeRoot || tag === Portal) {
currentParent = parent.realNode;
break;
}
@ -222,13 +241,13 @@ function unmountDomComponents(vNode: VNode): void {
currentParentIsValid = true;
}
if (node.tag === DomComponent || node.tag === DomText) {
if (node.tag === Component || node.tag === Text) {
// 卸载vNode递归遍历子vNode
unmountNestedVNodes(node);
// 在所有子项都卸载后删除dom树中的节点
removeChildDom(currentParent, node.realNode);
} else if (node.tag === DomPortal) {
InulaReconciler.hostConfig.removeChildElement(currentParent, node.realNode);
} else if (node.tag === Portal) {
if (node.child !== null) {
currentParent = node.realNode;
}
@ -238,10 +257,10 @@ function unmountDomComponents(vNode: VNode): void {
},
node =>
// 如果是dom不用再遍历child
node.tag === DomComponent || node.tag === DomText,
node.tag === Component || node.tag === Text,
vNode,
node => {
if (node.tag === DomPortal) {
if (node.tag === Portal) {
// 当离开portal需要重新设置parent
currentParentIsValid = false;
}
@ -275,11 +294,11 @@ function unmountVNode(vNode: VNode): void {
}
break;
}
case DomComponent: {
case Component: {
detachRef(vNode);
break;
}
case DomPortal: {
case Portal: {
// 这里会递归
unmountDomComponents(vNode);
break;
@ -292,9 +311,9 @@ function unmountVNode(vNode: VNode): void {
function insertDom(parent, realNode, beforeDom) {
if (beforeDom) {
insertDomBefore(parent, realNode, beforeDom);
InulaReconciler.hostConfig.insertElementBefore(parent, realNode, beforeDom);
} else {
appendChildElement(parent, realNode);
InulaReconciler.hostConfig.appendChildElement(parent, realNode);
}
}
@ -303,7 +322,7 @@ function insertOrAppendPlacementNode(node: VNode, beforeDom: Element | null, par
if (isDomVNode(node)) {
insertDom(parent, realNode, beforeDom);
} else if (tag === DomPortal) {
} else if (tag === Portal) {
// 这里不做处理直接在portal中处理
} else {
// 插入子节点们
@ -321,7 +340,7 @@ function submitAddition(vNode: VNode): void {
let tag;
while (parent !== null) {
tag = parent.tag;
if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) {
if (tag === Component || tag === TreeRoot || tag === Portal) {
parentDom = parent.realNode;
break;
}
@ -330,7 +349,7 @@ function submitAddition(vNode: VNode): void {
if ((parent!.flags & ResetText) === ResetText) {
// 在insert之前先reset
clearText(parentDom);
InulaReconciler.hostConfig.clearText(parentDom);
FlagUtils.removeFlag(parent!, ResetText);
}
@ -365,7 +384,7 @@ function submitClear(vNode: VNode): void {
let tag;
while (parent !== null) {
tag = parent.tag;
if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) {
if (tag === Component || tag === TreeRoot || tag === Portal) {
parentDom = parent.realNode;
break;
}
@ -381,7 +400,7 @@ function submitClear(vNode: VNode): void {
}
// 在所有子项都卸载后删除dom树中的节点
removeChildDom(parentDom, vNode.realNode);
InulaReconciler.hostConfig.removeChildElement(parentDom, vNode.realNode);
const realNodeNext = getSiblingDom(vNode);
insertDom(parentDom, cloneDom, realNodeNext);
vNode.realNode = cloneDom;
@ -414,8 +433,8 @@ function submitUpdate(vNode: VNode): void {
callUseLayoutEffectRemove(vNode);
break;
}
case DomComponent:
case DomText: {
case Component:
case Text: {
submitDomUpdate(vNode.tag, vNode);
break;
}
@ -431,7 +450,7 @@ function submitUpdate(vNode: VNode): void {
}
function submitResetTextContent(vNode: VNode) {
clearText(vNode.realNode);
InulaReconciler.hostConfig.clearText(vNode.realNode);
}
export {

View File

@ -16,7 +16,6 @@
import type { VNode } from '../Types';
import { FlagUtils, Addition, Snapshot, ResetText, Ref, Update, Deletion, Clear, Callback } from '../vnode/VNodeFlags';
import { prepareForSubmit, resetAfterSubmit } from '../../dom/DOMOperator';
import { handleSubmitError } from '../ErrorHandler';
import {
attachRef,
@ -33,6 +32,7 @@ import { tryRenderFromRoot } from '../TreeBuilder';
import { InRender, copyExecuteMode, setExecuteMode, changeMode } from '../ExecuteMode';
import { isSchedulingEffects, setSchedulingEffects } from './HookEffectHandler';
import { getStartVNode } from '../GlobalVar';
import { InulaReconciler } from '..';
let rootThrowError = null;
@ -179,14 +179,14 @@ export function submitToRender(treeRoot) {
const preMode = copyExecuteMode();
changeMode(InRender, true);
prepareForSubmit();
InulaReconciler.hostConfig.prepareForSubmit();
// before submit阶段
beforeSubmit(dirtyNodes);
// submit阶段
submit(dirtyNodes);
resetAfterSubmit();
InulaReconciler.hostConfig.resetAfterSubmit();
// after submit阶段
afterSubmit(dirtyNodes);

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula 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.
*/
/**
* dom节点赋 VNode
*/
import type { ElementType, VNode } from '../Types';
import { Container } from '../Types';
import { Component as Component, Text as Text, TreeRoot } from '../vnode/VNodeTags';
import { findDomVNode as findRealVNode } from '../vnode/VNodeUtils';
const randomKey = Math.random().toString(16).slice(2);
const INTERNAL_VNODE = `_inula_VNode_${randomKey}`;
const INTERNAL_PROPS = `_inula_Props_${randomKey}`;
const INTERNAL_NONDELEGATEEVENTS = `_inula_nonDelegatedEvents_${randomKey}`;
export const HANDLER_KEY = `_inula_valueChangeHandler_${randomKey}`;
export const EVENT_KEY = `_inula_ev_${randomKey}`;
export type Props = Record<string, any> & {
autoFocus?: boolean;
children?: any;
disabled?: boolean;
hidden?: boolean;
style?: { display?: string };
};
// 通过 VNode 实例获取 Element 节点
export function getElement(vNode: VNode): ElementType | null {
const { tag } = vNode;
if (tag === Component || tag === Text) {
return vNode.realNode;
}
return null;
}
// 将 VNode 属性相关信息挂到对象的特定属性上
export function saveVNode(vNode: VNode, element: ElementType | Text | Container): void {
element[INTERNAL_VNODE] = vNode;
}
// 用 Element 节点,来找其对应的 VNode 实例
export function getVNode(element: ElementType | Container): VNode | null {
const vNode = element[INTERNAL_VNODE] || (element as Container)._treeRoot;
if (vNode) {
const { tag } = vNode;
if (tag === Component || tag === Text || tag === TreeRoot) {
return vNode;
}
}
return null;
}
// 用 element 对象,来寻找其对应或者说是最近父级的 vNode
export function getNearestVNode(element: ElementType): null | VNode {
let elementNode: ElementType | null = element;
// 寻找当前节点及其所有祖先节点是否有标记VNODE
while (elementNode) {
const vNode = elementNode[INTERNAL_VNODE];
if (vNode) {
return vNode;
}
elementNode = element.parentNode ?? null;
}
return null;
}
export function findElementByClassInst(inst) {
const vNode = inst._vNode;
if (vNode === undefined) {
throw new Error('Unable to find the vNode by class instance.');
}
const elementVNode = findRealVNode(vNode);
return elementVNode !== null ? elementVNode.realNode : null;
}
// 获取 vNode 上的属性相关信息
export function getVNodeProps(element: ElementType | Text): Props | null {
return element[INTERNAL_PROPS] || null;
}
// 将 Element 属性相关信息挂到 Element 对象的特定属性上
export function updateVNodeProps(element: ElementType | Text, props: Props): void {
element[INTERNAL_PROPS] = props;
}
export function getNonDelegatedListenerMap(element: ElementType | Text): Map<string, EventListener> {
let eventsMap = element[INTERNAL_NONDELEGATEEVENTS];
if (!eventsMap) {
eventsMap = new Map();
element[INTERNAL_NONDELEGATEEVENTS] = eventsMap;
}
return eventsMap;
}

View File

@ -0,0 +1,33 @@
import { InulaReconciler } from '..';
import { ElementType } from '../Types';
import { Props } from './InternalKeys';
// button、input、select、textarea、如果有 autoFocus 属性需要focus
export function shouldAutoFocus(tagName: string, props: Props): boolean {
const { button, input, select, textarea } = InulaReconciler.hostConfig.elementConfig;
const types = [button.nodeName, input.nodeName, select.nodeName, textarea.nodeName];
return types.includes(tagName) ? Boolean(props.autoFocus) : false;
}
export function isNotNull(object: any): boolean {
return object !== null && object !== undefined;
}
export function getTag(element: ElementType) {
return element?.nodeName?.toLowerCase();
}
export function isInputElement(element: ElementType): boolean {
return getTag(element) === InulaReconciler.hostConfig.elementConfig.input.nodeName;
}
export function isElement(element: ElementType) {
return element.nodeType === InulaReconciler.hostConfig.elementConfig.common.nodeType;
}
export function isText(element: ElementType) {
return element.nodeType === InulaReconciler.hostConfig.elementConfig.text.nodeType;
}
export function isDocument(dom: ElementType) {
return dom.nodeType === 9;
}

View File

@ -20,13 +20,13 @@ import {
TreeRoot,
FunctionComponent,
ClassComponent,
DomPortal,
DomText,
Portal,
Text,
ContextConsumer,
ForwardRef,
SuspenseComponent,
LazyComponent,
DomComponent,
Component,
Fragment,
ContextProvider,
Profiler,
@ -153,19 +153,19 @@ export class VNode {
this.classComponentWillUnmount = null;
this.src = null;
break;
case DomPortal:
case Portal:
this.realNode = null;
this.context = null;
this.delegatedEvents = new Set<string>();
this.src = null;
break;
case DomComponent:
case Component:
this.realNode = null;
this.changeList = null;
this.context = null;
this.src = null;
break;
case DomText:
case Text:
this.realNode = null;
break;
case SuspenseComponent:

View File

@ -22,10 +22,10 @@ import {
ForwardRef,
Fragment,
FunctionComponent,
DomComponent,
DomPortal,
Component,
Portal,
TreeRoot,
DomText,
Text,
LazyComponent,
MemoComponent,
SuspenseComponent,
@ -108,14 +108,14 @@ export function createFragmentVNode(fragmentKey, fragmentProps) {
}
export function createDomTextVNode(content) {
const vNode = newVirtualNode(DomText, null, content);
const vNode = newVirtualNode(Text, null, content);
vNode.shouldUpdate = true;
return vNode;
}
export function createPortalVNode(portal) {
const children = portal.children ?? [];
const vNode = newVirtualNode(DomPortal, portal.key, children);
const vNode = newVirtualNode(Portal, portal.key, children);
vNode.shouldUpdate = true;
vNode.realNode = portal.realNode;
return vNode;
@ -130,7 +130,7 @@ export function createUndeterminedVNode(type, key, props, source: Source | null)
vNodeTag = ClassComponent;
}
} else if (componentType === 'string') {
vNodeTag = DomComponent;
vNodeTag = Component;
} else if (type === TYPE_SUSPENSE) {
vNodeTag = SuspenseComponent;
} else if (componentType === 'object' && type !== null && typeMap[type.vtype]) {
@ -166,7 +166,7 @@ export function getElementTag(element: JSXElement): string {
vNodeTag = ClassComponent;
}
} else if (componentType === 'string') {
vNodeTag = DomComponent;
vNodeTag = Component;
} else if (type === TYPE_SUSPENSE) {
vNodeTag = SuspenseComponent;
} else if (componentType === 'object' && type !== null && typeMap[type.vtype]) {

View File

@ -21,9 +21,9 @@ export type VNodeTag = string;
export const TreeRoot = 'TreeRoot'; // tree的根节点用于存放一些tree级的变量
export const FunctionComponent = 'FunctionComponent';
export const ClassComponent = 'ClassComponent';
export const DomPortal = 'DomPortal';
export const DomComponent = 'DomComponent';
export const DomText = 'DomText';
export const Portal = 'Portal';
export const Component = 'Component';
export const Text = 'Text';
export const Fragment = 'Fragment';
export const ContextConsumer = 'ContextConsumer';
export const ContextProvider = 'ContextProvider';

View File

@ -19,8 +19,8 @@
import type { VNode } from '../Types';
import { DomComponent, DomPortal, DomText, TreeRoot } from './VNodeTags';
import { getNearestVNode } from '../../dom/DOMInternalKeys';
import { Component, Portal, Text, TreeRoot } from './VNodeTags';
import { getNearestVNode } from '../../renderer/utils/InternalKeys';
import { Addition, InitFlag } from './VNodeFlags';
import { BELONG_CLASS_VNODE_KEY } from './VNode';
@ -133,19 +133,19 @@ export function clearVNode(vNode: VNode) {
// 是dom类型的vNode
export function isDomVNode(node: VNode) {
return node.tag === DomComponent || node.tag === DomText;
return node.tag === Component || node.tag === Text;
}
// 是容器类型的vNode
function isDomContainer(vNode: VNode): boolean {
return vNode.tag === DomComponent || vNode.tag === TreeRoot || vNode.tag === DomPortal;
return vNode.tag === Component || vNode.tag === TreeRoot || vNode.tag === Portal;
}
export function findDomVNode(vNode: VNode): VNode | null {
const ret = travelVNodeTree(
vNode,
node => {
if (node.tag === DomComponent || node.tag === DomText) {
if (node.tag === Component || node.tag === Text) {
return node;
}
return null;
@ -210,7 +210,7 @@ export function getSiblingDom(vNode: VNode): Element | null {
}
// 没有子节点或是DomPortal
if (!node.child || node.tag === DomPortal) {
if (!node.child || node.tag === Portal) {
continue findSibling;
} else {
const childVNode = node.child;
@ -227,11 +227,11 @@ export function getSiblingDom(vNode: VNode): Element | null {
}
function isPortalRoot(vNode, targetContainer) {
if (vNode.tag === DomPortal) {
if (vNode.tag === Portal) {
let topVNode = vNode.parent;
while (topVNode !== null) {
const grandTag = topVNode.tag;
if (grandTag === TreeRoot || grandTag === DomPortal) {
if (grandTag === TreeRoot || grandTag === Portal) {
const topContainer = topVNode.realNode;
// 如果topContainer是targetContainer不需要在这里处理
if (topContainer === targetContainer) {
@ -250,7 +250,7 @@ export function findRoot(targetVNode, targetDom) {
// 确认vNode节点是否准确portal场景下可能祖先节点不准确
let vNode = targetVNode;
while (vNode !== null) {
if (vNode.tag === TreeRoot || vNode.tag === DomPortal) {
if (vNode.tag === TreeRoot || vNode.tag === Portal) {
let dom = vNode.realNode;
if (dom === targetDom) {
break;
@ -264,7 +264,7 @@ export function findRoot(targetVNode, targetDom) {
if (parentNode === null) {
return null;
}
if (parentNode.tag === DomComponent || parentNode.tag === DomText) {
if (parentNode.tag === Component || parentNode.tag === Text) {
return findRoot(parentNode, targetDom);
}
dom = dom.parentNode;

View File

@ -17,7 +17,6 @@ import { Component } from './renderer/components/BaseClassComponent';
import { MutableRef, RefCallBack, RefObject } from './renderer/hooks/HookType';
import * as Event from './EventTypes';
import { BaseElement } from './jsx-type';
//
// --------------------------------- Inula Base Types ----------------------------------
@ -68,6 +67,7 @@ interface ConsumerProps<T> {
export interface Context<T> {
Provider: ExoticComponent<ProviderProps<T>>;
Consumer: ExoticComponent<ConsumerProps<T>>;
// 兼容React
displayName?: string | undefined;
}
@ -77,28 +77,6 @@ export interface FunctionComponent<P = KVObject> {
displayName?: string;
}
export type ClassType<P, T extends Component<P, ComponentState>, C extends ComponentClass<P>> = C &
(new (props: P, context?: any) => T);
export interface ClassicComponent<P = KVObject, S = KVObject> extends Component<P, S> {
replaceState(nextState: S, callback?: () => void): void;
isMounted(): boolean;
getInitialState?(): S;
}
export type InulaCElement<P, T extends Component<P, ComponentState>> = ComponentElement<P, T>;
interface ComponentElement<P, T extends Component<P, ComponentState>> extends InulaElement<P, ComponentClass<P>> {
ref?: LegacyRef<T> | undefined;
}
export interface ClassicComponentClass<P = KVObject> extends ComponentClass<P> {
new (props: P, context?: any): ClassicComponent<P, ComponentState>;
getDefaultProps?(): P;
}
export interface FunctionComponentElement<P> extends InulaElement<P, FunctionComponent<P>> {
ref?: ('ref' extends keyof P ? (P extends { ref?: infer R | undefined } ? R : never) : never) | undefined;
}
export interface ComponentClass<P = KVObject, S = ComponentState> extends StaticLifecycle<P, S> {
new (props: P, context?: unknown): Component<P, S>;
@ -141,7 +119,7 @@ export type PropsWithRef<P> = 'ref' extends keyof P
: P
: P;
export type Attributes = {
type Attributes = {
key?: Key | null | undefined;
};
@ -294,7 +272,7 @@ interface InulaBaseEvent<E = unknown, Tr = unknown, Ta = unknown> {
}
// eslint-disable-next-line
export interface SyntheticEvent<T = Element, E = Event> extends InulaBaseEvent<E, EventTarget & T, EventTarget> {}
interface SyntheticEvent<T = Element, E = Event> extends InulaBaseEvent<E, EventTarget & T, EventTarget> {}
export interface ClipboardEvent<T = Element> extends SyntheticEvent<T, Event.DomClipboardEvent> {
clipboardData: DataTransfer;
@ -432,59 +410,23 @@ export interface MouseEvent<T = Element, E = Event.DomMouseEvent> extends UIEven
export type EventHandler<E extends SyntheticEvent<unknown>> = { bivarianceHack(event: E): void }['bivarianceHack'];
export type InulaClipboardEventHandler<T = Element> = EventHandler<ClipboardEvent<T>>;
export type InulaCompositionEventHandler<T = Element> = EventHandler<CompositionEvent<T>>;
export type InulaDragEventHandler<T = Element> = EventHandler<DragEvent<T>>;
export type InulaFocusEventHandler<T = Element> = EventHandler<FocusEvent<T>>;
export type InulaFormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
export type InulaChangeEventHandler<T = Element> = EventHandler<ChangeEvent<T>>;
export type InulaKeyboardEventHandler<T = Element> = EventHandler<KeyboardEvent<T>>;
export type InulaMouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
export type InulaTouchEventHandler<T = Element> = EventHandler<TouchEvent<T>>;
export type InulaPointerEventHandler<T = Element> = EventHandler<PointerEvent<T>>;
export type InulaUIEventHandler<T = Element> = EventHandler<UIEvent<T>>;
export type InulaWheelEventHandler<T = Element> = EventHandler<WheelEvent<T>>;
export type InulaAnimationEventHandler<T = Element> = EventHandler<AnimationEvent<T>>;
export type InulaTransitionEventHandler<T = Element> = EventHandler<TransitionEvent<T>>;
export type ClipboardEventHandler<T = Element> = EventHandler<ClipboardEvent<T>>;
export type CompositionEventHandler<T = Element> = EventHandler<CompositionEvent<T>>;
export type DragEventHandler<T = Element> = EventHandler<DragEvent<T>>;
export type FocusEventHandler<T = Element> = EventHandler<FocusEvent<T>>;
export type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
export type ChangeEventHandler<T = Element> = EventHandler<ChangeEvent<T>>;
export type KeyboardEventHandler<T = Element> = EventHandler<KeyboardEvent<T>>;
export type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
export type TouchEventHandler<T = Element> = EventHandler<TouchEvent<T>>;
export type PointerEventHandler<T = Element> = EventHandler<PointerEvent<T>>;
export type UIEventHandler<T = Element> = EventHandler<UIEvent<T>>;
export type WheelEventHandler<T = Element> = EventHandler<WheelEvent<T>>;
export type AnimationEventHandler<T = Element> = EventHandler<AnimationEvent<T>>;
export type TransitionEventHandler<T = Element> = EventHandler<TransitionEvent<T>>;
//
// --------------------------------- Css Props----------------------------------
//
export type CSSProperties = Record<string | number, any>;
//
// --------------------------------- VDOM ---------------------------------------
//
export type LegacyRef<T> = string | Ref<T>;
export interface ClassAttributes<T> extends Attributes {
ref?: LegacyRef<T> | undefined;
}
export interface InulaCSSProperties extends CSSProperties {}
export type InulaBoolean = boolean | 'true' | 'false';
export type InulaEventHandler<T = Element> = EventHandler<SyntheticEvent<T>>;
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace JSX {
interface IntrinsicAttributes extends Attributes {}
interface Element extends InulaElement<any, any> {}
interface ElementClass extends Component<any> {
render(): InulaNode;
}
interface ElementAttributesProperty {
props: KVObject;
}
interface ElementChildrenAttribute {
children: KVObject;
}
interface IntrinsicClassAttributes<T> extends ClassAttributes<T> {}
type IntrinsicElements = BaseElement;
}
}

View File

@ -17,7 +17,6 @@ import * as Inula from '../../src/index';
describe('mouseenter和mouseleave事件测试', () => {
let container;
beforeEach(() => {
jest.resetModules();
container = document.createElement('div');
@ -254,7 +253,6 @@ describe('mouseenter和mouseleave事件测试', () => {
const divRef = Inula.createRef();
const otherDivRef = Inula.createRef();
const onMouseEnter = jest.fn();
function Component() {
return (
<div ref={divRef}>

View File

@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details.
*/
import { unmountComponentAtNode } from '../../src/dom/DOMExternal';
import { unmountComponentAtNode } from '../../src/renderer/External';
import { getLogUtils } from './testUtils';
const LogUtils = getLogUtils();

12
packages/max/.fatherrc.ts Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from 'father';
export default defineConfig({
cjs: {
output: 'dist',
ignores: ['src/client/**'],
},
esm: {
input: 'src/client',
output: 'client/client',
},
});

21
packages/max/.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
/node_modules
/packages/**/node_modules
/packages/**/dist
/packages/**/tsconfig.tsbuildinfo
/packages/**/src/**/fixtures/*/dist
/packages/**/src/**/fixtures/*/.umi
/packages/**/src/**/fixtures/*/.umi-production
.umi
.inula
.umi-production
.inula-production
.umi-test
.inula-test
dist
es
lib
.turbo
.idea
playwright-report
/packages/inula/client
.env.local

58
packages/max/README.md Normal file
View File

@ -0,0 +1,58 @@
# Inula
## 项目简介
inula-max 是一个关注业务需求,以开发体验为主的前端框架,集成 openInula 全生态。
## 快速开始
你可以通过以下步骤快速开始使用 Inula
```base
npx inula-max init [dir]
```
初始化一个 Inula 项目,目录可选,一般操作是新建一个空白文件夹,再执行 `npx inula-max init` 即可。
## 特性
### openInula 官方组件
#### 状态管理器
Inula-X 是 openInula 默认提供的状态管理器,无需额外引入三方库,就可以简单实现跨组件/页面共享状态。
#### 请求
Inula-request 涵盖常见的网络请求方式,并提供动态轮询钩子函数给用户更便捷的定制化请求体验。
#### 国际化
Inula-intl 提供了国际化功能,涵盖了基本的国际化组件和钩子函数,便于用户在构建国际化能力时方便操作。
### 其他能力
#### antd
Ant Design 是一个功能丰富的 UI 组件库。
#### ProComponents
ProComponents 是一个让中后台开发更简单的工具。
#### AIGC
AIGC 是一个使用 Azure Api 对接 OpenAI ChatGPT 4 模型的能力,可以快速使用 AIGC 助力业务开发。
## 更多信息
请访问 [OpenInula 文档](https://docs.openinula.net/) 获取更多详细信息。
## 贡献
欢迎贡献代码和提出问题!请查看 [贡献指南](CONTRIBUTING.md) 了解如何参与项目。
## 许可证
本项目基于 [MIT](LICENSE) 许可证开源。

14
packages/max/bin/inula.js Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env node
// setNodeTitle
process.title = 'inula';
// Use magic to suppress node deprecation warnings
// See: https://github.com/nodejs/node/blob/master/lib/internal/process/warning.js#L77
// @ts-ignore
process.noDeprecation = '1';
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('../dist/cli')
.run()
.catch((e) => {
console.error(e);
process.exit(1);
});

35
packages/max/client/client/plugin.d.ts vendored Normal file
View File

@ -0,0 +1,35 @@
export declare enum ApplyPluginsType {
compose = "compose",
modify = "modify",
event = "event"
}
interface IPlugin {
path?: string;
apply: Record<string, any>;
}
export declare class PluginManager {
opts: {
validKeys: string[];
};
hooks: {
[key: string]: any;
};
constructor(opts: {
validKeys: string[];
});
register(plugin: IPlugin): void;
getHooks(keyWithDot: string): any;
applyPlugins({ key, type, initialValue, args, async, }: {
key: string;
type: ApplyPluginsType;
initialValue?: any;
args?: object;
async?: boolean;
}): any;
static create(opts: {
validKeys: string[];
plugins: IPlugin[];
}): PluginManager;
}
export {};
//# sourceMappingURL=plugin.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/client/plugin.ts"],"names":[],"mappings":"AAEA,oBAAY,gBAAgB;IAC1B,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,KAAK,UAAU;CAChB;AAED,UAAU,OAAO;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,qBAAa,aAAa;IACxB,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC9B,KAAK,EAAE;QACL,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAM;gBACK,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE;IAIzC,QAAQ,CAAC,MAAM,EAAE,OAAO;IAaxB,QAAQ,CAAC,UAAU,EAAE,MAAM;IAqB3B,YAAY,CAAC,EACX,GAAG,EACH,IAAI,EACJ,YAAY,EACZ,IAAI,EACJ,KAAK,GACN,EAAE;QACD,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,gBAAgB,CAAC;QACvB,YAAY,CAAC,EAAE,GAAG,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB;IAuFD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,EAAE,CAAA;KAAE;CAShE"}

File diff suppressed because one or more lines are too long

7
packages/max/client/client/utils.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
export declare function assert(value: unknown, message: string): void;
export declare function compose({ fns, args, }: {
fns: (Function | any)[];
args?: object;
}): any;
export declare function isPromiseLike(obj: any): boolean;
//# sourceMappingURL=utils.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/client/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,QAErD;AAED,wBAAgB,OAAO,CAAC,EACtB,GAAG,EACH,IAAI,GACL,EAAE;IACD,GAAG,EAAE,CAAC,QAAQ,GAAG,GAAG,CAAC,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,OAMA;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,WAErC"}

View File

@ -0,0 +1,20 @@
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
export function assert(value, message) {
if (!value) throw new Error(message);
}
export function compose(_ref) {
var fns = _ref.fns,
args = _ref.args;
if (fns.length === 1) {
return fns[0];
}
var last = fns.pop();
return fns.reduce(function (a, b) {
return function () {
return b(a, args);
};
}, last);
}
export function isPromiseLike(obj) {
return !!obj && _typeof(obj) === 'object' && typeof obj.then === 'function';
}

View File

@ -0,0 +1,5 @@
import { defineConfig } from 'inula-max';
export default defineConfig({
title: 'boilerplate',
});

View File

@ -0,0 +1,15 @@
{
"name": "@example/boilerplate",
"private": true,
"scripts": {
"build": "inula-max build",
"build-analyze": "ANALYZE=1 inula-max build",
"dev": "inula-max dev",
"preview": "inula-max preview",
"setup": "inula-max setup",
"start": "npm run dev"
},
"dependencies": {
"inula-max": "link:.."
}
}

View File

@ -0,0 +1,19 @@
import { useStore } from 'inula';
const Page = () => {
const store = useStore('hello');
return (
<div>
hello {store.title}
<button
onClick={() => {
store.changeName();
}}
>
</button>
</div>
);
};
export default Page;

View File

@ -0,0 +1,13 @@
import { createStore } from 'inula';
export default createStore({
id: 'hello12',
actions: {
changeName: (state) => {
state.title = 'openinula';
},
},
state: {
title: 'inulajs',
},
});

View File

@ -0,0 +1,13 @@
import { createStore } from 'inula';
export default createStore({
id: 'hello',
actions: {
changeName: (state) => {
state.title = 'openinula';
},
},
state: {
title: 'inulajs',
},
});

View File

@ -0,0 +1,10 @@
{
"extends": "./src/.inula-max/tsconfig.json",
"compilerOptions":{
"paths": {
"inula-max": [
"../../"
]
},
}
}

1
packages/max/demo/typings.d.ts vendored Normal file
View File

@ -0,0 +1 @@
import "inula/typings";

7
packages/max/eslint.js Normal file
View File

@ -0,0 +1,7 @@
try {
require.resolve('@umijs/lint/package.json');
} catch (err) {
throw new Error('@umijs/lint is not built-in, please install it manually before run umi lint.');
}
module.exports = process.env.LEGACY_ESLINT ? require('@umijs/lint/dist/config/eslint/legacy') : require('@umijs/lint/dist/config/eslint');

View File

10
packages/max/index.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
// @ts-ignore
export * from '@@/exports';
export type {
IApi,
webpack,
IRoute,
UmiApiRequest,
UmiApiResponse,
} from '@aluni/types';
export * from './dist';

55
packages/max/package.json Normal file
View File

@ -0,0 +1,55 @@
{
"name": "inula-max",
"version": "0.0.1",
"description": "A Inulajs framework based on umi.",
"license": "MIT",
"main": "dist/index.js",
"types": "index.d.ts",
"bin": {
"inula-max": "bin/inula.js"
},
"files": [
"assets",
"bin",
"client",
"dist",
"index.d.ts",
"plugin-utils.d.ts",
"plugin-utils.js",
"eslint.js",
"prettier.js"
],
"scripts": {
"build": "father build",
"dev": "father dev"
},
"dependencies": {
"@aluni/preset-inula": "0.0.5",
"@aluni/types": "^0.0.5",
"@umijs/bundler-utils": "4.0.88",
"@umijs/bundler-vite": "4.0.88",
"@umijs/bundler-webpack": "4.0.88",
"@umijs/core": "4.0.88",
"@umijs/lint": "4.0.88",
"@umijs/openapi": "^1.13.0",
"@umijs/preset-blocks": "0.0.4",
"@umijs/preset-umi": "4.0.88",
"@umijs/server": "4.0.88",
"@umijs/utils": "4.0.88",
"prettier": "^2.6.2",
"prettier-plugin-organize-imports": "^3.2.2",
"prettier-plugin-packagejson": "2.4.3",
"rimraf": "^6.0.1",
"openinula": "0.1.1",
"serve-static": "^1.16.2"
},
"publishConfig": {
"access": "public"
},
"authors": [
"chenxiaocong <xiaohuoni@gmail.com> (https://github.com/xiaohuoni)"
],
"devDependencies": {
"father": "^4.5.0"
}
}

1
packages/max/plugin-utils.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from './dist/pluginUtils';

View File

@ -0,0 +1 @@
module.exports = require('./dist/pluginUtils');

13
packages/max/prettier.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
printWidth: 80,
singleQuote: true,
trailingComma: 'all',
proseWrap: 'never',
endOfLine: 'lf',
overrides: [{ files: '.prettierrc', options: { parser: 'json' } }],
plugins: [
require.resolve('prettier-plugin-packagejson'),
require.resolve('prettier-plugin-organize-imports'),
],
pluginSearchDirs: false,
};

64
packages/max/src/cli.ts Normal file
View File

@ -0,0 +1,64 @@
import { deepmerge, logger, yParser } from '@umijs/utils';
import { BUILD_COMMANDS, DEV_COMMAND } from './constants';
import {
checkLocal,
checkVersion as checkNodeVersion,
setNoDeprecation,
setNodeTitle,
} from './node';
import { Service } from './service';
interface IOpts {
args?: yParser.Arguments;
}
export async function run(_opts?: IOpts) {
checkNodeVersion();
checkLocal();
setNodeTitle();
setNoDeprecation();
const args =
_opts?.args ||
yParser(process.argv.slice(2), {
alias: {
version: ['v'],
help: ['h'],
},
boolean: ['version'],
});
const command = args._[0];
if (command === DEV_COMMAND) {
process.env.NODE_ENV = 'development';
} else if (BUILD_COMMANDS.includes(command)) {
process.env.NODE_ENV = 'production';
}
try {
const service = new Service();
await service.run2({
name: command,
args: deepmerge({}, args),
});
// handle restart for dev command
if (command === DEV_COMMAND) {
async function listener(data: any) {
if (data?.type === 'RESTART') {
// off self
process.off('message', listener);
// restart
run({ args });
}
}
process.on('message', listener);
}
} catch (e: any) {
logger.error(e);
process.exit(1);
}
}

View File

@ -0,0 +1,71 @@
import { ApplyPluginsType, PluginManager } from './plugin';
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
test('PluginManager#applyPlugins in async=false mode', async () => {
const pm = new PluginManager({
validKeys: ['foo'],
});
const asyncCall = jest.fn();
const syncCall = jest.fn();
pm.register({
apply: {
foo: async () => {
await delay(100);
asyncCall();
},
},
path: '/a',
});
pm.register({
apply: {
foo: syncCall,
},
path: '/a',
});
await pm.applyPlugins({
key: 'foo',
type: ApplyPluginsType.event,
async: false,
});
expect(syncCall).toBeCalled();
expect(asyncCall).not.toBeCalled();
});
test('PluginManager#applyPlugins in async=true mode', async () => {
const pm = new PluginManager({
validKeys: ['foo'],
});
const asyncCall = jest.fn();
const syncCall = jest.fn();
pm.register({
apply: {
foo: async () => {
await delay(100);
asyncCall();
},
},
path: '/a',
});
pm.register({
apply: {
foo: syncCall,
},
path: '/a',
});
await pm.applyPlugins({
key: 'foo',
type: ApplyPluginsType.event,
async: true,
});
expect(syncCall).toBeCalled();
expect(asyncCall).toBeCalled();
});

Some files were not shown because too many files have changed in this diff Show More