Showing preview only (238K chars total). Download the full file or copy to clipboard to get everything.
Repository: anuoua/unis
Branch: main
Commit: a40e220ce9e6
Files: 145
Total size: 203.9 KB
Directory structure:
gitextract_322wan4z/
├── .github/
│ └── workflows/
│ ├── unis-babel-preset.yml
│ ├── unis-core.yml
│ ├── unis-dom.yml
│ ├── unis-router.yml
│ ├── unis-transition.yml
│ └── unis-vite-preset.yml
├── .gitignore
├── .vscode/
│ └── settings.json
├── LICENSE
├── README-zh_CN.md
├── README.md
├── assets/
│ └── logo.txt
├── package.json
├── packages/
│ ├── unis-babel-preset/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── rollup.config.js
│ │ ├── src/
│ │ │ └── index.ts
│ │ ├── test/
│ │ │ └── index.test.ts
│ │ └── tsconfig.json
│ ├── unis-core/
│ │ ├── .gitignore
│ │ ├── index.d.ts
│ │ ├── jsx-runtime/
│ │ │ ├── jsx-dev-runtime.d.ts
│ │ │ ├── jsx-dev-runtime.js
│ │ │ ├── jsx-dev-runtime.mjs
│ │ │ ├── jsx-runtime.d.ts
│ │ │ ├── jsx-runtime.js
│ │ │ └── jsx-runtime.mjs
│ │ ├── package.json
│ │ ├── rollup.config.mjs
│ │ ├── src/
│ │ │ ├── api/
│ │ │ │ ├── use.ts
│ │ │ │ ├── useContext.ts
│ │ │ │ ├── useEffect.ts
│ │ │ │ ├── useId.ts
│ │ │ │ ├── useLayoutEffect.ts
│ │ │ │ ├── useMemo.ts
│ │ │ │ ├── useProps.ts
│ │ │ │ ├── useReducer.ts
│ │ │ │ ├── useRef.ts
│ │ │ │ ├── useState.ts
│ │ │ │ └── utils.ts
│ │ │ ├── commit.ts
│ │ │ ├── context.ts
│ │ │ ├── createTokTik.ts
│ │ │ ├── diff.ts
│ │ │ ├── fiber.ts
│ │ │ ├── h.ts
│ │ │ ├── index.ts
│ │ │ ├── reconcile.ts
│ │ │ ├── reconcileWalkHooks/
│ │ │ │ ├── context.ts
│ │ │ │ ├── effect.ts
│ │ │ │ └── preElFiber.ts
│ │ │ ├── svg.ts
│ │ │ └── utils.ts
│ │ ├── test/
│ │ │ └── utils.test.ts
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ ├── types/
│ │ │ └── jsx.d.ts
│ │ └── vitest.config.ts
│ ├── unis-dom/
│ │ ├── .gitignore
│ │ ├── index.d.ts
│ │ ├── package.json
│ │ ├── rollup.config.mjs
│ │ ├── server.d.ts
│ │ ├── src/
│ │ │ ├── browser/
│ │ │ │ ├── __test__/
│ │ │ │ │ ├── context.test.tsx
│ │ │ │ │ ├── dom.test.tsx
│ │ │ │ │ ├── effect.test.tsx
│ │ │ │ │ ├── hydrate.test.tsx
│ │ │ │ │ ├── memo.test.tsx
│ │ │ │ │ ├── portal.test.tsx
│ │ │ │ │ ├── reconcile.test.tsx
│ │ │ │ │ ├── util.ts
│ │ │ │ │ └── utils.test.ts
│ │ │ │ ├── const.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── operator.ts
│ │ │ │ ├── render.ts
│ │ │ │ └── toktik.ts
│ │ │ └── server/
│ │ │ ├── __test__/
│ │ │ │ └── server.test.tsx
│ │ │ ├── index.ts
│ │ │ └── operator.ts
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── unis-example/
│ │ ├── .gitignore
│ │ ├── index.html
│ │ ├── other.d.ts
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── src/
│ │ │ ├── Dialog.tsx
│ │ │ ├── Todo.tsx
│ │ │ ├── TodoItem/
│ │ │ │ ├── index.module.css
│ │ │ │ └── index.tsx
│ │ │ ├── Welcome/
│ │ │ │ ├── index.module.css
│ │ │ │ └── index.tsx
│ │ │ ├── global.css
│ │ │ ├── hooks/
│ │ │ │ └── update.ts
│ │ │ ├── index.module.css
│ │ │ └── index.tsx
│ │ ├── tailwind.config.js
│ │ ├── tsconfig.json
│ │ └── vite.config.js
│ ├── unis-router/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── rollup.config.mjs
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── BrowserRouter.tsx
│ │ │ │ ├── Link.tsx
│ │ │ │ ├── NavLink.tsx
│ │ │ │ ├── Outlet.tsx
│ │ │ │ ├── Redirect.tsx
│ │ │ │ ├── Route.tsx
│ │ │ │ └── Routes.tsx
│ │ │ ├── context.tsx
│ │ │ ├── hooks/
│ │ │ │ ├── uHistory.ts
│ │ │ │ ├── uLocation.ts
│ │ │ │ ├── uParams.ts
│ │ │ │ ├── uRouter.tsx
│ │ │ │ └── uTargetPath.tsx
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── utils.test.tsx
│ │ │ └── utils.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── unis-transition/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── rollup.config.mjs
│ │ ├── src/
│ │ │ ├── CSSTransition.ts
│ │ │ ├── TransitionGroup.ts
│ │ │ ├── hooks/
│ │ │ │ ├── uInstance.ts
│ │ │ │ ├── uTransition.ts
│ │ │ │ ├── uUpdate.ts
│ │ │ │ └── uWatch.ts
│ │ │ └── index.ts
│ │ └── tsconfig.json
│ └── unis-vite-preset/
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.js
│ ├── src/
│ │ └── index.ts
│ └── tsconfig.json
├── pnpm-workspace.yaml
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/unis-babel-preset.yml
================================================
name: "@unis/babel-preset CI/CD"
on:
push:
branches:
- 'main'
paths:
- 'packages/unis-babel-preset/**'
jobs:
"test":
runs-on: ubuntu-latest
if: ${{contains(github.event.head_commit.message, '(babel-preset):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.9.0
- run: |
pnpm install
cd packages/unis-core
pnpm build
cd ../unis-babel-preset
pnpm test
"publish":
needs: test
runs-on: ubuntu-latest
if: ${{startsWith(github.event.head_commit.message, 'release(babel-preset):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.9.0
- run: |
pnpm install
cd packages/unis-core
pnpm build
cd ../unis-babel-preset
pnpm build
pnpm publish --no-git-checks --access public
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .github/workflows/unis-core.yml
================================================
name: "@unis/core CI/CD"
on:
push:
branches:
- 'main'
paths:
- 'packages/unis-core/**'
jobs:
"test":
runs-on: ubuntu-latest
if: ${{contains(github.event.head_commit.message, '(core):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.9.0
- run: |
pnpm install
cd packages/unis-core
pnpm test
"publish":
needs: test
runs-on: ubuntu-latest
if: ${{startsWith(github.event.head_commit.message, 'release(core):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.1.2
- run: |
pnpm install
cd packages/unis-core
pnpm build
pnpm publish --no-git-checks --access public
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .github/workflows/unis-dom.yml
================================================
name: "@unis/dom CI/CD"
on:
push:
branches:
- 'main'
paths:
- 'packages/unis-dom/**'
jobs:
"test":
runs-on: ubuntu-latest
if: ${{contains(github.event.head_commit.message, '(dom):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.9.0
- run: |
pnpm install
cd packages/unis-core
pnpm build
cd ../unis-vite-preset
pnpm build
cd ../unis-dom
pnpm test
"publish":
needs: test
runs-on: ubuntu-latest
if: ${{startsWith(github.event.head_commit.message, 'release(dom):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.1.2
- run: |
pnpm install
cd packages/unis-core
pnpm build
cd ../unis-dom
pnpm build
pnpm publish --no-git-checks --access public
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .github/workflows/unis-router.yml
================================================
name: "@unis/router CI/CD"
on:
push:
branches:
- 'main'
paths:
- 'packages/unis-router/**'
jobs:
"test":
runs-on: ubuntu-latest
if: ${{contains(github.event.head_commit.message, '(router):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.9.0
- run: |
pnpm install
cd packages/unis-core
pnpm build
cd ../unis-vite-preset
pnpm build
cd ../unis-router
pnpm test
"publish":
needs: test
runs-on: ubuntu-latest
if: ${{startsWith(github.event.head_commit.message, 'release(router):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.9.0
- run: |
pnpm install
cd packages/unis-core
pnpm build
cd ../unis-router
pnpm build
pnpm publish --no-git-checks --access public
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .github/workflows/unis-transition.yml
================================================
name: "@unis/transition CI/CD"
on:
push:
branches:
- 'main'
paths:
- 'packages/unis-transition/**'
jobs:
"publish":
runs-on: ubuntu-latest
if: ${{startsWith(github.event.head_commit.message, 'release(transition):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.9.0
- run: |
pnpm install
cd packages/unis-core
pnpm build
cd ../unis-transition
pnpm build
pnpm publish --no-git-checks --access public
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .github/workflows/unis-vite-preset.yml
================================================
name: "@unis/vite-preset CI/CD"
on:
push:
branches:
- 'main'
paths:
- 'packages/unis-vite-preset/**'
jobs:
"publish":
runs-on: ubuntu-latest
if: ${{startsWith(github.event.head_commit.message, 'release(vite-preset):')}}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v2.0.1
with:
version: 7.9.0
- run: |
pnpm install
cd packages/unis-core
pnpm build
cd ../unis-vite-preset
pnpm build
pnpm publish --no-git-checks --access public
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .gitignore
================================================
# Dependency directories
node_modules/
.DS_Store
================================================
FILE: .vscode/settings.json
================================================
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescriptreact]": {
"editor.formatOnSave": true
},
"[typescript]": {
"editor.formatOnSave": true
},
"[javascript]": {
"editor.formatOnSave": true
},
"[markdown]": {
"editor.formatOnSave": true
},
"editor.tabSize": 2,
"editor.insertSpaces": true,
"files.associations": {
"*.json": "jsonc"
}
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021-present anuoua
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README-zh_CN.md
================================================
<p align="center">
<img height="300" src="./assets/logo.svg">
</p>
[](https://github.com/anuoua/unis/actions/workflows/unis-core.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-dom.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-router.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-transition.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-vite-preset.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-babel-preset.yml)
# Unis
Unis 是一款新的前端框架,创新的编译策略打造的组件 API 帮助你更加轻松的创建网页 UI。
## 性能
<img height="500" src="./assets/bench.png">
## 安装
```bash
npm i @unis/core @unis/dom
```
## Vite 开发
```shell
npm i vite @unis/vite-preset -D
```
vite.config.js
```javascript
import { defineConfig } from "vite";
import { unisPreset } from "@unis/vite-preset";
export default defineConfig({
plugins: [unisPreset()],
});
```
tsconfig.json
```json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@unis/core"
}
}
```
index.html
```javascript
<html>
...
<body>
<div id="root"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>
```
index.tsx
```javascript
function App() {
return () => <div>hello</div>;
}
render(<App />, document.querySelector("#root"));
```
## 用法
Unis 并不是 React 的复刻,而是保留了 React 使用体验的全新框架,unis 的用法很简单,熟悉 React 的可以很快上手。
### 组件
在 unis 中组件是一个高阶函数。
```javascript
import { render } from "@unis/dom";
const App = () => {
return () => (
// 返回一个函数
<div>hello world</div>
);
};
render(<App />, document.querySelector("#root"));
```
### 组件状态
Unis 中的 `useState` 用法和 React 相似,但是要注意的是 unis 中 `use` 系列方法,定义类型必须为 `let` ,因为 unis 使用了 Callback Reassign 编译策略,[@callback-reassign/rollup-plugin](https://github.com/anuoua/callback-reassign) 帮我们补全了 Callback Reassign 代码。
```javascript
import { useState } from "@unis/core";
const App = () => {
let [msg, setMsg] = useState("hello");
/**
* Compile to:
*
* let [msg, setMsg] = useState('hello', ([$0, $1]) => { msg = $0; setMsg = $1 });
*/
return () => <div>{msg}</div>;
};
```
### Props
Unis 直接使用 props 会无法获取最新值,所以 unis 提供了 useProps。
```javascript
import { useProps } from "@unis/core";
const App = (p) => {
let { some } = useProps(p);
/**
* Compile to:
*
* let { some } = useProps(p, ({ some: $0 }) => { some = $0 });
*/
return () => <div>{some}</div>;
};
```
### 副作用
Unis 保留了和 React 基本一致的 `useEffect` 和 `useLayoutEffect` ,但 deps 是一个返回数组的函数。
```javascript
import { useEffect } from "@unis/core";
const App = () => {
useEffect(
() => {
// ...
return () => {
// 清理...
};
},
() => [] // deps 是一个返回数组的函数
);
return () => <div>hello</div>;
};
```
### 自定义 hook
Unis 的自定义 hook ,在有返回值的场景需要搭配 `use` 方法使用,原因则是前面提到的 Callback Reassign 编译策略。自定义 hook 的命名我们约定以小写字母 `u` 开头,目的是用于区分其他函数,同时在 IDE 的提示下更加方便的导入。
```javascript
import { use, useState } from "@unis/core";
// 创建自定义 hook 高阶函数
const uCount = () => {
let [count, setCount] = useState(0);
const add = () => setCount(count + 1);
return () => [count, add];
};
// 通过 `use` 使用 hook
function App() {
let [count, add] = use(uCount());
/**
* Compile to:
*
* let [count, add] = use(uCount(), ([$0, $1]) => { count = $0; add = $1 });
*/
return () => <div onClick={add}>{count}</div>;
}
```
## 特性
### Fragment
```javascript
import { Fragment } from "@unis/core";
function App() {
return () => (
<Fragment>
<div></div>
<span></span>
</Fragment>
);
}
```
### Portal
```javascript
import { createPortal } from "@unis/core";
function App() {
return () => createPortal(<div></div>, document.body);
}
```
### Context
```javascript
import { createContext } from "@unis/core";
import { render } from "@unis/dom";
const ThemeContext = createContext("light");
function App() {
let theme = useContext(ThemeContext);
return () => <div>{theme}</div>;
}
render(
<ThemeContext.Provider value="dark">
<App />
</ThemeContext.Provider>,
document.querySelector("#root")
);
```
## SSR 服务端渲染
服务端
```javascript
import express from "express";
import { renderToString } from "@unis/dom/server";
const app = express();
app.get("/", (req, res) => {
const SSR_CONTENT = renderToString(<div>hello world</div>);
res.send(`
<html>
<header>...</header>
<body>
<div id="root">${SSR_CONTENT}</div>
</body>
</html>
`);
});
```
客户端
```javascript
import { render } from "@unis/dom";
render(
<App />,
document.querySelector("#root"),
true // true 代表使用 hydrate (水合)进行渲染,复用 server 端的内容。
);
```
## Todo 项目
完整项目请查看
- [packages/unis-example](packages/unis-example) Todo 示例
- [stackbliz](https://stackblitz.com/edit/vitejs-vite-8hn3pz) 试用
## API
- Core
- h
- h2 (for jsx2)
- Fragment
- createPortal
- createContext
- render
- memo
- Hooks
- use
- useProps
- useState
- useReducer
- useContext
- useMemo
- useEffect
- useRef
- useId
## License
MIT @anuoua
================================================
FILE: README.md
================================================
<p align="center">
<img height="300" src="./assets/logo.svg">
</p>
[](https://github.com/anuoua/unis/actions/workflows/unis-core.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-dom.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-router.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-transition.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-vite-preset.yml) [](https://github.com/anuoua/unis/actions/workflows/unis-babel-preset.yml)
# Unis [中文](./README-zh_CN.md)
Unis is a new front-end framework. Its innovative compilation strategy and component API built help you create web UI more easily.
## Performance
<img height="500" src="./assets/bench.png">
## Installation
```bash
npm i @unis/core @unis/dom
```
## Vite Development
```shell
npm i vite @unis/vite-preset -D
```
vite.config.js
```javascript
import { defineConfig } from "vite";
import { unisPreset } from "@unis/vite-preset";
export default defineConfig({
plugins: [unisPreset()],
});
```
tsconfig.json
```json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@unis/core"
}
}
```
index.html
```javascript
<html>
...
<body>
<div id="root"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>
```
index.tsx
```javascript
function App() {
return () => <div>hello</div>;
}
render(<App />, document.querySelector("#root"));
```
## Usage
Unis is not a replica of React, but a brand new framework that retains the user experience of React. Unis is easy to use, and those who are familiar with React can quickly get started.
### Components
In Unis, the component is a higher-order function.
```javascript
import { render } from "@unis/dom";
const App = () => {
return () => (
// Returns a function
<div>hello world</div>
);
};
render(<App />, document.querySelector("#root"));
```
### Component State
The usage of `useState` in Unis is similar to React, but it should be noted that for the `use` method series in Unis, the defined type must be `let`. This is because Unis uses the Callback Reassign compilation strategy, and [@callback-reassign/rollup-plugin](https://github.com/anuoua/callback-reassign) helps us complete the Callback Reassign code.
```javascript
import { useState } from "@unis/core";
const App = () => {
let [msg, setMsg] = useState("hello");
/**
* Compile to:
*
* let [msg, setMsg] = useState('hello', ([$0, $1]) => { msg = $0; setMsg = $1 });
*/
return () => <div>{msg}</div>;
};
```
### Props
Directly using `props` in Unis will be unable to get the latest value, so Unis provides `useProps`.
```javascript
import { useProps } from "@unis/core";
const App = (p) => {
let { some } = useProps(p);
/**
* Compile to:
*
* let { some } = useProps(p, ({ some: $0 }) => { some = $0 });
*/
return () => <div>{some}</div>;
};
```
### Side Effects
Unis retains the familiar `useEffect` and `useLayoutEffect` methods from React, but the `deps` parameter is a function that returns an array.
```javascript
import { useEffect } from "@unis/core";
const App = () => {
useEffect(
() => {
// ...
return () => {
// Clean up...
};
},
() => [] // deps is a function that returns an array
);
return () => <div>hello</div>;
};
```
### Custom Hook
For Unis' custom hooks that have a return value, the `use` method should be used accordingly, due to the Callback Reassign compilation strategy mentioned earlier. We conventionally name custom hooks with a lowercase `u` at the beginning, to differentiate them from other functions and make them easy to import with IDE hints.
```javascript
import { use, useState } from "@unis/core";
// Create a higher-order function for the custom hook
const uCount = () => {
let [count, setCount] = useState(0);
const add = () => setCount(count + 1);
return () => [count, add];
};
// Use the hook through `use`
function App() {
let [count, add] = use(uCount());
/**
* Compile to:
*
* let [count, add] = use(uCount(), ([$0, $1]) => { count = $0; add = $1 });
*/
return () => <div onClick={add}>{count}</div>;
}
```
## Features
### Fragment
```javascript
import { Fragment } from "@unis/core";
function App() {
return () => (
<Fragment>
<div></div>
<span></span>
</Fragment>
);
}
```
### Portal
```javascript
import { createPortal } from "@unis/core";
function App() {
return () => createPortal(<div></div>, document.body);
}
```
### Context
```javascript
import { createContext } from "@unis/core";
import { render } from "@unis/dom";
const ThemeContext = createContext("light");
function App() {
let theme = useContext(ThemeContext);
return () => <div>{theme}</div>;
}
render(
<ThemeContext.Provider value="dark">
<App />
</ThemeContext.Provider>,
document.querySelector("#root")
);
```
## Server-Side Rendering
Server
```javascript
import express from "express";
import { renderToString } from "@unis/dom/server";
const app = express();
app.get("/", (req, res) => {
const SSR_CONTENT = renderToString(<div>hello world</div>);
res.send(`
<html>
<header>...</header>
<body>
<div id="root">${SSR_CONTENT}</div>
</body>
</html>
`);
});
```
Client
```javascript
import { render } from "@unis/dom";
render(
<App />,
document.querySelector("#root"),
true // true means using hydration to render and reuse the server-side rendered content.
);
```
## Todo Project
See complete project at
- [packages/unis-example](packages/unis-example) Todo example
- [stackbliz](https://stackblitz.com/edit/vitejs-vite-8hn3pz) Try it out
## API
- Core
- h
- h2 (for jsx2)
- Fragment
- createPortal
- createContext
- render
- memo
- Hooks
- use
- useProps
- useState
- useReducer
- useContext
- useMemo
- useEffect
- useRef
- useId
## License
MIT @anuoua
================================================
FILE: assets/logo.txt
================================================
████
██ █
█ █
███ █ █
█ ████ ██████
█ ██ ██ ██
██ ███
█ █ █ ██
██ ███
█ █
██ ████
██████████
================================================
FILE: package.json
================================================
{
"name": "unis",
"version": "0.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"workspaces": {
"packages": [
"./packages/*"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/anuoua/unis.git"
},
"author": "anuoua",
"license": "MIT",
"bugs": {
"url": "https://github.com/anuoua/unis/issues"
},
"homepage": "https://github.com/anuoua/unis#readme"
}
================================================
FILE: packages/unis-babel-preset/.gitignore
================================================
build/
================================================
FILE: packages/unis-babel-preset/README.md
================================================
# Unis Babel Preset
Unis develop preset for babel.
## Install
```shell
npm add -D @unis/babel-preset
```
## Usage
.babelrc.json or babel.config.js
```javascript
{
"presets": ["@unis/babel-preset"]
}
```
If you use @babel/preset-env, please use relatively new targets. There is a bug in the babel transformation of destructuring syntax. e.g.
```javascript
{
"presets": [
[
"@babel/preset-env",
{
targets: "> 0.25%, not dead",
}
],
"@unis/babel-preset"
]
}
```
================================================
FILE: packages/unis-babel-preset/package.json
================================================
{
"name": "@unis/babel-preset",
"version": "0.0.2",
"description": "Unis babel preset",
"main": "build/index.js",
"module": "build/index.mjs",
"types": "build/index.d.ts",
"typings": "build/index.d.ts",
"scripts": {
"build": "rimraf build && rollup --config && tsc",
"build:dev": "cross-env NODE_ENV=development pnpm build",
"test": "vitest run"
},
"exports": {
".": {
"require": "./build/index.js",
"import": "./build/index.mjs"
}
},
"keywords": [
"babel",
"preset",
"unis"
],
"files": [
"build"
],
"author": "anuoua",
"license": "MIT",
"bugs": {
"url": "https://github.com/anuoua/unis/issues"
},
"homepage": "https://github.com/anuoua/unis/tree/main/packages/unis-babel-preset",
"peerDependencies": {
"@unis/core": "workspace:^"
},
"dependencies": {
"@babel/plugin-syntax-jsx": "^7.21.4",
"@babel/plugin-transform-react-jsx": "^7.21.0",
"@callback-reassign/babel-plugin": "^0.0.1"
},
"devDependencies": {
"@babel/core": "^7.21.4",
"@rollup/plugin-node-resolve": "^13.0.6",
"@types/babel__core": "^7.20.0",
"@unis/core": "workspace:^",
"cross-env": "^7.0.3",
"esbuild": "^0.13.13",
"rimraf": "^3.0.2",
"rollup": "^2.72.0",
"rollup-plugin-esbuild": "^4.6.0",
"typescript": "^4.4.4",
"vite": "^4.2.1",
"vitest": "^0.29.8"
}
}
================================================
FILE: packages/unis-babel-preset/rollup.config.js
================================================
import { defineConfig } from "rollup";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import esbuild from "rollup-plugin-esbuild";
const configGen = (format) =>
defineConfig({
input: "src/index.ts",
external: [
/^@unis/,
"@callback-reassign/rollup-plugin",
"@babel/plugin-syntax-jsx",
"@babel/plugin-transform-react-jsx",
],
output: [
{
dir: "build",
entryFileNames: `index.${format === "esm" ? "mjs" : "js"}`,
format,
sourcemap: true,
},
],
plugins: [
nodeResolve({
modulesOnly: true,
}),
esbuild({
sourceMap: true,
minify: process.env.NODE_ENV === "development" ? false : true,
target: "esnext",
}),
],
});
const config = [configGen("cjs"), configGen("esm")];
export default config;
================================================
FILE: packages/unis-babel-preset/src/index.ts
================================================
import { unisFns } from "@unis/core";
import reassign from "@callback-reassign/babel-plugin";
// @ts-ignore
import syntaxJsx from "@babel/plugin-syntax-jsx";
// @ts-ignore
import transformReactJsx from "@babel/plugin-transform-react-jsx";
export default function unisPreset() {
return {
plugins: [
syntaxJsx,
[
transformReactJsx,
{
runtime: "automatic",
importSource: "@unis/core",
},
],
[
reassign,
{
targetFns: {
"@unis/core": unisFns,
},
},
],
],
};
}
================================================
FILE: packages/unis-babel-preset/test/index.test.ts
================================================
import { it, expect } from "vitest";
import { transform } from "@babel/core";
import unisPreset from "../src/index";
const code = `
import { useState } from "@unis/core";
let [a, seta] = useState(1);
`;
const transformed = `import { useState } from "@unis/core";
let [a, seta] = useState(1, ([$0, $1]) => {
a = $0;
seta = $1;
});`;
it("transform", () => {
const result = transform(code, {
presets: [unisPreset],
});
expect(result?.code).toBe(transformed);
});
================================================
FILE: packages/unis-babel-preset/tsconfig.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"emitDeclarationOnly": true,
"declarationMap": false,
"outDir": "build"
},
"include": ["src"]
}
================================================
FILE: packages/unis-core/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
build
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
================================================
FILE: packages/unis-core/index.d.ts
================================================
import "./jsx-runtime/jsx-runtime";
import "./jsx-runtime/jsx-dev-runtime";
export * from "./dist";
================================================
FILE: packages/unis-core/jsx-runtime/jsx-dev-runtime.d.ts
================================================
declare module "@unis/core/jsx-dev-runtime";
================================================
FILE: packages/unis-core/jsx-runtime/jsx-dev-runtime.js
================================================
const { h2, Fragment } = require("../dist/index.js");
exports.jsxDEV = h2;
exports.Fragment = Fragment;
================================================
FILE: packages/unis-core/jsx-runtime/jsx-dev-runtime.mjs
================================================
export { h2 as jsxDEV, Fragment } from "../dist/index.mjs";
================================================
FILE: packages/unis-core/jsx-runtime/jsx-runtime.d.ts
================================================
declare module "@unis/core/jsx-runtime";
================================================
FILE: packages/unis-core/jsx-runtime/jsx-runtime.js
================================================
const { h2, Fragment } = require("../dist/index.mjs");
exports.jsx = h2;
exports.jsxs = h2;
exports.Fragment = Fragment;
================================================
FILE: packages/unis-core/jsx-runtime/jsx-runtime.mjs
================================================
export { h2 as jsx, h2 as jsxs, Fragment } from "../dist/index.mjs";
================================================
FILE: packages/unis-core/package.json
================================================
{
"name": "@unis/core",
"version": "1.2.5",
"description": "Unis is a simpler and easier to use front-end framework than React",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "index.d.ts",
"typings": "index.d.ts",
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./jsx-runtime": {
"require": "./jsx-runtime/jsx-runtime.js",
"import": "./jsx-runtime/jsx-runtime.mjs"
},
"./jsx-dev-runtime": {
"require": "./jsx-runtime/jsx-dev-runtime.js",
"import": "./jsx-runtime/jsx-dev-runtime.mjs"
}
},
"scripts": {
"build": "rimraf build && rimraf dist && tsc -p tsconfig.build.json && rollup --config",
"build:dev": "cross-env NODE_ENV=development pnpm build",
"test": "vitest run --coverage",
"test:watch": "vitest -w"
},
"repository": {
"type": "git",
"url": "git+https://github.com/anuoua/unis.git"
},
"keywords": [
"frontend",
"web",
"framwork"
],
"files": [
"dist",
"jsx-runtime",
"index.d.ts"
],
"author": "anuoua",
"license": "MIT",
"bugs": {
"url": "https://github.com/anuoua/unis/issues"
},
"homepage": "https://github.com/anuoua/unis#readme",
"dependencies": {
"@types/prop-types": "^15.7.5",
"@types/scheduler": "^0.16.3",
"csstype": "^3.1.2"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.2",
"@types/jsdom": "^21.1.1",
"@vitest/coverage-c8": "^0.29.8",
"cross-env": "^7.0.3",
"esbuild": "^0.17.15",
"jsdom": "^21.1.1",
"rimraf": "^4.4.1",
"rollup": "^3.20.2",
"rollup-plugin-dts": "^5.3.0",
"rollup-plugin-esbuild": "^5.0.0",
"tslib": "^2.5.0",
"typescript": "^5.0.3",
"vite": "^4.2.1",
"vitest": "^0.29.8"
}
}
================================================
FILE: packages/unis-core/rollup.config.mjs
================================================
import dts from "rollup-plugin-dts";
import esbuild from "rollup-plugin-esbuild";
import { defineConfig } from "rollup";
import { nodeResolve } from "@rollup/plugin-node-resolve";
const configGen = (format) =>
defineConfig({
input: "src/index.ts",
output: [
{
dir: "dist",
entryFileNames: `index.${format === "esm" ? "mjs" : "js"}`,
format,
sourcemap: true,
},
],
plugins: [
nodeResolve(),
esbuild({
sourceMap: true,
target: "esnext",
}),
],
});
const dtsRollup = () =>
defineConfig({
input: "build/index.d.ts",
output: [{ file: `dist/index.d.ts`, format: "es" }],
plugins: [dts()],
});
const config = [configGen("cjs"), configGen("esm"), dtsRollup()];
export default config;
================================================
FILE: packages/unis-core/src/api/use.ts
================================================
import { getWF } from "./utils";
export function use<T extends (...args: any[]) => any>(fn: T): ReturnType<T>;
export function use<T extends (...args: any[]) => any>(
fn: T,
raFn: Function
): ReturnType<T>;
export function use<T extends (...args: any[]) => any>(fn: T, raFn?: Function) {
const workingFiber = getWF();
const effect = () => {
const result = fn(getWF());
return raFn?.(result);
};
workingFiber.stateEffects?.push(effect) ??
(workingFiber.stateEffects = [effect]);
return fn(workingFiber) as ReturnType<T>;
}
================================================
FILE: packages/unis-core/src/api/useContext.ts
================================================
import { Context } from "../context";
import { Fiber } from "../fiber";
import { use } from "./use";
export function useContext<T extends Context>(ctx: T) {
return use(contextHof(ctx), arguments[1]);
}
const contextHof = <T extends Context>(context: T) => {
const readContext = (fiber: Fiber): T["initial"] => {
const { dependencyList = [] } = fiber.reconcileState!;
const result = [...dependencyList]
.reverse()
.find((d) => d.context === context);
if (result) {
if (fiber.dependencies) {
!fiber.dependencies.includes(result) && fiber.dependencies.push(result);
} else {
fiber.dependencies = [result];
}
return result.value;
} else {
return context.initial;
}
};
return (WF: Fiber) => readContext(WF);
};
================================================
FILE: packages/unis-core/src/api/useEffect.ts
================================================
import { Effect, EFFECT_TYPE, getWF } from "./utils";
export const useEffect = (cb: Effect, depsFn?: () => any[]) => {
const workingFiber = getWF();
cb.depsFn = depsFn;
if (!cb.type) cb.type = EFFECT_TYPE.TICK;
workingFiber.effects?.push(cb) ?? (workingFiber.effects = [cb]);
};
================================================
FILE: packages/unis-core/src/api/useId.ts
================================================
import { Fiber } from "../fiber";
import { getWF } from "./utils";
import { use } from "./use";
import { generateId } from "../utils";
export const idHof = () => {
let workingFiber = getWF();
if (!workingFiber.id) {
workingFiber.id = generateId();
}
return (WF: Fiber) => WF.id;
};
export function useId() {
return use(idHof());
}
================================================
FILE: packages/unis-core/src/api/useLayoutEffect.ts
================================================
import { useEffect } from "./useEffect";
import { Effect, EFFECT_TYPE } from "./utils";
export const useLayoutEffect = (cb: Effect, depsFn?: () => any[]) => {
cb.type = EFFECT_TYPE.LAYOUT;
return useEffect(cb, depsFn);
};
================================================
FILE: packages/unis-core/src/api/useMemo.ts
================================================
import { use } from "./use";
import { Fiber, MemorizeState } from "../fiber";
import { arraysEqual } from "../utils";
import { addDispatchEffect, linkMemorizeState } from "./useReducer";
export const memoHof = <T extends unknown>(
handler: () => T,
depsFn?: () => any[]
) => {
let freshFiber: Fiber | undefined;
let freshDeps: any[];
let freshMemorizeState: MemorizeState | undefined;
let memorizeState: MemorizeState = {
value: undefined,
deps: undefined!,
};
const effect = () => {
memorizeState = freshMemorizeState!;
memorizeState.deps = freshDeps;
freshFiber = undefined;
freshMemorizeState = undefined;
};
return (WF: Fiber) => {
freshFiber = WF;
freshDeps = depsFn?.() ?? freshDeps;
addDispatchEffect(freshFiber, effect);
freshMemorizeState = {
value:
depsFn && arraysEqual(memorizeState.deps, freshDeps)
? memorizeState.value
: handler(),
deps: memorizeState?.deps,
};
linkMemorizeState(freshFiber, freshMemorizeState);
return freshMemorizeState.value as T;
};
};
export function useMemo<T extends unknown>(
handler: () => T,
depsFn?: () => any[]
) {
return use(memoHof(handler, depsFn), arguments[2]);
}
================================================
FILE: packages/unis-core/src/api/useProps.ts
================================================
import { Fiber } from "../fiber";
import { use } from "./use";
export function useProps<T>(p: T) {
return use(propsHof(p), arguments[1]);
}
export const propsHof = <T>(props: T) => {
return (WF: Fiber) => WF.props as T;
};
================================================
FILE: packages/unis-core/src/api/useReducer.ts
================================================
import { Effect, markFiber } from "./utils";
import { use } from "./use";
import { Fiber, findRoot, findRuntime, MemorizeState, TokTik } from "../fiber";
import { readyForWork } from "../reconcile";
export type Reducer<T, T2> = (state: T, action: T2) => T;
const readyList: (() => Fiber)[] = [];
const triggerReconcile = () => {
const fibers = new Set(readyList.map((getFiber) => getFiber()));
fibers.forEach(markFiber);
// multiple app trigger same time
const rootFibers = new Set(Array.from(fibers).map(findRoot));
rootFibers.forEach((fiber) => readyForWork(fiber));
readyList.length = 0;
};
export const reducerHof = <T extends any, T2 extends any>(
reducerFn: Reducer<T, T2>,
initial: T
) => {
let currentFiber: Fiber | undefined; // do not getWF here, workingFiber should be assigned in effect.
let freshFiber: Fiber | undefined;
let freshMemorizeState: MemorizeState | undefined;
let toktik: TokTik | undefined;
let memorizeState: MemorizeState = {
value: undefined,
dispatchValue: initial,
deps: [initial],
};
const dispatch = (action: T2) => {
if (!currentFiber) return console.warn("Component is not created");
if (currentFiber.isDestroyed)
return console.warn("Component has been destroyed");
const newState = reducerFn(memorizeState.value, action);
if (Object.is(newState, memorizeState.value)) return;
memorizeState.dispatchValue = newState;
memorizeState.deps = [newState];
if (freshFiber) {
toktik!.clearTikTaskQueue();
}
readyList.push(() => currentFiber!);
if (readyList.length === 1) {
toktik!.addTok(triggerReconcile, true);
}
};
const effect: Effect = () => {
currentFiber = freshFiber!;
memorizeState = freshMemorizeState!;
if (!toktik) toktik = findRuntime(currentFiber).toktik;
freshFiber = undefined;
freshMemorizeState = undefined;
};
return (WF: Fiber) => {
freshFiber = WF;
addDispatchEffect(freshFiber, effect);
freshMemorizeState = {
value:
memorizeState.deps.length > 0
? memorizeState.dispatchValue
: memorizeState.value,
deps: [],
};
linkMemorizeState(freshFiber, freshMemorizeState);
return [freshMemorizeState.value, dispatch] as const;
};
};
export function useReducer<T, T2>(reducerFn: Reducer<T, T2>, initial: T) {
return use(reducerHof(reducerFn, initial), arguments[2]);
}
export const addDispatchEffect = (freshFiber: Fiber, effect: Effect) => {
freshFiber.reconcileState!.dispatchEffectList?.push(effect) ??
(freshFiber.reconcileState!.dispatchEffectList = [effect]);
};
export const linkMemorizeState = (
freshFiber: Fiber,
freshMemorizeState: MemorizeState
) => {
if (freshFiber.memorizeState) {
const first = freshFiber.memorizeState.next;
freshFiber.memorizeState.next = freshMemorizeState;
freshMemorizeState.next = first;
} else {
freshFiber.memorizeState = freshMemorizeState;
freshMemorizeState.next = freshMemorizeState;
}
};
export const cutMemorizeState = (fiber: Fiber) => {
const first = fiber.memorizeState?.next;
fiber.memorizeState && (fiber.memorizeState.next = undefined);
fiber.memorizeState = first;
};
================================================
FILE: packages/unis-core/src/api/useRef.ts
================================================
export interface Ref<T> {
current: T;
}
export function useRef<T>(): Ref<T | undefined>;
export function useRef<T>(value: T): Ref<T>;
export function useRef<T>(value?: T) {
return { current: value };
}
================================================
FILE: packages/unis-core/src/api/useState.ts
================================================
import { use } from "./use";
import { reducerHof } from "./useReducer";
export const stateHof = <T extends any>(initial: T) => {
return reducerHof<T, T>((preState, action) => action, initial);
};
export function useState<T = undefined>(): [
T | undefined,
(value: T | undefined) => void
];
export function useState<T>(initial: T): [T, (value: T) => void];
export function useState<T>(initial?: T) {
return use(stateHof(initial), arguments[1]);
}
================================================
FILE: packages/unis-core/src/api/utils.ts
================================================
import { Fiber, FLAG, mergeFlag } from "../fiber";
import { getWorkingFiber } from "../reconcile";
import { arraysEqual } from "../utils";
export enum EFFECT_TYPE {
LAYOUT = "layout",
TICK = "tick",
}
export type Effect = (() => (() => void) | void) & {
type?: EFFECT_TYPE;
clear?: (() => void) | void;
depsFn?: () => any;
deps?: any;
};
export const getWF = (): Fiber | never => {
const workingFiber = getWorkingFiber();
if (workingFiber) {
return workingFiber;
} else {
throw Error("Do not call use function outside of component");
}
};
export const markFiber = (workingFiber: Fiber) => {
workingFiber.flag = mergeFlag(workingFiber.flag, FLAG.UPDATE);
let iFiber: Fiber | undefined = workingFiber;
while ((iFiber = iFiber.parent)) {
if (iFiber.childFlag) break;
iFiber.childFlag = mergeFlag(iFiber.childFlag, FLAG.UPDATE);
}
};
export const runStateEffects = (fiber: Fiber) => {
for (const effect of fiber.stateEffects ?? []) {
effect();
}
};
export const effectDepsEqual = (effect: Effect) => {
const deps = effect.depsFn?.();
const equal = arraysEqual(deps, effect.deps);
effect.deps = deps;
return equal;
};
export const clearEffects = (effects?: Effect[]) => {
if (!effects) return;
for (const effect of effects) {
effect.clear?.();
}
};
export const runEffects = (effects?: Effect[]) => {
if (!effects) return;
for (const effect of effects) {
effect.clear = effect();
}
};
export const clearAndRunEffects = (effects?: Effect[]) => {
if (!effects) return;
for (const effect of effects) {
if (effectDepsEqual(effect)) continue;
effect.clear?.();
effect.clear = effect();
}
};
================================================
FILE: packages/unis-core/src/commit.ts
================================================
import { clearEffects } from "./api/utils";
import {
Fiber,
findEls,
FLAG,
getContainerElFiber,
graft,
isComponent,
isHostElement,
matchFlag,
isText,
isElement,
ReconcileState,
isPortal,
Operator,
} from "./fiber";
export const commitDeletion = (fiber: Fiber, operator: Operator) => {
let iFiber: Fiber | undefined = fiber;
while (iFiber) {
if (isHostElement(iFiber)) {
iFiber.props.ref && (iFiber.props.ref.current = undefined);
}
if (isComponent(iFiber)) {
clearEffects(iFiber.effects);
}
/**
* remove input element may trigger blur sync event,
* so isDestroyed must be true before remove to prevent dispatch in useReducer.
*/
iFiber.isDestroyed = true;
if (isPortal(iFiber)) {
iFiber.child && operator.remove(iFiber.child);
}
iFiber.dependencies = undefined;
iFiber.reconcileState = undefined;
if (iFiber.child) {
iFiber = iFiber.child;
continue;
} else if (iFiber === fiber) {
iFiber = undefined;
continue;
}
while (iFiber) {
if (iFiber.sibling) {
iFiber = iFiber.sibling;
break;
}
if (iFiber.parent !== fiber) {
iFiber = iFiber.parent;
} else {
iFiber = undefined;
break;
}
}
}
operator.remove(fiber);
};
export const commitUpdate = (fiber: Fiber, operator: Operator) => {
if (isText(fiber)) operator.updateTextProperties(fiber);
if (isHostElement(fiber)) operator.updateElementProperties(fiber);
};
export const commitInsert = (fiber: Fiber, operator: Operator) => {
const container = getContainerElFiber(fiber)!;
const insertElements = isElement(fiber)
? [fiber.el!]
: findEls(
matchFlag(fiber.commitFlag, FLAG.REUSE) ? fiber.alternate! : fiber
);
const insertTarget = isPortal(container)
? null
: fiber.preElFiber
? operator.nextSibling(fiber.preElFiber)
: operator.firstChild(container);
for (const insertElement of insertElements) {
operator.insertBefore(container, insertElement, insertTarget);
}
};
export const commit = (reconcileState: ReconcileState) => {
const { operator } = reconcileState.rootWorkingFiber.runtime!;
for (const fiber of reconcileState.commitList) {
if (matchFlag(fiber.commitFlag, FLAG.DELETE)) {
commitDeletion(fiber.alternate!, operator);
continue;
}
if (matchFlag(fiber.commitFlag, FLAG.UPDATE)) {
commitUpdate(fiber, operator);
}
if (matchFlag(fiber.commitFlag, FLAG.INSERT)) {
commitInsert(fiber, operator);
}
if (matchFlag(fiber.commitFlag, FLAG.REUSE)) {
graft(fiber, fiber.alternate!);
}
fiber.preElFiber = undefined;
fiber.alternate = undefined;
fiber.commitFlag = undefined;
}
};
================================================
FILE: packages/unis-core/src/context.ts
================================================
import { useProps } from "./api/useProps";
import { useContext } from "./api/useContext";
import { PROVIDER, Fiber } from "./fiber";
export interface Context<T = any> {
Provider: (props: { value: T; children: any }) => JSX.Element;
Consumer: (props: { children: (value: T) => JSX.Element }) => JSX.Element;
initial: T;
}
export interface Dependency<T = any> {
context: Context<T>;
value: T;
}
const providerContextMap = new WeakMap<Function, Context>();
export const createDependency = (fiber: Fiber) => {
return {
context: providerContextMap.get(fiber.tag as Function)!,
value: fiber.props.value,
};
};
export const findDependency = (fiber: Fiber, contextFiber: Fiber) =>
fiber.dependencies?.find(
(dependency) =>
dependency.context ===
providerContextMap.get(contextFiber.tag as Function)
);
export function createContext<T>(initial: T): Context<T>;
export function createContext<T>(initial: T) {
const Provider = (props: { value: T; children: any }) => props.children;
Provider.take = {
type: PROVIDER,
};
const Consumer = (props: { children: (value: T) => JSX.Element }) => {
let p = useProps(
props,
// @ts-ignore
($) => (p = $)
);
let state = useContext(
context,
// @ts-ignore
($) => (state = $)
);
return () => p.children(state);
};
const context: Context<T> = {
Provider,
Consumer,
initial,
};
providerContextMap.set(Provider, context);
return context;
}
================================================
FILE: packages/unis-core/src/createTokTik.ts
================================================
export type Task = Function & { isTok?: any };
export const createTokTik = (options: {
nextTick: (cb: VoidFunction, pending: boolean) => void;
now: () => number;
interval?: number;
}) => {
const { nextTick, now, interval = 4 } = options;
const timeSlicing = !!interval;
let lastTime: number = 0;
let looping = false;
const tikQueue: Task[] = [];
const tokQueue: Task[] = [];
const next = () => tikQueue[0] ?? tokQueue[0];
const pick = () => tikQueue.shift() ?? tokQueue.shift();
const loop = (task: Task): void => {
looping = true;
runTask(task);
const nextTask = next();
if (nextTask) {
if (shouldYield() || nextTask.isTok) {
nextTick(() => loop(pick()!), nextTask.isTok);
} else {
loop(pick()!);
}
return;
}
looping = false;
};
const runTask = timeSlicing
? (task: Task) => {
lastTime = now();
return task();
}
: (task: Task) => task();
const addTok = (task: Task, pending = false) => {
task.isTok = true;
looping
? tokQueue.push(task)
: pending
? nextTick(() => loop(task), pending)
: loop(task);
};
const addTik = (task: Task) => {
looping && tikQueue.push(task);
};
const clearTikTaskQueue = () => (tikQueue.length = 0);
const shouldYield = timeSlicing
? () => now() - lastTime > interval
: () => false;
return {
addTok,
addTik,
clearTikTaskQueue,
shouldYield,
};
};
================================================
FILE: packages/unis-core/src/diff.ts
================================================
import {
clearFlag,
Fiber,
FLAG,
isElement,
matchFlag,
isPortal,
isSame,
mergeFlag,
isComponent,
ReconcileState,
isText,
findToRoot,
} from "./fiber";
import {
classes,
isNullish,
isStr,
isEvent,
keys,
styleStr,
svgKey,
} from "./utils";
export type AttrDiff = [string, any, any][];
export const attrDiff = (
newFiber: Record<string, any>,
oldFiber: Record<string, any>,
onlyEvent = false
) => {
const diff: AttrDiff = [];
const newProps = newFiber.props;
const oldProps = oldFiber.props;
const getRealAttr = (attr: string) => {
if (attr === "className") return "class";
if (attr === "htmlFor") return "for";
if (newFiber.isSvg) return svgKey(attr);
return attr.toLowerCase();
};
const getRealValue = (newValue: any, key: string) => {
if (isNullish(newValue)) return;
switch (key) {
case "className":
return isStr(newValue) ? newValue : classes(newValue);
case "style":
return isStr(newValue)
? newValue
: styleStr(newValue as Partial<CSSStyleDeclaration>);
default:
return newValue;
}
};
for (const key of keys({ ...newProps, ...oldProps })) {
if (onlyEvent && !isEvent(key)) continue;
if (["xmlns", "children"].includes(key)) continue;
const newValue = newProps[key];
const oldValue = oldProps[key];
const realNewValue = getRealValue(newValue, key);
const realOldValue = getRealValue(oldValue, key);
if (
!isNullish(newValue) &&
!isNullish(oldValue) &&
realNewValue === realOldValue
)
continue;
diff.push([getRealAttr(key), realNewValue, realOldValue]);
}
return diff;
};
export const clone = (newFiber: Fiber, oldFiber: Fiber, commitFlag?: FLAG) =>
Object.assign(
{
...newFiber,
commitFlag,
alternate: oldFiber,
},
isComponent(newFiber)
? {
renderFn: oldFiber.renderFn,
rendered: oldFiber.rendered,
stateEffects: oldFiber.stateEffects,
effects: oldFiber.effects,
id: oldFiber.id,
}
: isElement(newFiber)
? { el: oldFiber.el, isSvg: oldFiber.isSvg }
: isPortal(newFiber)
? { to: oldFiber.to }
: undefined
);
export const reuse = (newFiber: Fiber, oldFiber: Fiber, commitFlag?: FLAG) => ({
...newFiber,
commitFlag,
alternate: oldFiber,
});
export const del = (oldFiber: Fiber): Fiber => ({
commitFlag: FLAG.DELETE,
alternate: oldFiber,
});
export const create = (
newFiber: Fiber,
parentFiber: Fiber,
hydrate = false
) => {
const retFiber = {
...newFiber,
commitFlag: matchFlag(parentFiber.commitFlag, FLAG.CREATE)
? FLAG.CREATE
: FLAG.CREATE | FLAG.INSERT,
} as Fiber;
if (isElement(newFiber)) {
retFiber.isSvg = newFiber.tag === "svg" || parentFiber.isSvg;
if (!hydrate) {
retFiber.attrDiff = isText(retFiber)
? undefined
: attrDiff(retFiber, { props: {} });
}
}
if (isPortal(newFiber)) {
retFiber.commitFlag = undefined;
}
return retFiber;
};
export const keyIndexMapGen = (
children: Fiber[],
start: number,
end: number
) => {
const map: any = {};
for (let i = start; i <= end; i++) {
const key = children[i].props?.key;
if (key !== undefined) map[key] = i;
}
return map;
};
const determineCommitFlag = (
parentFiber: Fiber,
newFiber: Fiber,
oldFiber: Fiber,
flag?: FLAG
) => {
/**
* the nearest parent component fiber
*/
const nearestComponent = isComponent(parentFiber)
? parentFiber
: findToRoot(parentFiber, (fiber) => isComponent(fiber));
/**
* when nearest parent component fiber with FLAG.UPDATE commitFlag, it should be FLAG.UPDATE.
*/
let commitFlag =
!matchFlag(nearestComponent?.commitFlag, FLAG.UPDATE) &&
parentFiber.alternate!.childFlag
? !oldFiber.childFlag && !oldFiber.flag
? FLAG.REUSE
: oldFiber.flag
: FLAG.UPDATE;
/**
* when memo fiber compare result is true, it should be FLAG.REUSE.
*/
if (
isComponent(oldFiber) &&
!oldFiber.childFlag &&
!oldFiber.flag &&
(oldFiber.tag as Function & { compare?: Function }).compare?.(
newFiber.props,
oldFiber.props
)
) {
commitFlag = mergeFlag(commitFlag, FLAG.REUSE);
}
if (isElement(newFiber) && matchFlag(commitFlag, FLAG.UPDATE)) {
let diff = attrDiff(newFiber, oldFiber);
if (diff.length) {
newFiber.attrDiff = diff;
} else {
commitFlag = clearFlag(commitFlag, FLAG.UPDATE);
}
}
flag && (commitFlag = mergeFlag(commitFlag, flag));
/**
* portal don't need commitFlag
*/
if (isPortal(newFiber)) {
commitFlag = undefined;
}
if (matchFlag(commitFlag, FLAG.REUSE)) {
commitFlag = clearFlag(commitFlag, FLAG.UPDATE);
}
return commitFlag;
};
const getSameNewFiber = (
parentFiber: Fiber,
newFiber: Fiber,
oldFiber: Fiber,
flag?: FLAG
) => {
const commitFlag = determineCommitFlag(parentFiber, newFiber, oldFiber, flag);
return matchFlag(commitFlag, FLAG.REUSE)
? reuse(newFiber, oldFiber, commitFlag)
: clone(newFiber, oldFiber, commitFlag);
};
export const diff = (
parentFiber: Fiber,
oldChildren: Fiber[] = [],
newChildren: Fiber[] = []
) => {
const { reconcileState } = parentFiber as { reconcileState: ReconcileState };
let cloneChildren: Fiber[] = [];
let newStartIndex = 0;
let newEndIndex = newChildren.length - 1;
let oldStartIndex = 0;
let oldEndIndex = oldChildren.length - 1;
let newStartFiber = newChildren[newStartIndex];
let newEndFiber = newChildren[newEndIndex];
let oldStartFiber = oldChildren[oldStartIndex];
let oldEndFiber = oldChildren[oldEndIndex];
let preStartFiber: Fiber | undefined;
let preEndFiber: Fiber | undefined;
const deletion = (fiber: Fiber) => {
reconcileState.commitList.push(del(fiber));
};
const forward = () => {
if (preStartFiber) preStartFiber.sibling = newStartFiber;
newStartFiber.parent = parentFiber;
newStartFiber.index = newStartIndex;
newStartFiber.reconcileState = reconcileState;
preStartFiber = newStartFiber;
cloneChildren[newStartIndex] = newStartFiber;
newStartFiber = newChildren[++newStartIndex];
};
const forwardEnd = () => {
if (preEndFiber) newEndFiber.sibling = preEndFiber;
newEndFiber.parent = parentFiber;
newEndFiber.index = newEndIndex;
newEndFiber.reconcileState = reconcileState;
preEndFiber = newEndFiber;
cloneChildren[newEndIndex] = newEndFiber;
newEndFiber = newChildren[--newEndIndex];
};
const oldForward = () => {
oldStartFiber = oldChildren[++oldStartIndex];
};
const oldForwardEnd = () => {
oldEndFiber = oldChildren[--oldEndIndex];
};
let keyIndexMap: any;
while (newStartIndex <= newEndIndex && oldStartIndex <= oldEndIndex) {
if (oldStartFiber === undefined) {
oldForward();
} else if (oldEndFiber === undefined) {
oldForwardEnd();
} else if (isSame(newStartFiber, oldStartFiber)) {
newStartFiber = getSameNewFiber(
parentFiber,
newStartFiber,
oldStartFiber
);
forward();
oldForward();
} else if (isSame(newEndFiber, oldEndFiber)) {
newEndFiber = getSameNewFiber(parentFiber, newEndFiber, oldEndFiber);
forwardEnd();
oldForwardEnd();
} else if (isSame(newStartFiber, oldEndFiber)) {
newStartFiber = getSameNewFiber(
parentFiber,
newStartFiber,
oldEndFiber,
FLAG.INSERT
);
forward();
oldForwardEnd();
} else if (isSame(newEndFiber, oldStartFiber)) {
newEndFiber = getSameNewFiber(
parentFiber,
newEndFiber,
oldStartFiber,
FLAG.INSERT
);
forwardEnd();
oldForward();
} else {
if (!keyIndexMap) {
keyIndexMap = keyIndexMapGen(oldChildren, oldStartIndex, oldEndIndex);
}
const index = keyIndexMap[newStartFiber.props.key];
if (isNaN(index)) {
newStartFiber = create(newStartFiber, parentFiber);
} else {
const targetFiber = oldChildren[index];
if (isSame(newStartFiber, targetFiber)) {
newStartFiber = getSameNewFiber(
parentFiber,
newStartFiber,
targetFiber,
FLAG.INSERT
);
oldChildren[index] = undefined as unknown as Fiber;
} else {
newStartFiber = create(newStartFiber, parentFiber);
}
}
forward();
}
}
if (oldStartIndex > oldEndIndex) {
newChildren.slice(newStartIndex, newEndIndex + 1).forEach((fiber) => {
newStartFiber = create(
newStartFiber,
parentFiber,
reconcileState.hydrate
);
forward();
});
} else if (newStartIndex > newEndIndex) {
oldChildren
.slice(oldStartIndex, oldEndIndex + 1)
.forEach((fiber) => fiber && deletion(fiber));
}
if (preStartFiber && preEndFiber) preStartFiber.sibling = preEndFiber;
parentFiber.child = cloneChildren[0];
parentFiber.children = cloneChildren;
};
================================================
FILE: packages/unis-core/src/fiber.ts
================================================
import { Effect } from "./api/utils";
import { Dependency } from "./context";
import { AttrDiff } from "./diff";
import { isFun, isNullish } from "./utils";
export interface ReconcileState {
rootWorkingFiber: Fiber;
dispatchEffectList: Effect[];
commitList: Fiber[];
dependencyList: Dependency[];
workingPreElFiber?: Fiber;
hydrate: boolean;
hydrateEl?: FiberEl;
}
export enum FLAG {
CREATE = 1 << 1,
INSERT = 1 << 2,
UPDATE = 1 << 3,
DELETE = 1 << 4,
REUSE = 1 << 5,
}
export type FlagName = "flag" | "childFlag" | "commitFlag";
export const mergeFlag = (a: FLAG | undefined, b: FLAG) =>
isNullish(a) ? b : a | b;
export const clearFlag = (a: FLAG | undefined, b: FLAG) =>
isNullish(a) ? a : a & ~b;
export const matchFlag = (a: FLAG | undefined, b: FLAG) =>
isNullish(a) ? false : a & b;
export type FiberEl = unknown;
export type FiberType =
| string
| Function
| Symbol
| ((...p: any[]) => () => any);
export interface MemorizeState {
value: any;
dispatchValue?: any;
deps: any[];
next?: MemorizeState;
}
export interface TokTik {
addTok: (task: Function, pending?: boolean) => void;
addTik: (task: Function) => void;
clearTikTaskQueue: () => void;
shouldYield: () => boolean;
}
export interface Operator {
// for reuse element when hydrate
nextElement(el: FiberEl): FiberEl | null;
// for reuse element when hydrate
matchElement(fiber: Fiber, el: FiberEl): boolean;
createElement(fiber: Fiber): FiberEl;
remove(fiber: Fiber): void;
insertBefore(
containerFiber: Fiber,
insertElement: FiberEl,
targetElement: FiberEl | null
): void;
firstChild(fiber: Fiber): FiberEl | null;
nextSibling(fiber: Fiber): FiberEl | null;
updateTextProperties(fiber: Fiber): void;
updateElementProperties(fiber: Fiber): void;
}
export interface Runtime {
toktik: TokTik;
operator: Operator;
}
export interface Fiber {
id?: string;
parent?: Fiber;
child?: Fiber;
sibling?: Fiber;
index?: number;
to?: FiberEl;
el?: FiberEl;
preElFiber?: Fiber;
isSvg?: boolean;
isDestroyed?: boolean;
props?: any;
compare?: Function;
attrDiff?: AttrDiff;
alternate?: Fiber;
tag?: string | Function;
type?: Symbol;
renderFn?: Function;
rendered?: any;
flag?: FLAG;
childFlag?: FLAG;
commitFlag?: FLAG;
children?: Fiber[];
stateEffects?: Effect[];
effects?: Effect[];
dependencies?: Dependency[];
reconcileState?: ReconcileState;
memorizeState?: MemorizeState;
runtime?: Runtime;
}
export const createFiber = (options: Partial<Fiber> = {}) =>
Object.assign<Fiber, Fiber>(
{
id: undefined,
parent: undefined,
child: undefined,
sibling: undefined,
index: undefined,
to: undefined,
el: undefined,
preElFiber: undefined,
isSvg: undefined,
isDestroyed: undefined,
props: undefined,
compare: undefined,
attrDiff: undefined,
alternate: undefined,
tag: undefined,
type: undefined,
renderFn: undefined,
rendered: undefined,
flag: undefined,
childFlag: undefined,
commitFlag: undefined,
children: undefined,
stateEffects: undefined,
effects: undefined,
dependencies: undefined,
reconcileState: undefined,
memorizeState: undefined,
},
options
);
export const TEXT = Symbol("$$Text");
export const ELEMENT = Symbol("$$Element");
export const PORTAL = Symbol("$$Portal");
export const PROVIDER = Symbol("$$Provider");
export const COMPONENT = Symbol("$$Component");
export const isText = (fiber: Fiber) => fiber.type === TEXT;
export const isHostElement = (fiber: Fiber) => fiber.type === ELEMENT;
export const isElement = (fiber: Fiber) =>
isHostElement(fiber) || isText(fiber);
export const isPortal = (fiber: Fiber) => fiber.type === PORTAL;
export const isProvider = (fiber: Fiber) => fiber.type === PROVIDER;
export const isCustomComponent = (fiber: Fiber) => fiber.type === COMPONENT;
export const isComponent = (fiber: Fiber) => isFun(fiber.tag);
export const isSame = (fiber1?: Fiber, fiber2?: Fiber) =>
fiber1 &&
fiber2 &&
fiber1.tag === fiber2.tag &&
fiber1.props?.key === fiber2.props?.key;
export interface WalkHook {
enter?: (currentFiber: Fiber, skipChild: boolean) => any;
down?: (currentFiber: Fiber, nextFiber: Fiber) => any;
sibling?: (currentFiber: Fiber, nextFiber?: Fiber) => any;
up?: (currentFiber: Fiber, nextFiber?: Fiber) => any;
return?: (currentFiber?: Fiber) => any;
}
export type WalkHookKeys = keyof WalkHook;
export type WalkHookList = {
[K in keyof WalkHook]: WalkHook[K][];
};
export const createNext = () => {
const walkHooks: WalkHookList = {};
const addHook = (walkHook: WalkHook) => {
Object.entries(walkHook).forEach(([key, value]) => {
const list = walkHooks[key as WalkHookKeys];
list ? list.push(value) : (walkHooks[key as WalkHookKeys] = [value]);
});
};
const runWalkHooks = <T extends WalkHookKeys>(
key: T,
...args: Parameters<Required<WalkHook>[T]>
) => {
return walkHooks[key]?.map((hook) => hook!(...(args as [any, any])));
};
const next = (fiber: Fiber, skipChild = false): Fiber | undefined => {
if (runWalkHooks("enter", fiber, skipChild)?.includes(false)) return;
const { child } = fiber;
let nextFiber: Fiber | undefined = fiber;
if (child && !skipChild) {
runWalkHooks("down", nextFiber, child);
nextFiber = child;
} else {
while (nextFiber) {
const { sibling, parent } = nextFiber as Fiber;
if (sibling) {
runWalkHooks("sibling", nextFiber, sibling);
nextFiber = sibling;
break;
}
if (runWalkHooks("up", nextFiber, parent)?.includes(false)) {
nextFiber = undefined;
break;
}
nextFiber = parent;
}
}
runWalkHooks("return", nextFiber);
return nextFiber;
};
return [next, addHook] as const;
};
export const graft = (newFiber: Fiber, oldFiber: Fiber) => {
const parent = newFiber.parent!;
const parentChildren = parent.children!;
const index = newFiber.index!;
const preIndex = index - 1;
if (index === 0) parent.child = oldFiber;
if (preIndex >= 0) parentChildren[preIndex].sibling = oldFiber;
parentChildren[index] = oldFiber;
oldFiber.sibling = newFiber.sibling;
oldFiber.parent = parent;
};
export const findEls = (fiber: Fiber, findInPortal = false) => {
const els: FiberEl[] = [];
isElement(fiber)
? els.push(fiber.el!)
: isPortal(fiber) && !findInPortal
? false
: fiber.children?.forEach((child) => {
els.push(...findEls(child, findInPortal));
});
return els;
};
export const findLastElFiber = (fiber: Fiber): Fiber | undefined => {
if (isElement(fiber)) {
return fiber;
} else if (isPortal(fiber)) {
return undefined;
} else {
for (let i = 0; i < (fiber.children?.length ?? 0); i++) {
return findLastElFiber(fiber.children!.at(-(i + 1))!);
}
}
};
export type ContainerElement = Exclude<FiberEl, Text>;
export const getContainerElFiber = (
fiber: Fiber | undefined
): Fiber | undefined => {
while ((fiber = fiber?.parent)) {
if (isPortal(fiber) || isElement(fiber)) return fiber;
}
};
export const findToRoot = (
fiber: Fiber | undefined,
cb: (fiber: Fiber) => boolean
): Fiber | undefined => {
while ((fiber = fiber?.parent)) {
if (cb(fiber)) return fiber;
}
};
export const findRoot = (fiber: Fiber) =>
findToRoot(fiber, (fiber) => !fiber.parent)!;
export const findRuntime = (fiber: Fiber) =>
fiber.reconcileState?.rootWorkingFiber
? fiber.reconcileState.rootWorkingFiber.runtime!
: findRoot(fiber).runtime!;
================================================
FILE: packages/unis-core/src/h.ts
================================================
import { isNum, isStr, keys, toArray } from "./utils";
import {
COMPONENT,
createFiber,
ELEMENT,
Fiber,
FiberEl,
PORTAL,
TEXT,
} from "./fiber";
export const h = (tag: any, props: any, ...children: any[]) => {
props = { ...props };
if (children.length === 1) props.children = children[0];
if (children.length > 1) props.children = children;
return createFiber({
tag,
type: isStr(tag) ? ELEMENT : COMPONENT,
props,
...tag.take,
});
};
export const h2 = (tag: any, props: any, key?: string | number) => {
if (key !== undefined) props.key = key;
return createFiber({
tag,
type: isStr(tag) ? ELEMENT : COMPONENT,
props,
...tag.take,
});
};
export const formatChildren = (children: any) => {
const formatedChildren: Fiber[] = [];
for (const child of toArray(children)) {
if ([null, false, true, undefined].includes(child)) {
continue;
} else {
Array.isArray(child)
? formatedChildren.push(...formatChildren(child))
: formatedChildren.push(
isStr(child) || isNum(child)
? createFiber({
type: TEXT,
props: { nodeValue: child },
})
: child
);
}
}
return formatedChildren;
};
export const createRoot = (element: any, container: FiberEl): Fiber => {
return {
tag: (container as any).tagName.toLocaleLowerCase(),
type: ELEMENT,
el: container,
index: 0,
props: {
children: toArray(element),
},
};
};
export const createPortal = (child: JSX.Element, container: FiberEl) =>
createFiber({
type: PORTAL,
props: { children: child },
to: container,
});
const defaultCompare = (newProps: any = {}, oldProps: any = {}) => {
const newKeys = keys(newProps);
const oldKeys = keys(oldProps);
if (newKeys.length !== oldKeys.length) return false;
return newKeys.every((key) => Object.is(newProps[key], oldProps[key]));
};
export const memo = <
T extends ((props: any) => JSX.Element) & { compare?: Function }
>(
child: T,
compare: Function = defaultCompare
) => {
child.compare = compare;
return child;
};
export const cloneElement = (
element: Fiber,
props = {},
...children: JSX.Element[]
) => h(element.tag, { ...props, ...props }, ...children);
export const createElement = h;
export const Fragment = (props: any) => props.children;
export const FGMT = Fragment;
================================================
FILE: packages/unis-core/src/index.ts
================================================
export * from "./h";
export * from "./fiber";
export * from "./context";
export * from "./reconcile";
export * from "./utils";
export * from "./diff";
export * from "./createTokTik";
export * from "./api/utils";
export * from "./api/use";
export * from "./api/useState";
export * from "./api/useContext";
export * from "./api/useProps";
export * from "./api/useReducer";
export * from "./api/useMemo";
export * from "./api/useEffect";
export * from "./api/useLayoutEffect";
export * from "./api/useId";
export * from "./api/useRef";
export type * from "../types/jsx";
export const unisFns = {
use: 1,
useState: 1,
useProps: 1,
useContext: 1,
useReducer: 2,
useMemo: 2,
};
================================================
FILE: packages/unis-core/src/reconcile.ts
================================================
import {
clearAndRunEffects,
clearEffects,
Effect,
EFFECT_TYPE,
effectDepsEqual,
runEffects,
runStateEffects,
} from "./api/utils";
import {
createNext,
Fiber,
FLAG,
ReconcileState,
isComponent,
createFiber,
matchFlag,
findRuntime,
isElement,
} from "./fiber";
import { commit } from "./commit";
import { preElFiberWalkHook } from "./reconcileWalkHooks/preElFiber";
import { effectWalkHook } from "./reconcileWalkHooks/effect";
import { formatChildren } from "./h";
import { isFun } from "./utils";
import { diff } from "./diff";
import { contextWalkHook } from "./reconcileWalkHooks/context";
import { cutMemorizeState } from "./api/useReducer";
let workingFiber: Fiber | undefined;
export const getWorkingFiber = () => workingFiber;
export const setWorkingFiber = (fiber: Fiber | undefined) =>
(workingFiber = fiber);
// reconcile walker
const [next, addHook] = createNext();
// preEl
addHook(preElFiberWalkHook);
// effect
addHook(effectWalkHook);
// context
addHook(contextWalkHook);
export const readyForWork = (rootCurrentFiber: Fiber, hydrate = false) => {
rootCurrentFiber.runtime!.toktik.addTok(() =>
performWork(rootCurrentFiber, hydrate)
);
};
const performWork = (rootCurrentFiber: Fiber, hydrate: boolean) => {
const rootWorkingFiber = createFiber({
index: rootCurrentFiber.index,
tag: rootCurrentFiber.tag,
type: rootCurrentFiber.type,
props: rootCurrentFiber.props,
alternate: rootCurrentFiber,
el: rootCurrentFiber.el,
runtime: rootCurrentFiber.runtime,
});
const initialReconcileState: ReconcileState = {
rootWorkingFiber,
dispatchEffectList: [],
commitList: [],
dependencyList: [],
workingPreElFiber: undefined,
hydrate,
hydrateEl: rootCurrentFiber.el,
};
rootWorkingFiber.reconcileState = initialReconcileState;
setWorkingFiber(rootWorkingFiber);
tickWork(rootWorkingFiber!);
};
const tickWork = (workingFiber: Fiber) => {
const { toktik } = findRuntime(workingFiber);
let iFiber: Fiber | undefined = workingFiber;
// work loop
while (iFiber && !toktik.shouldYield()) {
const isReuse = !!matchFlag(iFiber.commitFlag, FLAG.REUSE);
{
!isReuse && update(iFiber);
!isReuse && isElement(iFiber) && compose(iFiber);
complete(iFiber);
}
iFiber = next(iFiber, isReuse);
setWorkingFiber(iFiber);
}
if (iFiber) {
toktik.addTik(() => {
setWorkingFiber(iFiber);
tickWork(iFiber!);
});
} else {
const { reconcileState } = workingFiber;
// switch dispatch bind fiber
runEffects(reconcileState!.dispatchEffectList);
// commit
commit(reconcileState!);
// call component effects
callComponentEffects(reconcileState!);
// clear reconcileState
for (const prop of Object.keys(reconcileState!)) {
delete reconcileState![prop as keyof ReconcileState];
}
}
};
const callComponentEffects = (reconcileState: ReconcileState) => {
const { commitList, rootWorkingFiber } = reconcileState!;
const { toktik } = rootWorkingFiber.runtime!;
const triggeredLayoutEffects: Effect[] = [];
const tickEffects: Effect[] = [];
// clear and run layoutEffects
for (const fiber of commitList) {
if (!isComponent(fiber)) continue;
for (const effect of fiber.effects ?? []) {
if (effect.type === EFFECT_TYPE.TICK) {
tickEffects.push(effect);
} else {
const equal = effectDepsEqual(effect);
if (!equal) {
triggeredLayoutEffects.push(effect);
clearEffects([effect]);
}
}
}
}
// run triggered layout effects
runEffects(triggeredLayoutEffects);
// clear and run tick effects
toktik.addTik(() => clearAndRunEffects(tickEffects));
};
const update = (fiber: Fiber) => {
if (isComponent(fiber)) {
updateComponent(fiber);
} else {
updateHost(fiber);
}
};
const updateHost = (fiber: Fiber) => {
diff(fiber, fiber.alternate?.children, formatChildren(fiber.props.children));
};
const updateComponent = (fiber: Fiber) => {
if (!fiber.renderFn) {
fiber.renderFn = fiber.tag as Function;
let rendered = fiber.renderFn(fiber.props);
if (isFun(rendered)) {
fiber.renderFn = rendered;
rendered = fiber.renderFn!();
}
fiber.rendered = formatChildren(rendered);
} else {
runStateEffects(fiber);
if (matchFlag(fiber.commitFlag, FLAG.UPDATE)) {
fiber.rendered = formatChildren(fiber.renderFn(fiber.props));
} else {
/**
* this condition, means `fiber.alternate` is on childFlag marked chain, and `fiber.commitFlag` is undefined.
* diff will keep going on.
*/
}
}
cutMemorizeState(fiber);
diff(fiber, fiber.alternate?.children, fiber.rendered);
};
const compose = (fiber: Fiber) => {
const { hydrate, hydrateEl } = fiber.reconcileState!;
const { operator } = findRuntime(fiber);
if (hydrate && hydrateEl) {
if (!operator.matchElement(fiber, hydrateEl))
throw new Error("Hydrate failed!");
fiber.el = hydrateEl;
fiber.reconcileState!.hydrateEl = operator.nextElement(hydrateEl);
} else if (matchFlag(fiber.commitFlag, FLAG.CREATE) && !hydrate) {
fiber.el = operator.createElement(fiber);
fiber.attrDiff?.length && operator.updateElementProperties(fiber);
let iFiber: Fiber | undefined = fiber;
while ((iFiber = iFiber.parent)) {
if (!matchFlag(iFiber.commitFlag, FLAG.CREATE)) break;
if (isElement(iFiber)) {
operator.insertBefore(iFiber, fiber.el, null);
break;
}
}
}
};
const complete = (fiber: Fiber) => {
!fiber.commitFlag && (fiber.alternate = undefined);
};
================================================
FILE: packages/unis-core/src/reconcileWalkHooks/context.ts
================================================
import { createDependency, findDependency } from "../context";
import { createNext, Fiber, isProvider, WalkHook } from "../fiber";
import { markFiber } from "../api/utils";
export const contextWalkHook: WalkHook = {
down: (from: Fiber, to?: Fiber) => {
isProvider(from) &&
from.reconcileState!.dependencyList.push(createDependency(from));
},
up: (from: Fiber, to?: Fiber) => {
to && isProvider(to) && from.reconcileState!.dependencyList.pop();
},
enter: (enter: Fiber, skipChild: boolean) => {
if (
enter.alternate &&
isProvider(enter.alternate) &&
!Object.is(enter.alternate.props.value, enter.props.value)
) {
let alternate = enter.alternate;
let iFiber: Fiber | undefined = alternate;
const [next, addHook] = createNext();
addHook({ up: (from, to) => to !== alternate });
do {
findDependency(iFiber, enter) && markFiber(iFiber);
iFiber = next(
iFiber,
iFiber !== alternate && isProvider(iFiber) && iFiber.tag === enter.tag
);
} while (iFiber);
}
},
};
================================================
FILE: packages/unis-core/src/reconcileWalkHooks/effect.ts
================================================
import { Fiber, WalkHook } from "../fiber";
export const pushEffect = (fiber: Fiber) => {
fiber.reconcileState!.commitList.push(fiber);
};
export const effectWalkHook: WalkHook = {
up: (from, to) => {
!from.child && from.commitFlag && pushEffect(from);
to?.commitFlag && pushEffect(to);
},
sibling: (from) => {
!from.child && from.commitFlag && pushEffect(from);
},
};
================================================
FILE: packages/unis-core/src/reconcileWalkHooks/preElFiber.ts
================================================
import {
Fiber,
findLastElFiber,
FLAG,
isElement,
isPortal,
matchFlag,
WalkHook,
} from "../fiber";
const setWorkingPreElFiber = (
fiber: Fiber,
workingPreElFiber: Fiber | undefined
) => {
if (fiber.reconcileState)
fiber.reconcileState.workingPreElFiber = workingPreElFiber;
};
const setReuseFiberPreElFiber = (fiber: Fiber) => {
if (!matchFlag(fiber.commitFlag, FLAG.REUSE)) return;
const lastElFiber = findLastElFiber(fiber.alternate!);
lastElFiber && setWorkingPreElFiber(fiber, lastElFiber);
};
export const preElFiberWalkHook: WalkHook = {
down: (from: Fiber, to?: Fiber) => {
isElement(from) && setWorkingPreElFiber(from, undefined);
isPortal(from) && setWorkingPreElFiber(from, undefined);
},
up: (from: Fiber, to?: Fiber) => {
if (from && !from.child) {
isElement(from) && setWorkingPreElFiber(from, from);
}
if (to) {
isElement(to) && setWorkingPreElFiber(from, to);
isPortal(to) && setWorkingPreElFiber(from, to.preElFiber);
}
setReuseFiberPreElFiber(from);
},
sibling: (from: Fiber, to?: Fiber) => {
if (matchFlag(from.commitFlag, FLAG.REUSE)) {
setReuseFiberPreElFiber(from);
} else {
isElement(from) && setWorkingPreElFiber(from, from);
}
},
return: (retn?: Fiber) => {
if (retn && matchFlag(retn.commitFlag, FLAG.INSERT))
retn.preElFiber = retn.reconcileState!.workingPreElFiber;
},
};
================================================
FILE: packages/unis-core/src/svg.ts
================================================
// kebab svg attr keys
export const displayAttrs = [
"baselineShift",
"alignmentBaseline",
"clip",
"clipPath",
"clipRule",
"color",
"colorInterpolation",
"colorInterpolationFilters",
"colorProfile",
"colorRendering",
"cursor",
"direction",
"display",
"dominantBaseline",
"enableBackground",
"fill",
"fillOpacity",
"fillRule",
"filter",
"floodColor",
"floodOpacity",
"fontFamily",
"fontSize",
"fontSizeAdjust",
"fontStretch",
"fontStyle",
"fontVariant",
"fontWeight",
"glyphOrientationHorizontal",
"glyphOrientationVertical",
"imageRendering",
"kerning",
"letterSpacing",
"lightingColor",
"markerEnd",
"markerMid",
"markerStart",
"mask",
"opacity",
"overflow",
"pointerEvents",
"shapeRendering",
"stopColor",
"stopOpacity",
"stroke",
"strokeDasharray",
"strokeDashoffset",
"strokeLinecap",
"strokeLinejoin",
"strokeMiterlimit",
"strokeOpacity",
"strokeWidth",
"textAnchor",
"transform",
"textDecoration",
"textRendering",
"unicodeBidi",
"vectorEffect",
"visibility",
"wordSpacing",
"writingMode",
];
================================================
FILE: packages/unis-core/src/utils.ts
================================================
import type { CSArray, CSObject } from "../types/jsx";
import { displayAttrs } from "./svg";
export const keys = Object.keys;
export const type = (a: any) =>
Object.prototype.toString.bind(a)().slice(8, -1);
export const isFun = (a: any): a is Function => typeof a === "function";
export const isStr = (a: any): a is string => typeof a === "string";
export const isNum = (a: any): a is number => typeof a === "number";
export const isBool = (a: any): a is boolean => typeof a === "boolean";
export const isSymbol = (a: any): a is boolean => typeof a === "symbol";
export const isArray = Array.isArray;
export const isObject = (a: any): a is object => type(a) === "Object";
export const isNullish = (a: any): a is null | undefined => a == null;
export const isEvent = (a: string) => a.startsWith("on");
export const getEventName = (event: string) => {
const [, eventName, capture] = event.match(/^on(.*)(Capture)?$/)!;
return [eventName.toLowerCase(), !!capture] as const;
};
export const camel2kebab = (text: string) =>
text.replace(/([A-Z])/g, "-$1").toLowerCase();
export const toArray = <T extends any>(a: T) => (Array.isArray(a) ? a : [a]);
export const arraysEqual = (a: any, b: any) => {
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (!Object.is(a[i], b[i])) return false;
}
return true;
};
export const styleStr = (style: Partial<CSSStyleDeclaration>) =>
keys(style)
.map(
(key) => `${camel2kebab(key)}: ${style[key as keyof CSSStyleDeclaration]}`
)
.join("; ") + ";";
export const svgKey = (key: string) => {
for (const str of ["xmlns", "xml", "xlink"]) {
if (key.startsWith(str)) return key.toLowerCase().replace(str, `${str}:`);
}
return displayAttrs.includes(key) ? camel2kebab(key) : key;
};
export const classes = (cs: CSArray | CSObject): string => {
const objectClasses = (objcs: Record<string, any>) =>
keys(objcs)
.reduce((pre, cur) => pre + " " + (objcs[cur] ? cur : ""), "")
.trim();
const arrayClasses = (arrcs: CSArray) =>
arrcs
.reduce(
(pre: string, cur) =>
pre +
" " +
`${
isNum(cur) || isStr(cur)
? cur
: isObject(cur)
? objectClasses(cur)
: isArray(cur)
? classes(cur)
: ""
}`,
""
)
.trim();
return isArray(cs) ? arrayClasses(cs) : objectClasses(cs);
};
let overflow = "";
let count = 0;
export const generateId = () => {
if (count === Number.MAX_SAFE_INTEGER) {
overflow += count.toString(32);
count = 0;
}
return `${overflow}${(count++).toString(32)}`;
};
================================================
FILE: packages/unis-core/test/utils.test.ts
================================================
/**
* @vitest-environment jsdom
*/
import { expect, it } from "vitest";
import { classes, svgKey, styleStr } from "../src/utils";
it("classes", () => {
expect(classes(["a", "b", 1, ["c", { d: true }]])).toBe("a b 1 c d");
expect(classes({ a: true, b: undefined, c: null })).toBe("a");
expect(classes({ a: false, b: true, c: null })).toBe("b");
});
it("realSVGAttr", () => {
expect(svgKey("glyphOrientationVertical")).toBe("glyph-orientation-vertical");
});
it("style2String", () => {
expect(styleStr({ background: "yellow", fontSize: "14px" })).toBe(
"background: yellow; font-size: 14px;"
);
});
================================================
FILE: packages/unis-core/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"include": ["src"],
"exclude": ["**/*.test.*"]
}
================================================
FILE: packages/unis-core/tsconfig.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "build"
},
"include": ["src", "test", "jsx-runtime", "types/jsx.d.ts"]
}
================================================
FILE: packages/unis-core/types/jsx.d.ts
================================================
// Note: this file is auto concatenated to the end of the bundled d.ts during
// build.
// This code is based on react definition in DefinitelyTyped published under the MIT license.
// Repository: https://github.com/DefinitelyTyped/DefinitelyTyped
// Path in the repository: types/react/index.d.ts
//
// Copyrights of original definition are:
// AssureSign <http://www.assuresign.com>
// Microsoft <https://microsoft.com>
// John Reilly <https://github.com/johnnyreilly>
// Benoit Benezech <https://github.com/bbenezech>
// Patricio Zavolinsky <https://github.com/pzavolinsky>
// Digiguru <https://github.com/digiguru>
// Eric Anderson <https://github.com/ericanderson>
// Dovydas Navickas <https://github.com/DovydasNavickas>
// Josh Rutherford <https://github.com/theruther4d>
// Guilherme Hübner <https://github.com/guilhermehubner>
// Ferdy Budhidharma <https://github.com/ferdaber>
// Johann Rakotoharisoa <https://github.com/jrakotoharisoa>
// Olivier Pascal <https://github.com/pascaloliv>
// Martin Hochel <https://github.com/hotell>
// Frank Li <https://github.com/franklixuefei>
// Jessica Franco <https://github.com/Jessidhia>
// Saransh Kataria <https://github.com/saranshkataria>
// Kanitkorn Sujautra <https://github.com/lukyth>
// Sebastian Silbermann <https://github.com/eps1lon>
/**
*
The MIT License (MIT)
Copyright (c) 2018-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
*
*/
import * as CSS from "csstype";
export interface FiberNode {
tag?: string | Function;
type?: Symbol;
props?: any;
}
export interface CSSProperties
extends CSS.Properties<string | number>,
CSS.PropertiesHyphen<string | number> {
/**
* The index signature was removed to enable closed typing for style
* using CSSType. You're able to use type assertion or module augmentation
* to add properties or an index signature of your own.
*
* For examples and more information, visit:
* https://github.com/frenic/csstype#what-should-i-do-when-i-get-type-errors
*/
}
type Booleanish = boolean | "true" | "false";
type Numberish = number | string;
// All the WAI-ARIA 1.1 attributes from https://www.w3.org/TR/wai-aria-1.1/
interface AriaAttributes {
/** Identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application. */
"aria-activedescendant"?: string;
/** Indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the aria-relevant attribute. */
"aria-atomic"?: Booleanish;
/**
* Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for an input and specifies how predictions would be
* presented if they are made.
*/
"aria-autocomplete"?: "none" | "inline" | "list" | "both";
/** Indicates an element is being modified and that assistive technologies MAY want to wait until the modifications are complete before exposing them to the user. */
"aria-busy"?: Booleanish;
/**
* Indicates the current "checked" state of checkboxes, radio buttons, and other widgets.
* @see aria-pressed @see aria-selected.
*/
"aria-checked"?: Booleanish | "mixed";
/**
* Defines the total number of columns in a table, grid, or treegrid.
* @see aria-colindex.
*/
"aria-colcount"?: Numberish;
/**
* Defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid.
* @see aria-colcount @see aria-colspan.
*/
"aria-colindex"?: Numberish;
/**
* Defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid.
* @see aria-colindex @see aria-rowspan.
*/
"aria-colspan"?: Numberish;
/**
* Identifies the element (or elements) whose contents or presence are controlled by the current element.
* @see aria-owns.
*/
"aria-controls"?: string;
/** Indicates the element that represents the current item within a container or set of related elements. */
"aria-current"?: Booleanish | "page" | "step" | "location" | "date" | "time";
/**
* Identifies the element (or elements) that describes the object.
* @see aria-labelledby
*/
"aria-describedby"?: string;
/**
* Identifies the element that provides a detailed, extended description for the object.
* @see aria-describedby.
*/
"aria-details"?: string;
/**
* Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.
* @see aria-hidden @see aria-readonly.
*/
"aria-disabled"?: Booleanish;
/**
* Indicates what functions can be performed when a dragged object is released on the drop target.
* @deprecated in ARIA 1.1
*/
"aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup";
/**
* Identifies the element that provides an error message for the object.
* @see aria-invalid @see aria-describedby.
*/
"aria-errormessage"?: string;
/** Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed. */
"aria-expanded"?: Booleanish;
/**
* Identifies the next element (or elements) in an alternate reading order of content which, at the user's discretion,
* allows assistive technology to override the general default of reading in document source order.
*/
"aria-flowto"?: string;
/**
* Indicates an element's "grabbed" state in a drag-and-drop operation.
* @deprecated in ARIA 1.1
*/
"aria-grabbed"?: Booleanish;
/** Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element. */
"aria-haspopup"?:
| Booleanish
| "menu"
| "listbox"
| "tree"
| "grid"
| "dialog";
/**
* Indicates whether the element is exposed to an accessibility API.
* @see aria-disabled.
*/
"aria-hidden"?: Booleanish;
/**
* Indicates the entered value does not conform to the format expected by the application.
* @see aria-errormessage.
*/
"aria-invalid"?: Booleanish | "grammar" | "spelling";
/** Indicates keyboard shortcuts that an author has implemented to activate or give focus to an element. */
"aria-keyshortcuts"?: string;
/**
* Defines a string value that labels the current element.
* @see aria-labelledby.
*/
"aria-label"?: string;
/**
* Identifies the element (or elements) that labels the current element.
* @see aria-describedby.
*/
"aria-labelledby"?: string;
/** Defines the hierarchical level of an element within a structure. */
"aria-level"?: Numberish;
/** Indicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region. */
"aria-live"?: "off" | "assertive" | "polite";
/** Indicates whether an element is modal when displayed. */
"aria-modal"?: Booleanish;
/** Indicates whether a text box accepts multiple lines of input or only a single line. */
"aria-multiline"?: Booleanish;
/** Indicates that the user may select more than one item from the current selectable descendants. */
"aria-multiselectable"?: Booleanish;
/** Indicates whether the element's orientation is horizontal, vertical, or unknown/ambiguous. */
"aria-orientation"?: "horizontal" | "vertical";
/**
* Identifies an element (or elements) in order to define a visual, functional, or contextual parent/child relationship
* between DOM elements where the DOM hierarchy cannot be used to represent the relationship.
* @see aria-controls.
*/
"aria-owns"?: string;
/**
* Defines a short hint (a word or short phrase) intended to aid the user with data entry when the control has no value.
* A hint could be a sample value or a brief description of the expected format.
*/
"aria-placeholder"?: string;
/**
* Defines an element's number or position in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.
* @see aria-setsize.
*/
"aria-posinset"?: Numberish;
/**
* Indicates the current "pressed" state of toggle buttons.
* @see aria-checked @see aria-selected.
*/
"aria-pressed"?: Booleanish | "mixed";
/**
* Indicates that the element is not editable, but is otherwise operable.
* @see aria-disabled.
*/
"aria-readonly"?: Booleanish;
/**
* Indicates what notifications the user agent will trigger when the accessibility tree within a live region is modified.
* @see aria-atomic.
*/
"aria-relevant"?:
| "additions"
| "additions text"
| "all"
| "removals"
| "text";
/** Indicates that user input is required on the element before a form may be submitted. */
"aria-required"?: Booleanish;
/** Defines a human-readable, author-localized description for the role of an element. */
"aria-roledescription"?: string;
/**
* Defines the total number of rows in a table, grid, or treegrid.
* @see aria-rowindex.
*/
"aria-rowcount"?: Numberish;
/**
* Defines an element's row index or position with respect to the total number of rows within a table, grid, or treegrid.
* @see aria-rowcount @see aria-rowspan.
*/
"aria-rowindex"?: Numberish;
/**
* Defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.
* @see aria-rowindex @see aria-colspan.
*/
"aria-rowspan"?: Numberish;
/**
* Indicates the current "selected" state of various widgets.
* @see aria-checked @see aria-pressed.
*/
"aria-selected"?: Booleanish;
/**
* Defines the number of items in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.
* @see aria-posinset.
*/
"aria-setsize"?: Numberish;
/** Indicates if items in a table or grid are sorted in ascending or descending order. */
"aria-sort"?: "none" | "ascending" | "descending" | "other";
/** Defines the maximum allowed value for a range widget. */
"aria-valuemax"?: Numberish;
/** Defines the minimum allowed value for a range widget. */
"aria-valuemin"?: Numberish;
/**
* Defines the current value for a range widget.
* @see aria-valuetext.
*/
"aria-valuenow"?: Numberish;
/** Defines the human readable text alternative of aria-valuenow for a range widget. */
"aria-valuetext"?: string;
}
// All the WAI-ARIA 1.1 role attribute values from https://www.w3.org/TR/wai-aria-1.1/#role_definitions
type AriaRole =
| "alert"
| "alertdialog"
| "application"
| "article"
| "banner"
| "button"
| "cell"
| "checkbox"
| "columnheader"
| "combobox"
| "complementary"
| "contentinfo"
| "definition"
| "dialog"
| "directory"
| "document"
| "feed"
| "figure"
| "form"
| "grid"
| "gridcell"
| "group"
| "heading"
| "img"
| "link"
| "list"
| "listbox"
| "listitem"
| "log"
| "main"
| "marquee"
| "math"
| "menu"
| "menubar"
| "menuitem"
| "menuitemcheckbox"
| "menuitemradio"
| "navigation"
| "none"
| "note"
| "option"
| "presentation"
| "progressbar"
| "radio"
| "radiogroup"
| "region"
| "row"
| "rowgroup"
| "rowheader"
| "scrollbar"
| "search"
| "searchbox"
| "separator"
| "slider"
| "spinbutton"
| "status"
| "switch"
| "tab"
| "table"
| "tablist"
| "tabpanel"
| "term"
| "textbox"
| "timer"
| "toolbar"
| "tooltip"
| "tree"
| "treegrid"
| "treeitem"
| (string & {});
// Vue's style normalization supports nested arrays
export type StyleValue = string | CSSProperties | Array<StyleValue>;
export type CSValue = string | number | boolean | undefined | null;
export type CSObject = Record<string, CSValue>;
export type CSArray = (CSValue | CSObject | CSArray)[];
export interface HTMLAttributes extends AriaAttributes, EventHandlers<Events> {
// Standard HTML Attributes
accessKey?: string | undefined;
className?: string | CSObject | CSArray | undefined;
contentEditable?: Booleanish | "inherit" | undefined;
contextMenu?: string | undefined;
dir?: string | undefined;
draggable?: Booleanish | undefined;
hidden?: boolean | undefined;
id?: string | undefined;
lang?: string | undefined;
placeholder?: string | undefined;
slot?: string | undefined;
spellCheck?: Booleanish | undefined;
style?: CSSProperties | string | undefined;
tabIndex?: number | undefined;
title?: string | undefined;
translate?: "yes" | "no" | undefined;
// Unknown
radioGroup?: string | undefined; // <command>, <menuitem>
// WAI-ARIA
role?: AriaRole | undefined;
// RDFa Attributes
about?: string | undefined;
datatype?: string | undefined;
inlist?: any;
prefix?: string | undefined;
property?: string | undefined;
resource?: string | undefined;
typeof?: string | undefined;
vocab?: string | undefined;
// Non-standard Attributes
autoCapitalize?: string | undefined;
autoCorrect?: string | undefined;
autoSave?: string | undefined;
color?: string | undefined;
itemProp?: string | undefined;
itemScope?: boolean | undefined;
itemType?: string | undefined;
itemID?: string | undefined;
itemRef?: string | undefined;
results?: number | undefined;
security?: string | undefined;
unselectable?: "on" | "off" | undefined;
// Living Standard
/**
* Hints at the type of data that might be entered by the user while editing the element or its contents
* @see https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute
*/
inputMode?:
| "none"
| "text"
| "tel"
| "url"
| "email"
| "numeric"
| "decimal"
| "search"
| undefined;
/**
* Specify that a standard HTML element should behave like a defined custom built-in element
* @see https://html.spec.whatwg.org/multipage/custom-elements.html#attr-is
*/
is?: string | undefined;
}
type HTMLAttributeReferrerPolicy =
| ""
| "no-referrer"
| "no-referrer-when-downgrade"
| "origin"
| "origin-when-cross-origin"
| "same-origin"
| "strict-origin"
| "strict-origin-when-cross-origin"
| "unsafe-url";
type HTMLAttributeAnchorTarget =
| "_self"
| "_blank"
| "_parent"
| "_top"
| (string & {});
export interface AnchorHTMLAttributes extends HTMLAttributes {
download?: any;
href?: string | undefined;
hrefLang?: string | undefined;
media?: string | undefined;
ping?: string | undefined;
rel?: string | undefined;
target?: HTMLAttributeAnchorTarget | undefined;
type?: string | undefined;
referrerPolicy?: HTMLAttributeReferrerPolicy | undefined;
}
export interface AudioHTMLAttributes extends MediaHTMLAttributes {}
export interface AreaHTMLAttributes extends HTMLAttributes {
alt?: string | undefined;
coords?: string | undefined;
download?: any;
href?: string | undefined;
hrefLang?: string | undefined;
media?: string | undefined;
referrerPolicy?: HTMLAttributeReferrerPolicy | undefined;
rel?: string | undefined;
shape?: string | undefined;
target?: string | undefined;
}
export interface BaseHTMLAttributes extends HTMLAttributes {
href?: string | undefined;
target?: string | undefined;
}
export interface BlockquoteHTMLAttributes extends HTMLAttributes {
cite?: string | undefined;
}
export interface ButtonHTMLAttributes extends HTMLAttributes {
autoFocus?: boolean | undefined;
disabled?: boolean | undefined;
form?: string | undefined;
formAction?: string | undefined;
formEncType?: string | undefined;
formMethod?: string | undefined;
formNoValidate?: boolean | undefined;
formTarget?: string | undefined;
name?: string | undefined;
type?: "submit" | "reset" | "button" | undefined;
value?: string | ReadonlyArray<string> | number | undefined;
}
export interface CanvasHTMLAttributes extends HTMLAttributes {
height?: number | string | undefined;
width?: number | string | undefined;
}
export interface ColHTMLAttributes extends HTMLAttributes {
span?: number | undefined;
width?: number | string | undefined;
}
export interface ColgroupHTMLAttributes extends HTMLAttributes {
span?: number | undefined;
}
export interface DataHTMLAttributes extends HTMLAttributes {
value?: string | ReadonlyArray<string> | number | undefined;
}
export interface DetailsHTMLAttributes extends HTMLAttributes {
open?: boolean | undefined;
}
export interface DelHTMLAttributes extends HTMLAttributes {
cite?: string | undefined;
dateTime?: string | undefined;
}
export interface DialogHTMLAttributes extends HTMLAttributes {
open?: boolean | undefined;
}
export interface EmbedHTMLAttributes extends HTMLAttributes {
height?: number | string | undefined;
src?: string | undefined;
type?: string | undefined;
width?: number | string | undefined;
}
export interface FieldsetHTMLAttributes extends HTMLAttributes {
disabled?: boolean | undefined;
form?: string | undefined;
name?: string | undefined;
}
export interface FormHTMLAttributes extends HTMLAttributes {
acceptCharset?: string | undefined;
action?: string | undefined;
autoComplete?: string | undefined;
encType?: string | undefined;
method?: string | undefined;
name?: string | undefined;
noValidate?: boolean | undefined;
target?: string | undefined;
}
export interface HtmlHTMLAttributes extends HTMLAttributes {
manifest?: string | undefined;
}
export interface IframeHTMLAttributes extends HTMLAttributes {
allow?: string | undefined;
allowFullScreen?: boolean | undefined;
allowTransparency?: boolean | undefined;
/** @deprecated */
frameBorder?: number | string | undefined;
height?: number | string | undefined;
loading?: "eager" | "lazy" | undefined;
/** @deprecated */
marginHeight?: number | undefined;
/** @deprecated */
marginWidth?: number | undefined;
name?: string | undefined;
referrerPolicy?: HTMLAttributeReferrerPolicy | undefined;
sandbox?: string | undefined;
/** @deprecated */
scrolling?: string | undefined;
seamless?: boolean | undefined;
src?: string | undefined;
srcDoc?: string | undefined;
width?: number | string | undefined;
}
export interface ImgHTMLAttributes extends HTMLAttributes {
alt?: string | undefined;
crossOrigin?: "anonymous" | "use-credentials" | "" | undefined;
decoding?: "async" | "auto" | "sync" | undefined;
height?: number | string | undefined;
loading?: "eager" | "lazy" | undefined;
referrerPolicy?: HTMLAttributeReferrerPolicy | undefined;
sizes?: string | undefined;
src?: string | undefined;
srcSet?: string | undefined;
useMap?: string | undefined;
width?: number | string | undefined;
}
export interface InsHTMLAttributes extends HTMLAttributes {
cite?: string | undefined;
dateTime?: string | undefined;
}
type HTMLInputTypeAttribute =
| "button"
| "checkbox"
| "color"
| "date"
| "datetime-local"
| "email"
| "file"
| "hidden"
| "image"
| "month"
| "number"
| "password"
| "radio"
| "range"
| "reset"
| "search"
| "submit"
| "tel"
| "text"
| "time"
| "url"
| "week"
| (string & {});
export interface InputHTMLAttributes extends HTMLAttributes {
accept?: string | undefined;
alt?: string | undefined;
autoComplete?: string | undefined;
autoFocus?: boolean | undefined;
capture?: boolean | "user" | "environment" | undefined; // https://www.w3.org/TR/html-media-capture/#the-capture-attribute
checked?: boolean | undefined;
crossOrigin?: string | undefined;
disabled?: boolean | undefined;
enterKeyHint?:
| "enter"
| "done"
| "go"
| "next"
| "previous"
| "search"
| "send"
| undefined;
form?: string | undefined;
formAction?: string | undefined;
formEncType?: string | undefined;
formMethod?: string | undefined;
formNoValidate?: boolean | undefined;
formTarget?: string | undefined;
height?: number | string | undefined;
list?: string | undefined;
max?: number | string | undefined;
maxLength?: number | undefined;
min?: number | string | undefined;
minLength?: number | undefined;
multiple?: boolean | undefined;
name?: string | undefined;
pattern?: string | undefined;
placeholder?: string | undefined;
readOnly?: boolean | undefined;
required?: boolean | undefined;
size?: number | undefined;
src?: string | undefined;
step?: number | string | undefined;
type?: HTMLInputTypeAttribute | undefined;
value?: string | ReadonlyArray<string> | number | undefined;
width?: number | string | undefined;
}
export interface KeygenHTMLAttributes extends HTMLAttributes {
autoFocus?: boolean | undefined;
challenge?: string | undefined;
disabled?: boolean | undefined;
form?: string | undefined;
keyType?: string | undefined;
keyParams?: string | undefined;
name?: string | undefined;
}
export interface LabelHTMLAttributes extends HTMLAttributes {
form?: string | undefined;
htmlFor?: string | undefined;
}
export interface LiHTMLAttributes extends HTMLAttributes {
value?: string | ReadonlyArray<string> | number | undefined;
}
export interface LinkHTMLAttributes extends HTMLAttributes {
as?: string | undefined;
crossOrigin?: string | undefined;
href?: string | undefined;
hrefLang?: string | undefined;
integrity?: string | undefined;
media?: string | undefined;
imageSrcSet?: string | undefined;
referrerPolicy?: HTMLAttributeReferrerPolicy | undefined;
rel?: string | undefined;
sizes?: string | undefined;
type?: string | undefined;
charSet?: string | undefined;
}
export interface MapHTMLAttributes extends HTMLAttributes {
name?: string | undefined;
}
export interface MenuHTMLAttributes extends HTMLAttributes {
type?: string | undefined;
}
export interface MediaHTMLAttributes extends HTMLAttributes {
autoPlay?: boolean | undefined;
controls?: boolean | undefined;
controlsList?: string | undefined;
crossOrigin?: string | undefined;
loop?: boolean | undefined;
mediaGroup?: string | undefined;
muted?: boolean | undefined;
playsInline?: boolean | undefined;
preload?: string | undefined;
src?: string | undefined;
}
export interface MetaHTMLAttributes extends HTMLAttributes {
charSet?: string | undefined;
content?: string | undefined;
httpEquiv?: string | undefined;
name?: string | undefined;
media?: string | undefined;
}
export interface MeterHTMLAttributes extends HTMLAttributes {
form?: string | undefined;
high?: number | undefined;
low?: number | undefined;
max?: number | string | undefined;
min?: number | string | undefined;
optimum?: number | undefined;
value?: string | ReadonlyArray<string> | number | undefined;
}
export interface QuoteHTMLAttributes extends HTMLAttributes {
cite?: string | undefined;
}
export interface ObjectHTMLAttributes extends HTMLAttributes {
classID?: string | undefined;
data?: string | undefined;
form?: string | undefined;
height?: number | string | undefined;
name?: string | undefined;
type?: string | undefined;
useMap?: string | undefined;
width?: number | string | undefined;
wmode?: string | undefined;
}
export interface OlHTMLAttributes extends HTMLAttributes {
reversed?: boolean | undefined;
start?: number | undefined;
type?: "1" | "a" | "A" | "i" | "I" | undefined;
}
export interface OptgroupHTMLAttributes extends HTMLAttributes {
disabled?: boolean | undefined;
label?: string | undefined;
}
export interface OptionHTMLAttributes extends HTMLAttributes {
disabled?: boolean | undefined;
label?: string | undefined;
selected?: boolean | undefined;
value?: string | ReadonlyArray<string> | number | undefined;
}
export interface OutputHTMLAttributes extends HTMLAttributes {
form?: string | undefined;
htmlFor?: string | undefined;
name?: string | undefined;
}
export interface ParamHTMLAttributes extends HTMLAttributes {
name?: string | undefined;
value?: string | ReadonlyArray<string> | number | undefined;
}
export interface ProgressHTMLAttributes extends HTMLAttributes {
max?: number | string | undefined;
value?: string | ReadonlyArray<string> | number | undefined;
}
export interface SlotHTMLAttributes extends HTMLAttributes {
name?: string | undefined;
}
export interface ScriptHTMLAttributes extends HTMLAttributes {
async?: boolean | undefined;
/** @deprecated */
charSet?: string | undefined;
crossOrigin?: string | undefined;
defer?: boolean | undefined;
integrity?: string | undefined;
noModule?: boolean | undefined;
nonce?: string | undefined;
referrerPolicy?: HTMLAttributeReferrerPolicy | undefined;
src?: string | undefined;
type?: string | undefined;
}
export interface SelectHTMLAttributes extends HTMLAttributes {
autoComplete?: string | undefined;
autoFocus?: boolean | undefined;
disabled?: boolean | undefined;
form?: string | undefined;
multiple?: boolean | undefined;
name?: string | undefined;
required?: boolean | undefined;
size?: number | undefined;
value?: string | ReadonlyArray<string> | number | undefined;
}
export interface SourceHTMLAttributes extends HTMLAttributes {
height?: number | string | undefined;
media?: string | undefined;
sizes?: string | undefined;
src?: string | undefined;
srcSet?: string | undefined;
type?: string | undefined;
width?: number | string | undefined;
}
export interface StyleHTMLAttributes extends HTMLAttributes {
media?: string | undefined;
nonce?: string | undefined;
scoped?: boolean | undefined;
type?: string | undefined;
}
export interface TableHTMLAttributes extends HTMLAttributes {
cellPadding?: number | string | undefined;
cellSpacing?: number | string | undefined;
summary?: string | undefined;
width?: number | string | undefined;
}
export interface TextareaHTMLAttributes extends HTMLAttributes {
autoComplete?: string | undefined;
autoFocus?: boolean | undefined;
cols?: number | undefined;
dirName?: string | undefined;
disabled?: boolean | undefined;
form?: string | undefined;
maxLength?: number | undefined;
minLength?: number | undefined;
name?: string | undefined;
placeholder?: string | undefined;
readOnly?: boolean | undefined;
required?: boolean | undefined;
rows?: number | undefined;
value?: string | ReadonlyArray<string> | number | undefined;
wrap?: string | undefined;
}
export interface TdHTMLAttributes extends HTMLAttributes {
align?: "left" | "center" | "right" | "justify" | "char" | undefined;
colSpan?: number | undefined;
headers?: string | undefined;
rowSpan?: number | undefined;
scope?: string | undefined;
abbr?: string | undefined;
height?: number | string | undefined;
width?: number | string | undefined;
valign?: "top" | "middle" | "bottom" | "baseline" | undefined;
}
export interface ThHTMLAttributes extends HTMLAttributes {
align?: "left" | "center" | "right" | "justify" | "char" | undefined;
colSpan?: number | undefined;
headers?: string | undefined;
rowSpan?: number | undefined;
scope?: string | undefined;
abbr?: string | undefined;
}
export interface TimeHTMLAttributes extends HTMLAttributes {
dateTime?: string | undefined;
}
export interface TrackHTMLAttributes extends HTMLAttributes {
default?: boolean | undefined;
kind?: string | undefined;
label?: string | undefined;
src?: string | undefined;
srcLang?: string | undefined;
}
export interface VideoHTMLAttributes extends MediaHTMLAttributes {
height?: number | string | undefined;
playsInline?: boolean | undefined;
poster?: string | undefined;
width?: number | string | undefined;
disablePictureInPicture?: boolean | undefined;
disableRemotePlayback?: boolean | undefined;
}
export interface WebViewHTMLAttributes extends HTMLAttributes {
allowFullScreen?: boolean | undefined;
allowpopups?: boolean | undefined;
autoFocus?: boolean | undefined;
autosize?: boolean | undefined;
blinkfeatures?: string | undefined;
disableblinkfeatures?: string | undefined;
disableguestresize?: boolean | undefined;
disablewebsecurity?: boolean | undefined;
guestinstance?: string | undefined;
httpreferrer?: string | undefined;
nodeintegration?: boolean | undefined;
partition?: string | undefined;
plugins?: boolean | undefined;
preload?: string | undefined;
src?: string | undefined;
useragent?: string | undefined;
webpreferences?: string | undefined;
}
export interface SVGAttributes extends AriaAttributes, EventHandlers<Events> {
innerHTML?: string;
/**
* SVG Styling Attributes
* @see https://www.w3.org/TR/SVG/styling.html#ElementSpecificStyling
*/
class?: any;
className?: string | CSObject | CSArray | undefined;
color?: string | undefined;
height?: number | string | undefined;
id?: string | undefined;
lang?: string | undefined;
max?: number | string | undefined;
media?: string | undefined;
method?: string | undefined;
min?: number | string | undefined;
name?: string | undefined;
style?: CSSProperties | string | undefined;
target?: string | undefined;
type?: string | undefined;
width?: number | string | undefined;
// Other HTML properties supported by SVG elements in browsers
role?: AriaRole | undefined;
tabIndex?: number | undefined;
crossOrigin?: "anonymous" | "use-credentials" | "" | undefined;
// SVG Specific attributes
accentHeight?: number | string | undefined;
accumulate?: "none" | "sum" | undefined;
additive?: "replace" | "sum" | undefined;
alignmentBaseline?:
| "auto"
| "baseline"
| "before-edge"
| "text-before-edge"
| "middle"
| "central"
| "after-edge"
| "text-after-edge"
| "ideographic"
| "alphabetic"
| "hanging"
| "mathematical"
| "inherit"
| undefined;
allowReorder?: "no" | "yes" | undefined;
alphabetic?: number | string | undefined;
amplitude?: number | string | undefined;
arabicForm?: "initial" | "medial" | "terminal" | "isolated" | undefined;
ascent?: number | string | undefined;
attributeName?: string | undefined;
attributeType?: string | undefined;
autoReverse?: Booleanish | undefined;
azimuth?: number | string | undefined;
baseFrequency?: number | string | undefined;
baselineShift?: number | string | undefined;
baseProfile?: number | string | undefined;
bbox?: number | string | undefined;
begin?: number | string | undefined;
bias?: number | string | undefined;
by?: number | string | undefined;
calcMode?: number | string | undefined;
capHeight?: number | string | undefined;
clip?: number | string | undefined;
clipPath?: string | undefined;
clipPathUnits?: number | string | undefined;
clipRule?: number | string | undefined;
colorInterpolation?: number | string | undefined;
colorInterpolationFilters?:
| "auto"
| "sRGB"
| "linearRGB"
| "inherit"
| undefined;
colorProfile?: number | string | undefined;
colorRendering?: number | string | undefined;
contentScriptType?: number | string | undefined;
contentStyleType?: number | string | undefined;
cursor?: number | string | undefined;
cx?: number | string | undefined;
cy?: number | string | undefined;
d?: string | undefined;
decelerate?: number | string | undefined;
descent?: number | string | undefined;
diffuseConstant?: number | string | undefined;
direction?: number | string | undefined;
display?: number | string | undefined;
divisor?: number | string | undefined;
dominantBaseline?: number | string | undefined;
dur?: number | string | undefined;
dx?: number | string | undefined;
dy?: number | string | undefined;
edgeMode?: number | string | undefined;
elevation?: number | string | undefined;
enableBackground?: number | string | undefined;
end?: number | string | undefined;
exponent?: number | string | undefined;
externalResourcesRequired?: Booleanish | undefined;
fill?: string | undefined;
fillOpacity?: number | string | undefined;
fillRule?: "nonzero" | "evenodd" | "inherit" | undefined;
filter?: string | undefined;
filterRes?: number | string | undefined;
filterUnits?: number | string | undefined;
floodColor?: number | string | undefined;
floodOpacity?: number | string | undefined;
focusable?: Booleanish | "auto" | undefined;
fontFamily?: string | undefined;
fontSize?: number | string | undefined;
fontSizeAdjust?: number | string | undefined;
fontStretch?: number | string | undefined;
fontStyle?: number | string | undefined;
fontVariant?: number | string | undefined;
fontWeight?: number | string | undefined;
format?: number | string | undefined;
fr?: number | string | undefined;
from?: number | string | undefined;
fx?: number | string | undefined;
fy?: number | string | undefined;
g1?: number | string | undefined;
g2?: number | string | undefined;
glyphName?: number | string | undefined;
glyphOrientationHorizontal?: number | string | undefined;
glyphOrientationVertical?: number | string | undefined;
glyphRef?: number | string | undefined;
gradientTransform?: string | undefined;
gradientUnits?: string | undefined;
hanging?: number | string | undefined;
horizAdvX?: number | string | undefined;
horizOriginX?: number | string | undefined;
href?: string | undefined;
ideographic?: number | string | undefined;
imageRendering?: number | string | undefined;
in2?: number | string | undefined;
in?: string | undefined;
intercept?: number | string | undefined;
k1?: number | string | undefined;
k2?: number | string | undefined;
k3?: number | string | undefined;
k4?: number | string | undefined;
k?: number | string | undefined;
kernelMatrix?: number | string | undefined;
kernelUnitLength?: number | string | undefined;
kerning?: number | string | undefined;
keyPoints?: number | string | undefined;
keySplines?: number | string | undefined;
keyTimes?: number | string | undefined;
lengthAdjust?: number | string | undefined;
letterSpacing?: number | string | undefined;
lightingColor?: number | string | undefined;
limitingConeAngle?: number | string | undefined;
local?: number | string | undefined;
markerEnd?: string | undefined;
markerHeight?: number | string | undefined;
markerMid?: string | undefined;
markerStart?: string | undefined;
markerUnits?: number | string | undefined;
markerWidth?: number | string | undefined;
mask?: string | undefined;
maskContentUnits?: number | string | undefined;
maskUnits?: number | string | undefined;
mathematical?: number | string | undefined;
mode?: number | string | undefined;
numOctaves?: number | string | undefined;
offset?: number | string | undefined;
opacity?: number | string | undefined;
operator?: number | string | undefined;
order?: number | string | undefined;
orient?: number | string | undefined;
orientation?: number | string | undefined;
origin?: number | string | undefined;
overflow?: number | string | undefined;
overlinePosition?: number | string | undefined;
overlineThickness?: number | string | undefined;
paintOrder?: number | string | undefined;
panose1?: number | string | undefined;
path?: string | undefined;
pathLength?: number | string | undefined;
patternContentUnits?: string | undefined;
patternTransform?: number | string | undefined;
patternUnits?: string | undefined;
pointerEvents?: number | string | undefined;
points?: string | undefined;
pointsAtX?: number | string | undefined;
pointsAtY?: number | string | undefined;
pointsAtZ?: number | string | undefined;
preserveAlpha?: Booleanish | undefined;
preserveAspectRatio?: string | undefined;
primitiveUnits?: number | string | undefined;
r?: number | string | undefined;
radius?: number | string | undefined;
refX?: number | string | undefined;
refY?: number | string | undefined;
renderingIntent?: number | string | undefined;
repeatCount?: number | string | undefined;
repeatDur?: number | string | undefined;
requiredExtensions?: number | string | undefined;
requiredFeatures?: number | string | undefined;
restart?: number | string | undefined;
result?: string | undefined;
rotate?: number | string | undefined;
rx?: number | string | undefined;
ry?: number | string | undefined;
scale?: number | string | undefined;
seed?: number | string | undefined;
shapeRendering?: number | string | undefined;
slope?: number | string | undefined;
spacing?: number | string | undefined;
specularConstant?: number | string | undefined;
specularExponent?: number | string | undefined;
speed?: number | string | undefined;
spreadMethod?: string | undefined;
startOffset?: number | string | undefined;
stdDeviation?: number | string | undefined;
stemh?: number | string | undefined;
stemv?: number | string | undefined;
stitchTiles?: number | string | undefined;
stopColor?: string | undefined;
stopOpacity?: number | string | undefined;
strikethroughPosition?: number | string | undefined;
strikethroughThickness?: number | string | undefined;
string?: number | string | undefined;
stroke?: string | undefined;
strokeDasharray?: string | number | undefined;
strokeDashoffset?: string | number | undefined;
strokeLinecap?: "butt" | "round" | "square" | "inherit" | undefined;
strokeLinejoin?: "miter" | "round" | "bevel" | "inherit" | undefined;
strokeMiterlimit?: number | string | undefined;
strokeOpacity?: number | string | undefined;
strokeWidth?: number | string | undefined;
surfaceScale?: number | string | undefined;
systemLanguage?: number | string | undefined;
tableValues?: number | string | undefined;
targetX?: number | string | undefined;
targetY?: number | string | undefined;
textAnchor?: string | undefined;
textDecoration?: number | string | undefined;
textLength?: number | string | undefined;
textRendering?: number | string | undefined;
to?: number | string | undefined;
transform?: string | undefined;
u1?: number | string | undefined;
u2?: number | string | undefined;
underlinePosition?: number | string | undefined;
underlineThickness?: number | string | undefined;
unicode?: number | string | undefined;
unicodeBidi?: number | string | undefined;
unicodeRange?: number | string | undefined;
unitsPerEm?: number | string | undefined;
vAlphabetic?: number | string | undefined;
values?: string | undefined;
vectorEffect?: number | string | undefined;
version?: string | undefined;
vertAdvY?: number | string | undefined;
vertOriginX?: number | string | undefined;
vertOriginY?: number | string | undefined;
vHanging?: number | string | undefined;
vIdeographic?: number | string | undefined;
viewBox?: string | undefined;
viewTarget?: number | string | undefined;
visibility?: number | string | undefined;
vMathematical?: number | string | undefined;
widths?: number | string | undefined;
wordSpacing?: number | string | undefined;
writingMode?: number | string | undefined;
x1?: number | string | undefined;
x2?: number | string | undefined;
x?: number | string | undefined;
xChannelSelector?: string | undefined;
xHeight?: number | string | undefined;
xlinkActuate?: string | undefined;
xlinkArcrole?: string | undefined;
xlinkHref?: string | undefined;
xlinkRole?: string | undefined;
xlinkShow?: string | undefined;
xlinkTitle?: string | undefined;
xlinkType?: string | undefined;
xmlBase?: string | undefined;
xmlLang?: string | undefined;
xmlns?: string | undefined;
xmlnsXlink?: string | undefined;
xmlSpace?: string | undefined;
y1?: number | string | undefined;
y2?: number | string | undefined;
y?: number | string | undefined;
yChannelSelector?: string | undefined;
z?: number | string | undefined;
zoomAndPan?: string | undefined;
}
export interface IntrinsicElementAttributes {
a: AnchorHTMLAttributes;
abbr: HTMLAttributes;
address: HTMLAttributes;
area: AreaHTMLAttributes;
article: HTMLAttributes;
aside: HTMLAttributes;
audio: AudioHTMLAttributes;
b: HTMLAttributes;
base: BaseHTMLAttributes;
bdi: HTMLAttributes;
bdo: HTMLAttributes;
blockquote: BlockquoteHTMLAttributes;
body: HTMLAttributes;
br: HTMLAttributes;
button: ButtonHTMLAttributes;
canvas: CanvasHTMLAttributes;
caption: HTMLAttributes;
cite: HTMLAttributes;
code: HTMLAttributes;
col: ColHTMLAttributes;
colgroup: ColgroupHTMLAttributes;
data: DataHTMLAttributes;
datalist: HTMLAttributes;
dd: HTMLAttributes;
del: DelHTMLAttributes;
details: DetailsHTMLAttributes;
dfn: HTMLAttributes;
dialog: DialogHTMLAttributes;
div: HTMLAttributes;
dl: HTMLAttributes;
dt: HTMLAttributes;
em: HTMLAttributes;
embed: EmbedHTMLAttributes;
fieldset: FieldsetHTMLAttributes;
figcaption: HTMLAttributes;
figure: HTMLAttributes;
footer: HTMLAttributes;
form: FormHTMLAttributes;
h1: HTMLAttributes;
h2: HTMLAttributes;
h3: HTMLAttributes;
h4: HTMLAttributes;
h5: HTMLAttributes;
h6: HTMLAttributes;
head: HTMLAttributes;
header: HTMLAttributes;
hgroup: HTMLAttributes;
hr: HTMLAttributes;
html: HtmlHTMLAttributes;
i: HTMLAttributes;
iframe: IframeHTMLAttributes;
img: ImgHTMLAttributes;
input: InputHTMLAttributes;
ins: InsHTMLAttributes;
kbd: HTMLAttributes;
keygen: KeygenHTMLAttributes;
label: LabelHTMLAttributes;
legend: HTMLAttributes;
li: LiHTMLAttributes;
link: LinkHTMLAttributes;
main: HTMLAttributes;
map: MapHTMLAttributes;
mark: HTMLAttributes;
menu: MenuHTMLAttributes;
meta: MetaHTMLAttributes;
meter: MeterHTMLAttributes;
nav: HTMLAttributes;
noindex: HTMLAttributes;
noscript: HTMLAttributes;
object: ObjectHTMLAttributes;
ol: OlHTMLAttributes;
optgroup: OptgroupHTMLAttributes;
option: OptionHTMLAttributes;
output: OutputHTMLAttributes;
p: HTMLAttributes;
param: ParamHTMLAttributes;
picture: HTMLAttributes;
pre: HTMLAttributes;
progress: ProgressHTMLAttributes;
q: QuoteHTMLAttributes;
rp: HTMLAttributes;
rt: HTMLAttributes;
ruby: HTMLAttributes;
s: HTMLAttributes;
samp: HTMLAttributes;
script: ScriptHTMLAttributes;
section: HTMLAttributes;
select: SelectHTMLAttributes;
slot: SlotHTMLAttributes;
small: HTMLAttributes;
source: SourceHTMLAttributes;
span: HTMLAttributes;
strong: HTMLAttributes;
style: StyleHTMLAttributes;
sub: HTMLAttributes;
summary: HTMLAttributes;
sup: HTMLAttributes;
table: TableHTMLAttributes;
template: HTMLAttributes;
tbody: HTMLAttributes;
td: TdHTMLAttributes;
textarea: TextareaHTMLAttributes;
tfoot: HTMLAttributes;
th: ThHTMLAttributes;
thead: HTMLAttributes;
time: TimeHTMLAttributes;
title: HTMLAttributes;
tr: HTMLAttributes;
track: TrackHTMLAttributes;
u: HTMLAttributes;
ul: HTMLAttributes;
var: HTMLAttributes;
video: VideoHTMLAttributes;
wbr: HTMLAttributes;
webview: WebViewHTMLAttributes;
// SVG
svg: SVGAttributes;
animate: SVGAttributes;
animateMotion: SVGAttributes;
animateTransform: SVGAttributes;
circle: SVGAttributes;
clipPath: SVGAttributes;
defs: SVGAttributes;
desc: SVGAttributes;
ellipse: SVGAttributes;
feBlend: SVGAttributes;
feColorMatrix: SVGAttributes;
feComponentTransfer: SVGAttributes;
feComposite: SVGAttributes;
feConvolveMatrix: SVGAttributes;
feDiffuseLighting: SVGAttributes;
feDisplacementMap: SVGAttributes;
feDistantLight: SVGAttributes;
feDropShadow: SVGAttributes;
feFlood: SVGAttributes;
feFuncA: SVGAttributes;
feFuncB: SVGAttributes;
feFuncG: SVGAttributes;
feFuncR: SVGAttributes;
feGaussianBlur: SVGAttributes;
feImage: SVGAttributes;
feMerge: SVGAttributes;
feMergeNode: SVGAttributes;
feMorphology: SVGAttributes;
feOffset: SVGAttributes;
fePointLight: SVGAttributes;
feSpecularLighting: SVGAttributes;
feSpotLight: SVGAttributes;
feTile: SVGAttributes;
feTurbulence: SVGAttributes;
filter: SVGAttributes;
foreignObject: SVGAttributes;
g: SVGAttributes;
image: SVGAttributes;
line: SVGAttributes;
linearGradient: SVGAttributes;
marker: SVGAttributes;
mask: SVGAttributes;
metadata: SVGAttributes;
mpath: SVGAttributes;
path: SVGAttributes;
pattern: SVGAttributes;
polygon: SVGAttributes;
polyline: SVGAttributes;
radialGradient: SVGAttributes;
rect: SVGAttributes;
stop: SVGAttributes;
switch: SVGAttributes;
symbol: SVGAttributes;
text: SVGAttributes;
textPath: SVGAttributes;
tspan: SVGAttributes;
use: SVGAttributes;
view: SVGAttributes;
}
export interface Events {
// Clipboard Events
onCopy: ClipboardEvent;
onCut: ClipboardEvent;
onPaste: ClipboardEvent;
// Composition Events
onCompositionEnd: CompositionEvent;
onCompositionStart: CompositionEvent;
onCompositionUpdate: CompositionEvent;
// Focus Events
onFocus: FocusEvent;
onBlur: FocusEvent;
// Form Events
onChange: Event;
onBeforeInput: Event;
onInput: Event;
onReset: Event;
onSubmit: Event;
onInvalid: Event;
// Image Events
onLoad: Event;
onError: Event;
// Keyboard Events
onKeyDown: KeyboardEvent;
onKeyPress: KeyboardEvent;
onKeyUp: KeyboardEvent;
// Media Events
onAbort: Event;
onCanPlay: Event;
onCanPlayThrough: Event;
onDurationChange: Event;
onEmptied: Event;
onEncrypted: Event;
onEnded: Event;
onLoadedData: Event;
onLoadedMetadata: Event;
onLoadStart: Event;
onPause: Event;
onPlay: Event;
onPlaying: Event;
onProgress: Event;
onRateChange: Event;
onSeeked: Event;
onSeeking: Event;
onStalled: Event;
onSuspend: Event;
onTimeUpdate: Event;
onVolumeChange: Event;
onWaiting: Event;
// DragEvent
onDrag: DragEvent;
onDragEnd: DragEvent;
onDragEnter: DragEvent;
onDragExit: DragEvent;
onDragLeave: DragEvent;
onDragOver: DragEvent;
onDragStart: DragEvent;
onDrop: DragEvent;
// MouseEvents
onAuxClick: MouseEvent;
onClick: MouseEvent;
onContextMenu: MouseEvent;
onDoubleClick: MouseEvent;
onMouseDown: MouseEvent;
onMouseEnter: MouseEvent;
onMouseLeave: MouseEvent;
onMouseMove: MouseEvent;
onMouseOut: MouseEvent;
onMouseOver: MouseEvent;
onMouseUp: MouseEvent;
// Selection Events
onSelect: Event;
// Touch Events
onTouchCancel: TouchEvent;
onTouchEnd: TouchEvent;
onTouchMove: TouchEvent;
onTouchStart: TouchEvent;
// Pointer Events
onPointerDown: PointerEvent;
onPointerMove: PointerEvent;
onPointerUp: PointerEvent;
onPointerCancel: PointerEvent;
onPointerEnter: PointerEvent;
onPointerLeave: PointerEvent;
onPointerOver: PointerEvent;
onPointerOut: PointerEvent;
// UI Events
onScroll: UIEvent;
// Wheel Events
onWheel: WheelEvent;
// Animation Events
onAnimationStart: AnimationEvent;
onAnimationEnd: AnimationEvent;
onAnimationIteration: AnimationEvent;
// Transition Events
onTransitionEnd: TransitionEvent;
onTransitionStart: TransitionEvent;
}
type EventHandlers<E> = {
[K in keyof E]?: E[K] extends Function ? E[K] : (payload: E[K]) => void;
};
type ReservedProps = {
children?: unknown;
key?: string | number | symbol;
ref?: { current: any } | ((ref: Element | null) => void);
};
export type ElementAttrs<T> = T & ReservedProps;
type NativeElements = {
[K in keyof IntrinsicElementAttributes]: ElementAttrs<
IntrinsicElementAttributes[K]
>;
};
declare global {
namespace JSX {
type ElementTypes =
| string
| number
| null
| boolean
| void
| undefined
| FiberNode;
type Element =
| Element[]
| ElementTypes
| ElementTypes[]
| (() => Element)
| (() => Element[]);
interface ElementClass {
props: {};
}
interface ElementAttributesProperty {
props: {};
}
interface ElementChildrenAttribute {
children: {};
}
interface IntrinsicElements extends NativeElements {
// allow arbitrary elements
// @ts-ignore suppress ts:2374 = Duplicate string index signature.
[name: string]: any;
}
interface IntrinsicAttributes extends ReservedProps {}
}
}
// suppress ts:2669
export {};
================================================
FILE: packages/unis-core/vitest.config.ts
================================================
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
include: ["**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
coverage: {
reporter: ["text", "json", "html"],
},
},
});
================================================
FILE: packages/unis-dom/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
build
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
================================================
FILE: packages/unis-dom/index.d.ts
================================================
import "@unis/core";
export * from "./dist/browser";
================================================
FILE: packages/unis-dom/package.json
================================================
{
"name": "@unis/dom",
"version": "1.2.5",
"description": "Unis is a simpler and easier to use front-end framework than React",
"main": "dist/browser.js",
"module": "dist/browser.mjs",
"types": "index.d.ts",
"typings": "index.d.ts",
"exports": {
".": {
"require": "./dist/browser.js",
"import": "./dist/browser.mjs"
},
"./server": {
"require": "./dist/server.js",
"import": "./dist/server.mjs"
}
},
"scripts": {
"build": "rimraf build && rimraf dist && tsc -p tsconfig.build.json && rollup --config",
"build:dev": "cross-env NODE_ENV=development pnpm build",
"build:server": "rollup --config rollup.config.server.mjs",
"test": "vitest run --coverage",
"test:watch": "vitest -w"
},
"repository": {
"type": "git",
"url": "git+https://github.com/anuoua/unis.git"
},
"keywords": [
"frontend",
"web",
"framwork"
],
"files": [
"dist",
"server.d.ts",
"index.d.ts"
],
"author": "anuoua",
"license": "MIT",
"bugs": {
"url": "https://github.com/anuoua/unis/issues"
},
"homepage": "https://github.com/anuoua/unis#readme",
"peerDependencies": {
"@unis/core": "workspace:^"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.2",
"@types/jsdom": "^21.1.1",
"@unis/core": "workspace:^",
"@unis/vite-preset": "workspace:^",
"@vitest/coverage-c8": "^0.28.5",
"cross-env": "^7.0.3",
"esbuild": "^0.17.15",
"jsdom": "^21.1.1",
"rimraf": "^4.4.1",
"rollup": "^3.20.2",
"rollup-plugin-dts": "^5.3.0",
"rollup-plugin-esbuild": "^5.0.0",
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"vite": "^4.2.1",
"vitest": "^0.29.8"
}
}
================================================
FILE: packages/unis-dom/rollup.config.mjs
================================================
import esbuild from "rollup-plugin-esbuild";
import dts from "rollup-plugin-dts";
import { defineConfig } from "rollup";
import { nodeResolve } from "@rollup/plugin-node-resolve";
const configGen = (format, plateform) =>
defineConfig({
input: `src/${plateform}/index.ts`,
external: [/^@unis/],
output: [
{
dir: "dist",
entryFileNames: `${plateform}.${format === "esm" ? "mjs" : "js"}`,
format,
sourcemap: true,
},
],
plugins: [
nodeResolve(),
esbuild({
sourceMap: true,
target: "esnext",
}),
],
});
const dtsRollup = (which) =>
defineConfig({
input: `build/${which}/index.d.ts`,
output: [{ file: `dist/${which}.d.ts`, format: "es" }],
plugins: [dts()],
});
const config = [
configGen("cjs", "browser"),
configGen("esm", "browser"),
configGen("cjs", "server"),
configGen("esm", "server"),
dtsRollup("browser"),
dtsRollup("server"),
];
export default config;
================================================
FILE: packages/unis-dom/server.d.ts
================================================
export * from "./dist/server";
================================================
FILE: packages/unis-dom/src/browser/__test__/context.test.tsx
================================================
/**
* @vitest-environment jsdom
*/
import { afterEach, beforeEach, expect, it } from "vitest";
import { useContext } from "@unis/core";
import { useEffect } from "@unis/core";
import { useProps } from "@unis/core";
import { useState } from "@unis/core";
import { createContext } from "@unis/core";
import { Fragment, memo } from "@unis/core";
import { rendered, testRender } from "./util";
let root: Element;
beforeEach(() => {
root = document.createElement("div");
document.body.append(root);
});
afterEach(() => {
root.innerHTML = "";
});
it("context", async () => {
const AppContext = createContext<string>("initial");
const Cpp = memo(() => {
let theme = useContext(AppContext);
return () => <div>Cpp: {theme}</div>;
});
const Dpp = () => {
return () => (
<AppContext.Consumer>
{(theme) => <div>Dpp: {theme}</div>}
</AppContext.Consumer>
);
};
const Epp = () => {
return () => (
<AppContext.Consumer>
{(theme) => <div>Epp: {theme}</div>}
</AppContext.Consumer>
);
};
const Bpp = () => {
let theme = useContext(AppContext);
return () => (
<div>
Bpp: {theme}
<AppContext.Provider value="gray">
<Cpp />
</AppContext.Provider>
</div>
);
};
const App = () => {
let [theme, setTheme] = useState("light");
useEffect(
() => {
setTheme("dark");
},
() => []
);
return () => (
<Fragment>
<AppContext.Provider value={theme}>
<div>App</div>
<Bpp />
<Dpp />
</AppContext.Provider>
<Epp />
</Fragment>
);
};
testRender(<App />, root);
expect(root.innerHTML).toBe(
"<div>App</div><div>Bpp: light<div>Cpp: gray</div></div><div>Dpp: light</div><div>Epp: initial</div>"
);
await rendered();
expect(root.innerHTML).toBe(
"<div>App</div><div>Bpp: dark<div>Cpp: gray</div></div><div>Dpp: dark</div><div>Epp: initial</div>"
);
});
it("context pass through", async () => {
const AppContext = createContext({} as any);
const App = () => {
let [hello, setHello] = useState("hello");
return () => (
<AppContext.Provider value={{ hello, setHello }}>
<div>
<Bpp />
</div>
</AppContext.Provider>
);
};
const Bpp = () => {
let { hello, setHello } = useContext(AppContext);
return () => <Cpp msg={hello} setMsg={setHello} />;
};
const Cpp = (p: { msg: string; setMsg: (msg: string) => void }) => {
let { msg, setMsg } = useProps(p);
let [count, setCount] = useState(0);
useEffect(
() => {
setMsg("world");
setCount(count + 1);
},
() => []
);
return () => msg;
};
testRender(<App />, root);
expect(root.innerHTML).toBe("<div>hello</div>");
await rendered();
expect(root.innerHTML).toBe("<div>world</div>");
});
================================================
FILE: packages/unis-dom/src/browser/__test__/dom.test.tsx
================================================
/**
* @vitest-environment jsdom
*/
import { afterEach, beforeEach, expect, it } from "vitest";
import { useEffect } from "@unis/core";
import { useState } from "@unis/core";
import { rendered, testRender } from "./util";
let root: Element;
beforeEach(() => {
root = document.createElement("div");
document.body.append(root);
});
afterEach(() => {
root.innerHTML = "";
});
it("dom", async () => {
const App = () => {
let [toggle, setToggle] = useState(true);
const getCurrentStyle = () => {
return toggle
? {
style: {
background: "yellow",
},
tabindex: "1",
className: "class1",
onClick: () => {},
}
: {
style: {
background: "red",
},
tabindex: "2",
onClick: () => {},
};
};
useEffect(
() => {
setToggle(false);
},
() => []
);
return () => {
return <div {...getCurrentStyle()}>hello</div>;
};
};
testRender(<App />, root);
expect(root.innerHTML).toBe(
'<div style="background: yellow;" tabindex="1" class="class1">hello</div>'
);
await rendered();
expect(root.innerHTML).toBe(
'<div style="background: red;" tabindex="2">hello</div>'
);
});
================================================
FILE: packages/unis-dom/src/browser/__test__/effect.test.tsx
================================================
/**
* @vitest-environment jsdom
*/
import { afterEach, beforeEach, expect, it } from "vitest";
import { useEffect } from "@unis/core";
import { useState } from "@unis/core";
import { rendered, testRender } from "./util";
let root: Element;
beforeEach(() => {
root = document.createElement("div");
document.body.append(root);
});
afterEach(() => {
root.innerHTML = "";
});
it("effect", async () => {
const Bpp = () => {
useEffect(
() => {
return () => {};
},
() => []
);
return () => "bpp";
};
const App = () => {
let [visible, setVisible] = useState(true);
useEffect(
() => {
setVisible(false);
},
() => []
);
return () => (visible ? <Bpp /> : null);
};
testRender(<App />, root);
expect(root.innerHTML).toBe("bpp");
await rendered();
expect(root.innerHTML).toBe("");
});
================================================
FILE: packages/unis-dom/src/browser/__test__/hydrate.test.tsx
================================================
/**
* @vitest-environment jsdom
*/
import { afterEach, beforeEach, expect, it } from "vitest";
import { rendered, testRender } from "./util";
import { use, useState } from "@unis/core";
let root: Element;
beforeEach(() => {
root = document.createElement("div");
document.body.append(root);
});
afterEach(() => {
root.innerHTML = "";
});
it("hydrate", async () => {
root.innerHTML = "<div>App<span>hello</span></div>";
let setMsgOutter: any;
const App = () => {
let [msg, setMsg] = useState("hello");
use(() => {
setMsgOutter = setMsg;
});
return () => (
<div>
App<span>{msg}</span>
</div>
);
};
testRender(<App />, root, true);
expect(root.innerHTML).toBe("<div>App<span>hello</span></div>");
setMsgOutter("world");
await rendered();
expect(root.innerHTML).toBe("<div>App<span>world</span></div>");
});
================================================
FILE: packages/unis-dom/src/browser/__test__/memo.test.tsx
================================================
/**
* @vitest-environment jsdom
*/
import { afterEach, beforeEach, expect, it } from "vitest";
import { useEffect } from "@unis/core";
import { useState } from "@unis/core";
import { h2, memo } from "@unis/core";
import { rendered, testRender } from "./util";
let root: Element;
beforeEach(() => {
root = document.createElement("div");
document.body.append(root);
});
afterEach(() => {
root.innerHTML = "";
});
it("memo", async () => {
const Bpp = memo(() => {
let renderCount = 0;
return () => {
return <div>{renderCount++}</div>;
};
});
const App = () => {
let [msg, setMsg] = useState("hello");
useEffect(
() => {
setMsg("hello world");
},
() => []
);
return () => (
<div>
{msg}
{h2(Bpp, {}, "key")}
</div>
);
};
testRender(<App />, root);
expect(root.innerHTML).toBe("<div>hello<div>0</div></div>");
await rendered();
expect(root.innerHTML).toBe("<div>hello world<div>0</div></div>");
});
================================================
FILE: packages/unis-dom/src/browser/__test__/portal.test.tsx
================================================
/**
* @vitest-environment jsdom
*/
import { afterEach, beforeEach, expect, it } from "vitest";
import { useEffect } from "@unis/core";
import { useState } from "@unis/core";
import { createPortal, Fragment } from "@unis/core";
import { rendered, testRender } from "./util";
let root: Element;
let dialog: Element;
beforeEach(() => {
root = document.createElement("div");
dialog = document.createElement("div");
document.body.append(root, dialog);
});
afterEach(() => {
root.innerHTML = "";
dialog.innerHTML = "";
});
it("portal", async () => {
const App = () => {
let [visible, setVisible] = useState(true);
useEffect(
() => {
setVisible(false);
},
() => []
);
return () => (
<Fragment>
<div>hello</div>
{visible && createPortal(<main>hello dialog</main>, dialog)}
</Fragment>
);
};
testRender(<App />, root);
expect(document.body.innerHTML).toBe(
"<div><div>hello</div></div><div><main>hello dialog</main></div>"
);
await rendered();
expect(document.body.innerHTML).toBe(
"<div><div>hello</div></div><div></div>"
);
});
================================================
FILE: packages/unis-dom/src/browser/__test__/reconcile.test.tsx
================================================
/**
* @vitest-environment jsdom
*/
import { afterEach, beforeEach, expect, it } from "vitest";
import { useEffect } from "@unis/core";
import { useState } from "@unis/core";
import { rendered, testRender } from "./util";
let root: Element;
beforeEach(() => {
root = document.createElement("div");
document.body.append(root);
});
afterEach(() => {
root.innerHTML = "";
});
it("diff with key", async () => {
const App = () => {
let [toggle, setToggle] = useState(false);
useEffect(
() => {
setToggle(true);
},
() => []
);
return () =>
!toggle ? (
<div>
<span>1</span>
<span key="2">2</span>
<span key="3">3</span>
<div>del</div>
<span key="4">4</span>
<div key="5">5</div>
<div>6</div>
</div>
) : (
<div>
<span>1</span>
<div key="5">5</div>
<span key="4">4</span>
<span key="2">2</span>
<span key="3">3</span>
<div>6</div>
</div>
);
};
testRender(<App />, root);
expect(root.innerHTML).toBe(
'<div><span>1</span><span key="2">2</span><span key="3">3</span><div>del</div><span key="4">4</span><div key="5">5</div><div>6</div></div>'
);
await rendered();
expect(root.innerHTML).toBe(
'<div><span>1</span><div key="5">5</div><span key="4">4</span><span key="2">2</span><span key="3">3</span><div>6</div></div>'
);
});
================================================
FILE: packages/unis-dom/src/browser/__test__/util.ts
================================================
import { readyForWork, createRoot, createTokTik } from "@unis/core";
import { createOperator } from "../operator";
const toktik = createTokTik({
nextTick: (cb: VoidFunction) =>
Promise.resolve()
.catch((err) => console.error(err))
.then(() => cb()),
now: () => 0,
});
const operator = createOperator();
export const testRender = (
element: any,
container: Element,
hydrate = false
) => {
const rootFiber = createRoot(element, container);
rootFiber.runtime = {
toktik,
operator,
};
readyForWork(rootFiber, hydrate);
};
export const rendered = () =>
new Promise((resolve) => {
setTimeout(resolve, 0);
});
================================================
FILE: packages/unis-dom/src/browser/__test__/utils.test.ts
================================================
/**
* @vitest-environment jsdom
*/
import { expect, it } from "vitest";
import { classes, svgKey, styleStr } from "@unis/core";
it("classes", () => {
expect(classes(["a", "b", 1, ["c", { d: true }]])).toBe("a b 1 c d");
expect(classes({ a: true, b: undefined, c: null })).toBe("a");
expect(classes({ a: false, b: true, c: null })).toBe("b");
});
it("realSVGAttr", () => {
expect(svgKey("glyphOrientationVertical")).toBe("glyph-orientation-vertical");
});
it("style2String", () => {
expect(styleStr({ background: "yellow", fontSize: "14px" })).toBe(
"background: yellow; font-size: 14px;"
);
});
================================================
FILE: packages/unis-dom/src/browser/const.ts
================================================
export const UNIS_ROOT = Symbol("unis_root");
================================================
FILE: packages/unis-dom/src/browser/index.ts
================================================
export { UNIS_ROOT } from "./const";
export { render } from "./render";
================================================
FILE: packages/unis-dom/src/browser/operator.ts
================================================
import { Fiber, findEls, isPortal, isText, Operator } from "@unis/core";
import { getEventName, isEvent, isNullish } from "@unis/core";
import { UNIS_ROOT } from "./const";
type FiberDomEl = Element | DocumentFragment | SVGAElement | Text | ParentNode;
interface FiberDom extends Fiber {
el?: FiberDomEl;
}
export const createOperator = (): Operator => {
const createElement = (fiber: FiberDom) => {
const { tag: type, isSvg } = fiber;
return isText(fiber)
? document.createTextNode(fiber.props.nodeValue + "")
: isSvg
? document.createElementNS("http://www.w3.org/2000/svg", type as string)
: document.createElement(type as string);
};
const nextElement = (el: FiberDomEl | null) => {
while (el) {
if (el.firstChild) return el.firstChild;
if (el.nextSibling) return el.nextSibling;
while ((el = el.parentNode)) {
if ((el as any)[UNIS_ROOT]) return null;
if (el.nextSibling) return el.nextSibling;
}
}
return null;
};
const matchElement = (fiber: FiberDom, el: Element | Text) =>
el.nodeType === Node.TEXT_NODE
? isText(fiber)
: (el as Element).tagName.toLocaleLowerCase() === fiber.tag;
const insertBefore = (
containerFiber: FiberDom,
insertElement: FiberDomEl,
targetElement: FiberDomEl | null
) => {
(
(isPortal(containerFiber)
? containerFiber.to
: containerFiber.el)! as FiberDomEl
).insertBefore(insertElement, targetElement);
};
const nextSibling = (fiber: FiberDom) => fiber.el!.nextSibling;
const firstChild = (fiber: FiberDom) => fiber.el!.firstChild;
const remove = (fiber: FiberDom) => {
const [first, ...rest] = findEls(fiber) as FiberDomEl[];
const parentNode = first?.parentNode;
if (parentNode) {
for (const el of [first, ...rest]) {
parentNode.removeChild(el);
}
}
};
const updateTextProperties = (fiber: FiberDom) => {
(fiber.el! as Text).nodeValue = fiber.props.nodeValue + "";
};
const setAttr = (
el: SVGAElement | HTMLElement,
isSvg: boolean,
key: string,
value: string
) =>
isSvg
? (el as SVGAElement).setAttributeNS(null, key, value)
: (el as HTMLElement).setAttribute(key, value);
const removeAttr = (
el: SVGAElement | HTMLElement,
isSvg: boolean,
key: string
) =>
isSvg
? (el as SVGAElement).removeAttributeNS(null, key)
: (el as HTMLElement).removeAttribute(key);
const updateElementProperties = (fiber: FiberDom) => {
let { el, isSvg, attrDiff } = fiber;
for (const [key, newValue, oldValue] of attrDiff || []) {
const newExist = !isNullish(newValue);
const oldExist = !isNullish(oldValue);
if (key === "ref") {
oldExist && (oldValue.current = undefined);
newExist && (newValue.current = el);
} else if (isEvent(key)) {
const [eventName, capture] = getEventName(key);
oldExist && el!.removeEventListener(eventName, oldValue);
newExist && el!.addEventListener(eventName, newValue, capture);
} else {
newExist
? setAttr(el as SVGAElement | HTMLElement, isSvg!, key, newValue)
: removeAttr(el as SVGAElement | HTMLElement, isSvg!, key);
}
}
};
return {
createElement,
nextElement,
matchElement,
insertBefore,
nextSibling,
firstChild,
remove,
updateTextProperties,
updateElementProperties,
};
};
================================================
FILE: packages/unis-dom/src/browser/render.ts
================================================
import { readyForWork, createRoot, createTokTik } from "@unis/core";
import { createOperator } from "./operator";
import { UNIS_ROOT } from "./const";
import { nextTick, now } from "./toktik";
const operator = createOperator();
const toktik = createTokTik({
now,
nextTick,
interval: (window as any).UNIS_INTERVAL,
});
export const render = (element: any, container: Element, hydrate = false) => {
(container as any)[UNIS_ROOT] = true;
const rootFiber = createRoot(element, container);
rootFiber.runtime = {
toktik,
operator,
};
readyForWork(rootFiber, hydrate);
};
================================================
FILE: packages/unis-dom/src/browser/toktik.ts
================================================
export const nextTick = (cb: VoidFunction, pending = false) => {
if (pending) {
queueMicrotask(cb);
} else if (window.MessageChannel) {
const { port1, port2 } = new window.MessageChannel();
port1.postMessage("");
port2.onmessage = () => cb();
} else {
setTimeout(() => cb());
}
};
export const now = () => performance.now();
================================================
FILE: packages/unis-dom/src/server/__test__/server.test.tsx
================================================
import { expect, it } from "vitest";
import { renderToString } from "..";
it("render to string", () => {
const App = () => {
return () => (
<div className="test">
<>
<h1>hello</h1>
<span style={{ background: "yellow" }}>world</span>
</>
</div>
);
};
const result = renderToString(<App />);
expect(result).toBe(
'<div class="test"><h1>hello</h1><span style="background: yellow;">world</span></div>'
);
});
================================================
FILE: packages/unis-dom/src/server/index.ts
================================================
import { createRoot, createTokTik, readyForWork } from "@unis/core";
import { createOperator, ElementNode } from "./operator";
const operator = createOperator();
const toktik = createTokTik({
nextTick: (cb: VoidFunction) =>
Promise.resolve()
.catch((err) => console.error(err))
.then(() => cb()),
now: () => 0,
});
export const renderToString = (element: any) => {
const rootNode = new ElementNode("");
const rootFiber = createRoot(element, rootNode);
rootFiber.runtime = {
toktik,
operator,
};
readyForWork(rootFiber);
return rootNode.renderToString();
};
================================================
FILE: packages/unis-dom/src/server/operator.ts
================================================
import { isEvent, isNullish, Operator, Fiber, isText } from "@unis/core";
export class ElementNode {
children: ServerNode[] = [];
properties: Record<string, string> = {};
constructor(public tagName: string) {}
insertBefore(
node: ElementNode | TextNode,
child: ElementNode | TextNode | null
) {
this.children.push(node);
}
append(...nodes: ServerNode[]) {
this.children.push(...nodes);
}
renderToString(): string {
const children = this.children
.map((child) => child.renderToString())
.join("");
const propertiesStr = Object.keys(this.properties)
.map((key) => `${key}="${this.properties[key]}"`)
.join(" ");
const gap = propertiesStr ? " " : "";
return this.tagName
? `<${this.tagName}${gap}${propertiesStr}>${children}</${this.tagName}>`
: children;
}
}
export class TextNode {
constructor(public nodeValue: string) {}
renderToString() {
return this.nodeValue;
}
}
export type ServerNode = ElementNode | TextNode;
interface FiberDomServer extends Fiber {
el?: ServerNode;
}
export const createOperator = (): Operator => {
const createElement = (fiber: FiberDomServer) => {
const { tag: type } = fiber;
return isText(fiber)
? new TextNode(fiber.props.nodeValue + "")
: new ElementNode(type as string);
};
const insertBefore = (
containerFiber: FiberDomServer,
insertElement: ServerNode,
targetElement: ServerNode
) => {
(containerFiber.el as ElementNode).insertBefore(
insertElement,
targetElement
);
};
const firstChild = (fiber: FiberDomServer) =>
(fiber.el as ElementNode).children[0] ?? null;
const updateTextProperties = (fiber: FiberDomServer) => {
(fiber.el as TextNode).nodeValue = fiber.props.nodeValue + "";
};
const updateElementProperties = (fiber: FiberDomServer) => {
let { el, attrDiff } = fiber;
for (const [key, newValue, oldValue] of attrDiff || []) {
const newExist = !isNullish(newValue);
const oldExist = !isNullish(oldValue);
if (key === "ref") {
oldExist && (oldValue.current = undefined);
newExist && (newValue.current = el);
} else if (isEvent(key)) {
// nothing...
} else {
newExist
? ((el as ElementNode).properties[key] = newValue)
: delete (el as ElementNode).properties[key];
}
}
};
return {
nextElement() {},
matchElement: () => false,
remove() {},
nextSibling() {},
createElement,
insertBefore,
firstChild,
updateTextProperties,
updateElementProperties,
};
};
================================================
FILE: packages/unis-dom/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["**/*.test.*", "**/__test__"]
}
================================================
FILE: packages/unis-dom/tsconfig.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "build"
},
"include": ["src"]
}
================================================
FILE: packages/unis-dom/vitest.config.ts
================================================
import { defineConfig } from "vitest/config";
import { unisPreset } from "@unis/vite-preset";
export default defineConfig({
plugins: [unisPreset()],
test: {
include: ["**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
coverage: {
reporter: ["text", "json", "html"],
},
},
});
================================================
FILE: packages/unis-example/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
build
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
.DS_Store
================================================
FILE: packages/unis-example/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Unis</title>
</head>
<body>
<div id="root"></div>
<div id="dialog"></div>
<script type="module" src="./src/index.tsx"></script>
</body>
</html>
================================================
FILE: packages/unis-example/other.d.ts
================================================
declare module "*.css";
================================================
FILE: packages/unis-example/package.json
================================================
{
"name": "@unis/unis-example",
"version": "0.0.0",
"description": "",
"main": "index.js",
"private": "true",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/anuoua/unis.git"
},
"author": "anuoua",
"license": "MIT",
"bugs": {
"url": "https://github.com/anuoua/unis/issues"
},
"homepage": "https://github.com/anuoua/unis#readme",
"dependencies": {
"@unis/core": "workspace:^",
"@unis/dom": "workspace:^",
"@unis/router": "workspace:^",
"@unis/transition": "workspace:^"
},
"devDependencies": {
"@types/lodash": "^4.14.182",
"@unis/vite-preset": "workspace:^",
"autoprefixer": "^10.4.0",
"postcss": "^8.3.11",
"rollup-plugin-reassign": "^1.0.2",
"tailwindcss": "^3.3.1",
"vite": "^4.1.2"
}
}
================================================
FILE: packages/unis-example/postcss.config.js
================================================
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
================================================
FILE: packages/unis-example/src/Dialog.tsx
================================================
import { createPortal, useProps, useRef } from "@unis/core";
import { Item } from "./TodoItem";
interface DialogProps {
onClose: () => void;
onConfirm: (item: Item) => void;
item: Item | null;
}
export function Dialog(props: DialogProps) {
let { item, onClose, onConfirm } = useProps(props);
const portalRef = useRef<HTMLElement>();
const handleClose = (e: MouseEvent) => {
if ((e.target as HTMLElement) === portalRef.current) {
onClose();
}
};
const handleConfirm = () => {
onConfirm(item!);
};
return () =>
createPortal(
<div
ref={portalRef}
className="bg-gray-900 bg-opacity-40 fixed left-0 top-0 h-full w-full flex items-center justify-center"
onClick={handleClose}
>
<div className="w-96 bg-white rounded-md flex flex-col shadow-md">
<div className="flex-shrink-0 text-right px-4 pt-4">
<button onClick={() => props.onClose()}>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<div className="flex-1 p-10 flex">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-10 w-10 text-gray-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<div className="p-2 text-gray-700">
{" "}
Delete <span className="text-red-400">{item?.name}</span> ?
</div>
</div>
<div className="flex-shrink-0 text-right">
<button
className="m-2 p-2 px-5 bg-blue-500 text-gray-100 text-sm rounded-md focus:border-4 border-blue-300"
onClick={handleConfirm}
>
Confirm
</button>
</div>
</div>
</div>,
document.querySelector("#dialog")!
);
}
================================================
FILE: packages/unis-example/src/Todo.tsx
================================================
import { useState } from "@unis/core";
import { CSSTransition, TransitionGroup } from "@unis/transition";
import { Dialog } from "./Dialog";
import { Item, TodoItem } from "./TodoItem";
let count = 0;
let todos: any[] = [];
for (; count < 0; count++) {
todos.push({
id: count,
name: "",
editing: false,
canceled: false,
});
}
export function ToDo() {
let [todoList, setTodoList] = useState<any[]>(todos);
let [dialogVisible, setDialogVisible] = useState(false);
let [titleVisible, setTitleVisible] = useState(true);
let [currentItem, setCurrentItem] = useState<Item | null>(null);
const handleToggleTitle = () => {
setTitleVisible(!titleVisible);
setTimeout(() => {
setTitleVisible(true);
}, 200);
};
const handleAdd = (e: any) => {
if (e.key !== "Enter") return;
setTodoList([
...todoList,
{
id: ++count,
name: e.target.value,
editing: false,
canceled: false,
},
]);
e.target.value = "";
};
const handleClose = () => {
setDialogVisible(false);
};
const handleConfirm = (item: Item) => {
setDialogVisible(false);
setTodoList(todoList.filter((i) => i !== item));
};
const handleDelete = (item: Item) => {
setCurrentItem(item);
setTodoList(todoList.filter((i) => i !== item));
// setDialogVisible(true);
};
return () => (
<div className="w-80">
<CSSTransition timeout={400} in={titleVisible} classNames="scale">
<h1
className="font-mono text-white font-bold text-4xl mb-10 w-full text-center cursor-pointer"
onClick={handleToggleTitle}
>
TODO
</h1>
</CSSTransition>
<input
type="text"
placeholder="Input here"
className="h-14 border-2 rounded-md px-2 mb-10 w-full"
onKeyPress={handleAdd}
/>
<ul className="h-80 w-full flex flex-col gap-5 overflow-auto">
<TransitionGroup>
{todoList.map((i: any) => (
<CSSTransition key={i.id} timeout={400} classNames="scale" appear>
<TodoItem item={i} onDelete={handleDelete} />
</CSSTransition>
))}
</TransitionGroup>
</ul>
{dialogVisible && (
<Dialog
item={currentItem}
onClose={handleClose}
onConfirm={handleConfirm}
/>
)}
</div>
);
}
================================================
FILE: packages/unis-example/src/TodoItem/index.module.css
================================================
.deleteIcon {
color: white;
}
================================================
FILE: packages/unis-example/src/TodoItem/index.tsx
================================================
import { use, memo, useEffect, useProps, useRef } from "@unis/core";
import { Update } from "../hooks/update";
import s from "./index.module.css";
export interface Item {
id: number;
name: string;
editing: boolean;
canceled: boolean;
}
interface TodoItemProps {
item: Item;
onDelete: (item: Item) => any;
}
export const TodoItem = memo((props: TodoItemProps) => {
let { item, onDelete } = useProps(props);
let [render] = use(Update());
let { editing, canceled, name } = use(() => item);
const inputRef = useRef();
const handleEditing = () => {
if (canceled) return;
item.editing = true;
render();
};
const handleClick = () => {
onDelete(item);
};
const handleCancel = () => {
console.log(inputRef.current);
item.canceled = !item.canceled;
render();
};
const handleKeyDown = (e: any) => {
if (e.key !== "Enter") return;
item.name = e.target.value;
item.editing = false;
render();
};
const handleBlur = () => {
item.editing = false;
render();
};
useEffect(
() => {
item.name = item.name + "x";
render();
},
() => [item.canceled]
);
return () => {
// console.log("TodoItem render");
return (
<li className="flex flex-row items-center justify-between gap-5 bg-white bg-opacity-50 rounded-md p-5 shadow-md">
{editing ? (
<input
ref={inputRef}
value={name}
className="flex-grow min-w-0"
type="text"
onKeyDown={handleKeyDown}
onBlur={handleBlur}
></input>
) : canceled ? (
<del
className="flex-grow min-w-0 overflow-ellipsis overflow-hidden"
onClick={handleEditing}
>
{name}
</del>
) : (
<span
className="flex-grow min-w-0 overflow-ellipsis overflow-hidden"
onClick={handleEditing}
>
{name}
</span>
)}
<i
className={`${s.deleteIcon} cursor-pointer`}
onClick={handleCancel}
title={canceled ? "Revert" : "Cancel"}
>
{canceled ? (
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6 text-gray-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"
/>
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6 text-gray-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
)}
</i>
<i
className={`${s.deleteIcon} cursor-pointer`}
onClick={handleClick}
title="Delete"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6 text-gray-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
</i>
</li>
);
};
});
================================================
FILE: packages/unis-example/src/Welcome/index.module.css
================================================
.msg {
font-size: 50px;
font-weight: bold;
background: -webkit-linear-gradient(0deg,#e339bc 25%,#4f5b94);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
transition: all .3s ease;
}
.msg:hover {
cursor: pointer;
transform: scale(1.2);
background: -webkit-linear-gradient(0deg,#4f5b94 25%,#e339bc);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
================================================
FILE: packages/unis-example/src/Welcome/index.tsx
================================================
import { Link } from "@unis/router";
import s from "./index.module.css";
export const Welcome = () => {
return () => (
<Link to="main" className={s.msg}>
Unis Todo
</Link>
);
};
================================================
FILE: packages/unis-example/src/global.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
html,
body,
#root {
height: 100%;
}
.scale-appear {
transform: scale(0) translateZ(0);
}
.scale-appear-active {
transform: scale(1) translateZ(0);
transition: all 0.4s ease;
}
.scale-appear-done {
transform: scale(1) translateZ(0);
}
.scale-enter {
transform: scale(0) translateZ(0);
}
.scale-enter-active {
transform: scale(1) translateZ(0);
transition: all 0.4s ease;
}
.scale-enter-done {
transform: scale(1) translateZ(0);
}
.scale-exit {
transform: scale(1) translateZ(0);
}
.scale-exit-active {
transform: scale(0) translateZ(0);
transition: all 0.4s ease;
}
.scale-exit-done {
transform: scale(0) translateZ(0);
}
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: all 0.4s ease;
}
.fade-enter-done {
opacity: 1;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: all 0.4s ease;
}
.fade-exit-done {
opacity: 0;
}
================================================
FILE: packages/unis-example/src/hooks/update.ts
================================================
import { useState } from "@unis/core";
export const Update = () => {
let [count, setCount] = useState(1);
const update = () => setCount(++count);
return () => [update];
};
================================================
FILE: packages/unis-example/src/index.module.css
================================================
.background_img {
background: url(./bg.jpeg);
z-index: -1;
background-size: cover;
background-position: center;
}
================================================
FILE: packages/unis-example/src/index.tsx
================================================
import {
Fragment,
// useProps,
// useState,
// useEffect
} from "@unis/core";
import { render } from "@unis/dom";
import { ToDo } from "./Todo";
import "./global.css";
import s from "./index.module.css";
import { BrowserRouter, Redirect, Outlet, Route, Routes } from "@unis/router";
import { Welcome } from "./Welcome";
// const Bpp = (props: { time: number; msg: string }) => {
// let { time, msg } = useProps(props);
// let [visible, setVisible] = useState(false);
// useEffect(
// () => {
// setVisible(true);
// setTimeout(() => {
// console.log("timeout");
// setVisible(false);
// }, 0);
// },
// () => []
// );
// setTimeout(() => {
// setVisible(true);
// }, time);
// return () => (visible ? <div>{msg}</div> : false);
// };
const App = () => {
return () => (
<Fragment>
<div className={`${s.background_img} absolute h-full w-full`}></div>
<>
{/* <Bpp time={0} msg="Bpp" /> */}
{/* <Bpp time={1000} msg="Bpp2" /> */}
</>
{/* <Bpp time={1500} msg="Bpp3" /> */}
<div
className={`flex justify-center flex-col items-center h-full relative backdrop-filter backdrop-blur-lg`}
>
<Outlet />
</div>
</Fragment>
);
};
render(
<BrowserRouter>
<Routes path="/" element={<App />}>
<Route element={<Welcome />} />
<Route path="main" element={<ToDo />} />
<Route path="*" element={<Redirect to="main" />} />
</Routes>
</BrowserRouter>,
document.querySelector("#root")!
);
================================================
FILE: packages/unis-example/tailwind.config.js
================================================
module.exports = {
content: ["./public/**/*.html", "./src/**/*.{js,jsx,ts,tsx,vue}"],
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
================================================
FILE: packages/unis-example/tsconfig.json
================================================
{
"extends": "../../tsconfig.json",
"include": ["src", "other.d.ts"]
}
================================================
FILE: packages/unis-example/vite.config.js
================================================
import { defineConfig } from "vite";
import { unisPreset } from "@unis/vite-preset";
export default defineConfig({
plugins: [unisPreset()],
});
================================================
FILE: packages/unis-router/.gitignore
================================================
build/
coverage/
dist/
================================================
FILE: packages/unis-router/README.md
================================================
# Unis Router
Router for unis, inspire by [React Router V6](https://github.com/remix-run/react-router).
## Install
```shell
npm i @unis/router
```
## Usage
Unis router's api is partial same as React Router V6.
example
```javascript
import { BrowserRouter, Routes, Route, Outlet } from '@unis/router'
const Dashboard = () => {
return () => (
<div>
dashboard
<Outlet /> // hello
</div>
)
}
const App = () => {
return () => (
<div>
<header>App</header>
<Routes path="dashboard" element={<Dashboard />}>
<Route path="hello" element={<div>hello</div>} />
</Routes>
</div>
)
}
render(
<BrowserRouter basename="admin">
<App />
</BrowserRouter>, document.querySelector('#app'))
```
================================================
FILE: packages/unis-router/package.json
================================================
{
"name": "@unis/router",
"version": "0.1.0",
"description": "Unis router component",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"typings": "dist/index.d.ts",
"scripts": {
"build": "rimraf build && rimraf dist && tsc -p tsconfig.json && rollup --config",
"build:dev": "cross-env NODE_ENV=development pnpm build",
"test": "vitest run --coverage",
"test:watch": "vitest -w"
},
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs"
}
},
"keywords": [
"unis",
"router"
],
"files": [
"dist"
],
"author": "anuoua",
"peerDependencies": {
"@unis/core": "workspace:^"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/anuoua/unis/issues"
},
"homepage": "https://github.com/anuoua/unis/tree/main/packages/unis-router",
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.2",
"@types/node": "^18.15.11",
"@unis/core": "workspace:^",
"@unis/vite-preset": "workspace:^",
"@vitest/coverage-c8": "^0.29.8",
"cross-env": "^7.0.3",
"esbuild": "^0.17.15",
"rimraf": "^4.4.1",
"rollup": "^3.20.2",
"rollup-plugin-dts": "^5.3.0",
"rollup-plugin-esbuild": "^5.0.0",
"rollup-plugin-reassign": "^1.0.3",
"typescript": "^5.0.3",
"vitest": "^0.29.8"
},
"dependencies": {
"history": "^5.3.0"
}
}
================================================
FILE: packages/unis-router/rollup.config.mjs
================================================
import esbuild from "rollup-plugin-esbuild";
import dts from "rollup-plugin-dts";
import { defineConfig } from "rollup";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import { reassign } from "rollup-plugin-reassign";
import { unisFns } from "@unis/core";
const configGen = (format) =>
defineConfig({
input: "src/index.ts",
external: [/^@unis/, "history"],
output: [
{
dir: "dist",
entryFileNames: `index.${format === "esm" ? "mjs" : "js"}`,
format,
sourcemap: true,
},
],
plugins: [
nodeResolve(),
esbuild({
sourceMap: true,
target: "esnext",
jsx: "automatic",
jsxImportSource: "@unis/core",
}),
reassign({
include: ["**/*.(t|j)s?(x)"],
targetFns: {
"@unis/core": unisFns,
},
}),
],
});
const dtsRollup = () =>
defineConfig({
input: "build/index.d.ts",
output: [{ file: "dist/index.d.ts", format: "es" }],
plugins: [dts()],
});
const config = [configGen("cjs"), configGen("esm"), dtsRollup()];
export default config;
================================================
FILE: packages/unis-router/src/components/BrowserRouter.tsx
================================================
import { useProps, useLayoutEffect, useState, useMemo } from "@unis/core";
import { createBrowserHistory, BrowserHistory } from "history";
import { LocationContext, RouterContext } from "../context";
export interface BrowserRouterProps {
children?: JSX.Element | JSX.Element[];
history?: BrowserHistory;
basename?: string;
}
export const BrowserRouter = (p: BrowserRouterProps) => {
let { children, history, basename } = useProps(p);
const historyInstance = history ?? createBrowserHistory();
let [location, setLocation] = useState(historyInstance.location);
const navigationContextValue = {
basename: basename ?? "",
history: historyInstance,
};
let locationContextValue = useMemo(
() => ({ location }),
() => [location]
);
useLayoutEffect(
() => {
const unlisten = historyInstance.listen(({ location }) => {
setLocation(location);
});
return () => unlisten();
},
() => []
);
return () => (
<RouterContext.Provider value={navigationContextValue}>
<LocationContext.Provider value={locationContextValue}>
{children}
</LocationContext.Provider>
</RouterContext.Provider>
);
};
================================================
FILE: packages/unis-router/src/components/Link.tsx
================================================
import {
AnchorHTMLAttributes,
ElementAttrs,
use,
useContext,
useProps,
} from "@unis/core";
import { RouterContext } from "../context";
import { uTargetPath } from "../hooks/uTargetPath";
export type LinkProps = Partial<
Omit<ElementAttrs<AnchorHTMLAttributes>, "href"> & {
to: string;
}
>;
export const Link = (p: LinkProps) => {
let { to, children, onClick, ...rest } = useProps(p);
let { history } = useContext(RouterContext);
let targetPath = use(uTargetPath(() => ({ to })));
const handleJump = (e: MouseEvent) => {
const target = (e.target as HTMLAnchorElement).target;
if (
e.button === 0 &&
(!target || target === "_self") &&
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
) {
if (history.location.pathname !== targetPath) {
history.push(targetPath);
} else {
history.replace(targetPath);
}
e.preventDefault();
}
onClick?.(e);
};
return () => (
<a {...rest} href={targetPath} onClick={handleJump}>
{children}
</a>
);
};
================================================
FILE: packages/unis-router/src/components/NavLink.tsx
================================================
import { HTMLAttributes, use, useContext, useProps } from "@unis/core";
import { RouteContext } from "../context";
import { uTargetPath } from "../hooks/uTargetPath";
import { Link, LinkProps } from "./Link";
export type LinkStyle = (isActive: boolean) => HTMLAttributes["style"];
export type LinkClassName = (isActive: boolean) => HTMLAttributes["className"];
export type NavLinkProps = Omit<LinkProps, "style" | "className"> & {
style?: LinkStyle;
className?: LinkClassName;
};
export const NavLink = (p: NavLinkProps) => {
let { style, className, to, ...rest } = useProps(p);
let { matches } = useContext(RouteContext);
let targetPath = use(uTargetPath(() => ({ to })));
let isActive = use(() => !!matches.find((i) => i.pathname === targetPath));
return () => (
<Link
{...rest}
to={to}
style={style?.(isActive)}
className={className?.(isActive)}
/>
);
};
================================================
FILE: packages/unis-router/src/components/Outlet.tsx
================================================
import { use, useContext } from "@unis/core";
import { RouteContext } from "../context";
export const Outlet = () => {
let { matches, route } = useContext(RouteContext);
let nextRoute = use(() => matches[matches.findIndex((i) => i === route) + 1]);
let { element, ...rest } = use(() => route ?? {});
return () =>
route ? (
<RouteContext.Provider value={{ route: nextRoute ?? rest, matches }}>
{element}
</RouteContext.Provider>
) : null;
};
================================================
FILE: packages/unis-router/src/components/Redirect.tsx
================================================
import { useContext, useEffect, useProps } from "@unis/core";
import { RouteContext, RouterContext } from "../context";
import { resolvePath } from "../utils";
export interface RedirectProps {
to?: string;
replace?: boolean;
}
export const Redirect = (p: RedirectProps) => {
let { to, replace = true } = useProps(p);
let { history } = useContext(RouterContext);
let { route } = useContext(RouteContext);
useEffect(
() => {
if (route) {
const pathname = resolvePath(route.pathname!, to ?? "");
replace ? history.replace(pathname) : history.push(pathname);
}
},
() => []
);
return () => null;
};
================================================
FILE: packages/unis-router/src/components/Route.tsx
================================================
import { RouteData } from "../types";
export type RouteProps = Omit<RouteData, "children"> & {
children?: JSX.Element | JSX.Element[];
};
export const Route = (p: RouteProps) => null;
================================================
FILE: packages/unis-router/src/components/Routes.tsx
================================================
import { cloneElement, FiberNode, use, useProps } from "@unis/core";
import { uRouter } from "../hooks/uRouter";
import { Route } from "./Route";
import { RouteData } from "../types";
export type RoutesProps = Omit<RouteData, "children"> & {
children?: JSX.Element | JSX.Element[];
};
export const Routes = (p: RoutesProps) => {
let { children, path, element: incomeElement } = useProps(p);
let realChildren = use(() => flatChildren(children));
let routes = use(() => realChildren.map((node) => pick(node)));
let element = use(
uRouter(() => [
{
path,
element: incomeElement,
children: routes,
} as RouteData,
])
);
function pick(node: FiberNode): RouteData {
return {
...node.props,
path: node.props.path,
element: cloneElement(node.props.element),
children: flatChildren(node.props.children).map(pick),
};
}
function flatChildren(children: JSX.Element | JSX.Element[]) {
return ([] as FiberNode[])
.concat(children as FiberNode[])
.filter((child) => child?.tag === Route);
}
return () => element;
};
================================================
FILE: packages/unis-router/src/context.tsx
================================================
import { createContext } from "@unis/core";
import { BrowserHistory, Location } from "history";
import { MatchRoute } from "./types";
export interface RouteContextValue {
route: MatchRoute;
matches: MatchRoute[];
}
export const RouteContext = createContext<RouteContextValue>({
route: undefined!,
matches: [],
});
export interface RouterContextValue {
history: BrowserHistory;
basename: string;
}
export const RouterContext = createContext<RouterContextValue>(undefined!);
export interface LocationContextValue {
location: Location;
}
export const LocationContext = createContext<LocationContextValue>(undefined!);
================================================
FILE: packages/unis-router/src/hooks/uHistory.ts
================================================
import { useContext } from "@unis/core";
import { RouterContext } from "../context";
export const uHistory = () => {
let { history } = useContext(RouterContext);
return () => history!;
};
================================================
FILE: packages/unis-router/src/hooks/uLocation.ts
================================================
import { use } from "@unis/core";
import { uHistory } from "./uHistory";
export const uLocation = () => {
let { location } = use(uHistory());
return () => location;
};
================================================
FILE: packages/unis-router/src/hooks/uParams.ts
================================================
import { useContext } from "@unis/core";
import { RouteContext } from "../context";
export const uParams = <T = Record<string, any>>() => {
let { route } = useContext(RouteContext);
return () => route?.params as unknown as T;
};
================================================
FILE: packages/unis-router/src/hooks/uRouter.tsx
================================================
import { use, useContext } from "@unis/core";
import { RouteContext, RouterContext } from "../context";
import { Outlet } from "../components/Outlet";
import { RouteData } from "../types";
import { matchRoutes } from "../utils";
export const uRouter = (configFn: () => RouteData[]) => {
let routerData = use(configFn);
let { history, basename } = useContext(RouterContext);
let location = use(() => history?.location!);
let wrapedRouterData = use(() => [
{
path: basename,
element: <Outlet />,
children: routerData,
} as RouteData,
]);
let matches = use(() => matchRoutes(location.pathname, wrapedRouterData));
return () => (
<RouteContext.Provider value={{ route: matches.at(0)!, matches }}>
<Outlet />
</RouteContext.Provider>
);
};
================================================
FILE: packages/unis-router/src/hooks/uTargetPath.tsx
================================================
import { use, useContext } from "@unis/core";
import { RouteContext } from "../context";
import { resolvePath } from "../utils";
export interface Options {
to?: string;
}
export const uTargetPath = (opts: () => Options) => {
let { to } = use(opts);
let { route } = useContext(RouteContext);
let targetPath = use(() => resolvePath(route.pathname ?? "", to ?? ""));
return () => targetPath;
};
================================================
FILE: packages/unis-router/src/index.ts
================================================
export * from "./components/BrowserRouter";
export * from "./context";
export * from "./components/Routes";
export * from "./components/Route";
export * from "./components/Outlet";
export * from "./components/Link";
export * from "./components/NavLink";
export * from "./components/Redirect";
export * from "./hooks/uHistory";
export * from "./hooks/uRouter";
export * from "./hooks/uLocation";
export * from "./hooks/uHistory";
export * from "./hooks/uParams";
================================================
FILE: packages/unis-router/src/types.ts
================================================
export interface RouteData {
path?: string;
element?: JSX.Element;
children?: RouteData[];
}
export interface MatchRoute extends RouteData {
pathname?: string;
params?: Record<string, string>;
}
================================================
FILE: packages/unis-router/src/utils.test.tsx
================================================
import { expect, it } from "vitest";
import { RouteData } from "./types";
import { matchRoutes, resolvePath } from "./utils";
const getRoutes = (): RouteData[] => [
{
path: "/",
children: [
{
path: "home/:from/",
children: [
{ path: "*" },
{
path: "post/:id",
},
{
path: "post/1",
children: [
{
children: [{ path: "xx" }],
},
],
},
],
},
],
},
];
it("root match", () => {
const matchedRoutes = matchRoutes("/home/www", getRoutes());
expect(matchedRoutes).toMatchObject([
{ path: "/", params: {}, pathname: "/" },
{
path: "home/:from/",
params: { from: "www" },
pathname: "/home/www",
},
]);
});
it("match test1", () => {
const matchedRoutes = matchRoutes("/home/www/post", getRoutes());
expect(match
gitextract_322wan4z/ ├── .github/ │ └── workflows/ │ ├── unis-babel-preset.yml │ ├── unis-core.yml │ ├── unis-dom.yml │ ├── unis-router.yml │ ├── unis-transition.yml │ └── unis-vite-preset.yml ├── .gitignore ├── .vscode/ │ └── settings.json ├── LICENSE ├── README-zh_CN.md ├── README.md ├── assets/ │ └── logo.txt ├── package.json ├── packages/ │ ├── unis-babel-preset/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src/ │ │ │ └── index.ts │ │ ├── test/ │ │ │ └── index.test.ts │ │ └── tsconfig.json │ ├── unis-core/ │ │ ├── .gitignore │ │ ├── index.d.ts │ │ ├── jsx-runtime/ │ │ │ ├── jsx-dev-runtime.d.ts │ │ │ ├── jsx-dev-runtime.js │ │ │ ├── jsx-dev-runtime.mjs │ │ │ ├── jsx-runtime.d.ts │ │ │ ├── jsx-runtime.js │ │ │ └── jsx-runtime.mjs │ │ ├── package.json │ │ ├── rollup.config.mjs │ │ ├── src/ │ │ │ ├── api/ │ │ │ │ ├── use.ts │ │ │ │ ├── useContext.ts │ │ │ │ ├── useEffect.ts │ │ │ │ ├── useId.ts │ │ │ │ ├── useLayoutEffect.ts │ │ │ │ ├── useMemo.ts │ │ │ │ ├── useProps.ts │ │ │ │ ├── useReducer.ts │ │ │ │ ├── useRef.ts │ │ │ │ ├── useState.ts │ │ │ │ └── utils.ts │ │ │ ├── commit.ts │ │ │ ├── context.ts │ │ │ ├── createTokTik.ts │ │ │ ├── diff.ts │ │ │ ├── fiber.ts │ │ │ ├── h.ts │ │ │ ├── index.ts │ │ │ ├── reconcile.ts │ │ │ ├── reconcileWalkHooks/ │ │ │ │ ├── context.ts │ │ │ │ ├── effect.ts │ │ │ │ └── preElFiber.ts │ │ │ ├── svg.ts │ │ │ └── utils.ts │ │ ├── test/ │ │ │ └── utils.test.ts │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── types/ │ │ │ └── jsx.d.ts │ │ └── vitest.config.ts │ ├── unis-dom/ │ │ ├── .gitignore │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── rollup.config.mjs │ │ ├── server.d.ts │ │ ├── src/ │ │ │ ├── browser/ │ │ │ │ ├── __test__/ │ │ │ │ │ ├── context.test.tsx │ │ │ │ │ ├── dom.test.tsx │ │ │ │ │ ├── effect.test.tsx │ │ │ │ │ ├── hydrate.test.tsx │ │ │ │ │ ├── memo.test.tsx │ │ │ │ │ ├── portal.test.tsx │ │ │ │ │ ├── reconcile.test.tsx │ │ │ │ │ ├── util.ts │ │ │ │ │ └── utils.test.ts │ │ │ │ ├── const.ts │ │ │ │ ├── index.ts │ │ │ │ ├── operator.ts │ │ │ │ ├── render.ts │ │ │ │ └── toktik.ts │ │ │ └── server/ │ │ │ ├── __test__/ │ │ │ │ └── server.test.tsx │ │ │ ├── index.ts │ │ │ └── operator.ts │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── unis-example/ │ │ ├── .gitignore │ │ ├── index.html │ │ ├── other.d.ts │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── src/ │ │ │ ├── Dialog.tsx │ │ │ ├── Todo.tsx │ │ │ ├── TodoItem/ │ │ │ │ ├── index.module.css │ │ │ │ └── index.tsx │ │ │ ├── Welcome/ │ │ │ │ ├── index.module.css │ │ │ │ └── index.tsx │ │ │ ├── global.css │ │ │ ├── hooks/ │ │ │ │ └── update.ts │ │ │ ├── index.module.css │ │ │ └── index.tsx │ │ ├── tailwind.config.js │ │ ├── tsconfig.json │ │ └── vite.config.js │ ├── unis-router/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── rollup.config.mjs │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── BrowserRouter.tsx │ │ │ │ ├── Link.tsx │ │ │ │ ├── NavLink.tsx │ │ │ │ ├── Outlet.tsx │ │ │ │ ├── Redirect.tsx │ │ │ │ ├── Route.tsx │ │ │ │ └── Routes.tsx │ │ │ ├── context.tsx │ │ │ ├── hooks/ │ │ │ │ ├── uHistory.ts │ │ │ │ ├── uLocation.ts │ │ │ │ ├── uParams.ts │ │ │ │ ├── uRouter.tsx │ │ │ │ └── uTargetPath.tsx │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ ├── utils.test.tsx │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── unis-transition/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── rollup.config.mjs │ │ ├── src/ │ │ │ ├── CSSTransition.ts │ │ │ ├── TransitionGroup.ts │ │ │ ├── hooks/ │ │ │ │ ├── uInstance.ts │ │ │ │ ├── uTransition.ts │ │ │ │ ├── uUpdate.ts │ │ │ │ └── uWatch.ts │ │ │ └── index.ts │ │ └── tsconfig.json │ └── unis-vite-preset/ │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src/ │ │ └── index.ts │ └── tsconfig.json ├── pnpm-workspace.yaml └── tsconfig.json
SYMBOL INDEX (170 symbols across 36 files)
FILE: packages/unis-babel-preset/src/index.ts
function unisPreset (line 8) | function unisPreset() {
FILE: packages/unis-core/src/api/use.ts
function use (line 8) | function use<T extends (...args: any[]) => any>(fn: T, raFn?: Function) {
FILE: packages/unis-core/src/api/useContext.ts
function useContext (line 5) | function useContext<T extends Context>(ctx: T) {
FILE: packages/unis-core/src/api/useId.ts
function useId (line 14) | function useId() {
FILE: packages/unis-core/src/api/useMemo.ts
function useMemo (line 46) | function useMemo<T extends unknown>(
FILE: packages/unis-core/src/api/useProps.ts
function useProps (line 4) | function useProps<T>(p: T) {
FILE: packages/unis-core/src/api/useReducer.ts
type Reducer (line 6) | type Reducer<T, T2> = (state: T, action: T2) => T;
function useReducer (line 85) | function useReducer<T, T2>(reducerFn: Reducer<T, T2>, initial: T) {
FILE: packages/unis-core/src/api/useRef.ts
type Ref (line 1) | interface Ref<T> {
function useRef (line 7) | function useRef<T>(value?: T) {
FILE: packages/unis-core/src/api/useState.ts
function useState (line 13) | function useState<T>(initial?: T) {
FILE: packages/unis-core/src/api/utils.ts
type EFFECT_TYPE (line 5) | enum EFFECT_TYPE {
type Effect (line 10) | type Effect = (() => (() => void) | void) & {
FILE: packages/unis-core/src/context.ts
type Context (line 5) | interface Context<T = any> {
type Dependency (line 11) | interface Dependency<T = any> {
function createContext (line 33) | function createContext<T>(initial: T) {
FILE: packages/unis-core/src/createTokTik.ts
type Task (line 1) | type Task = Function & { isTok?: any };
FILE: packages/unis-core/src/diff.ts
type AttrDiff (line 25) | type AttrDiff = [string, any, any][];
FILE: packages/unis-core/src/fiber.ts
type ReconcileState (line 6) | interface ReconcileState {
type FLAG (line 16) | enum FLAG {
type FlagName (line 24) | type FlagName = "flag" | "childFlag" | "commitFlag";
type FiberEl (line 35) | type FiberEl = unknown;
type FiberType (line 36) | type FiberType =
type MemorizeState (line 42) | interface MemorizeState {
type TokTik (line 49) | interface TokTik {
type Operator (line 56) | interface Operator {
type Runtime (line 82) | interface Runtime {
type Fiber (line 87) | interface Fiber {
constant TEXT (line 152) | const TEXT = Symbol("$$Text");
constant ELEMENT (line 153) | const ELEMENT = Symbol("$$Element");
constant PORTAL (line 154) | const PORTAL = Symbol("$$Portal");
constant PROVIDER (line 155) | const PROVIDER = Symbol("$$Provider");
constant COMPONENT (line 156) | const COMPONENT = Symbol("$$Component");
type WalkHook (line 174) | interface WalkHook {
type WalkHookKeys (line 182) | type WalkHookKeys = keyof WalkHook;
type WalkHookList (line 184) | type WalkHookList = {
type ContainerElement (line 274) | type ContainerElement = Exclude<FiberEl, Text>;
FILE: packages/unis-core/src/h.ts
constant FGMT (line 103) | const FGMT = Fragment;
FILE: packages/unis-core/types/jsx.d.ts
type FiberNode (line 82) | interface FiberNode {
type CSSProperties (line 88) | interface CSSProperties
type Booleanish (line 101) | type Booleanish = boolean | "true" | "false";
type Numberish (line 102) | type Numberish = number | string;
type AriaAttributes (line 105) | interface AriaAttributes {
type AriaRole (line 304) | type AriaRole =
type StyleValue (line 377) | type StyleValue = string | CSSProperties | Array<StyleValue>;
type CSValue (line 379) | type CSValue = string | number | boolean | undefined | null;
type CSObject (line 380) | type CSObject = Record<string, CSValue>;
type CSArray (line 381) | type CSArray = (CSValue | CSObject | CSArray)[];
type HTMLAttributes (line 383) | interface HTMLAttributes extends AriaAttributes, EventHandlers<Events> {
type HTMLAttributeReferrerPolicy (line 454) | type HTMLAttributeReferrerPolicy =
type HTMLAttributeAnchorTarget (line 465) | type HTMLAttributeAnchorTarget =
type AnchorHTMLAttributes (line 472) | interface AnchorHTMLAttributes extends HTMLAttributes {
type AudioHTMLAttributes (line 484) | interface AudioHTMLAttributes extends MediaHTMLAttributes {}
type AreaHTMLAttributes (line 486) | interface AreaHTMLAttributes extends HTMLAttributes {
type BaseHTMLAttributes (line 499) | interface BaseHTMLAttributes extends HTMLAttributes {
type BlockquoteHTMLAttributes (line 504) | interface BlockquoteHTMLAttributes extends HTMLAttributes {
type ButtonHTMLAttributes (line 508) | interface ButtonHTMLAttributes extends HTMLAttributes {
type CanvasHTMLAttributes (line 522) | interface CanvasHTMLAttributes extends HTMLAttributes {
type ColHTMLAttributes (line 527) | interface ColHTMLAttributes extends HTMLAttributes {
type ColgroupHTMLAttributes (line 532) | interface ColgroupHTMLAttributes extends HTMLAttributes {
type DataHTMLAttributes (line 536) | interface DataHTMLAttributes extends HTMLAttributes {
type DetailsHTMLAttributes (line 540) | interface DetailsHTMLAttributes extends HTMLAttributes {
type DelHTMLAttributes (line 544) | interface DelHTMLAttributes extends HTMLAttributes {
type DialogHTMLAttributes (line 549) | interface DialogHTMLAttributes extends HTMLAttributes {
type EmbedHTMLAttributes (line 553) | interface EmbedHTMLAttributes extends HTMLAttributes {
type FieldsetHTMLAttributes (line 560) | interface FieldsetHTMLAttributes extends HTMLAttributes {
type FormHTMLAttributes (line 566) | interface FormHTMLAttributes extends HTMLAttributes {
type HtmlHTMLAttributes (line 577) | interface HtmlHTMLAttributes extends HTMLAttributes {
type IframeHTMLAttributes (line 581) | interface IframeHTMLAttributes extends HTMLAttributes {
type ImgHTMLAttributes (line 604) | interface ImgHTMLAttributes extends HTMLAttributes {
type InsHTMLAttributes (line 618) | interface InsHTMLAttributes extends HTMLAttributes {
type HTMLInputTypeAttribute (line 623) | type HTMLInputTypeAttribute =
type InputHTMLAttributes (line 648) | interface InputHTMLAttributes extends HTMLAttributes {
type KeygenHTMLAttributes (line 692) | interface KeygenHTMLAttributes extends HTMLAttributes {
type LabelHTMLAttributes (line 702) | interface LabelHTMLAttributes extends HTMLAttributes {
type LiHTMLAttributes (line 707) | interface LiHTMLAttributes extends HTMLAttributes {
type LinkHTMLAttributes (line 711) | interface LinkHTMLAttributes extends HTMLAttributes {
type MapHTMLAttributes (line 726) | interface MapHTMLAttributes extends HTMLAttributes {
type MenuHTMLAttributes (line 730) | interface MenuHTMLAttributes extends HTMLAttributes {
type MediaHTMLAttributes (line 734) | interface MediaHTMLAttributes extends HTMLAttributes {
type MetaHTMLAttributes (line 747) | interface MetaHTMLAttributes extends HTMLAttributes {
type MeterHTMLAttributes (line 755) | interface MeterHTMLAttributes extends HTMLAttributes {
type QuoteHTMLAttributes (line 765) | interface QuoteHTMLAttributes extends HTMLAttributes {
type ObjectHTMLAttributes (line 769) | interface ObjectHTMLAttributes extends HTMLAttributes {
type OlHTMLAttributes (line 781) | interface OlHTMLAttributes extends HTMLAttributes {
type OptgroupHTMLAttributes (line 787) | interface OptgroupHTMLAttributes extends HTMLAttributes {
type OptionHTMLAttributes (line 792) | interface OptionHTMLAttributes extends HTMLAttributes {
type OutputHTMLAttributes (line 799) | interface OutputHTMLAttributes extends HTMLAttributes {
type ParamHTMLAttributes (line 805) | interface ParamHTMLAttributes extends HTMLAttributes {
type ProgressHTMLAttributes (line 810) | interface ProgressHTMLAttributes extends HTMLAttributes {
type SlotHTMLAttributes (line 815) | interface SlotHTMLAttributes extends HTMLAttributes {
type ScriptHTMLAttributes (line 819) | interface ScriptHTMLAttributes extends HTMLAttributes {
type SelectHTMLAttributes (line 833) | interface SelectHTMLAttributes extends HTMLAttributes {
type SourceHTMLAttributes (line 845) | interface SourceHTMLAttributes extends HTMLAttributes {
type StyleHTMLAttributes (line 855) | interface StyleHTMLAttributes extends HTMLAttributes {
type TableHTMLAttributes (line 862) | interface TableHTMLAttributes extends HTMLAttributes {
type TextareaHTMLAttributes (line 869) | interface TextareaHTMLAttributes extends HTMLAttributes {
type TdHTMLAttributes (line 887) | interface TdHTMLAttributes extends HTMLAttributes {
type ThHTMLAttributes (line 899) | interface ThHTMLAttributes extends HTMLAttributes {
type TimeHTMLAttributes (line 908) | interface TimeHTMLAttributes extends HTMLAttributes {
type TrackHTMLAttributes (line 912) | interface TrackHTMLAttributes extends HTMLAttributes {
type VideoHTMLAttributes (line 920) | interface VideoHTMLAttributes extends MediaHTMLAttributes {
type WebViewHTMLAttributes (line 929) | interface WebViewHTMLAttributes extends HTMLAttributes {
type SVGAttributes (line 949) | interface SVGAttributes extends AriaAttributes, EventHandlers<Events> {
type IntrinsicElementAttributes (line 1241) | interface IntrinsicElementAttributes {
type Events (line 1420) | interface Events {
type EventHandlers (line 1534) | type EventHandlers<E> = {
type ReservedProps (line 1538) | type ReservedProps = {
type ElementAttrs (line 1544) | type ElementAttrs<T> = T & ReservedProps;
type NativeElements (line 1546) | type NativeElements = {
type ElementTypes (line 1554) | type ElementTypes =
type Element (line 1563) | type Element =
type ElementClass (line 1569) | interface ElementClass {
type ElementAttributesProperty (line 1572) | interface ElementAttributesProperty {
type ElementChildrenAttribute (line 1575) | interface ElementChildrenAttribute {
type IntrinsicElements (line 1578) | interface IntrinsicElements extends NativeElements {
type IntrinsicAttributes (line 1583) | interface IntrinsicAttributes extends ReservedProps {}
FILE: packages/unis-dom/src/browser/const.ts
constant UNIS_ROOT (line 1) | const UNIS_ROOT = Symbol("unis_root");
FILE: packages/unis-dom/src/browser/operator.ts
type FiberDomEl (line 5) | type FiberDomEl = Element | DocumentFragment | SVGAElement | Text | Pare...
type FiberDom (line 7) | interface FiberDom extends Fiber {
FILE: packages/unis-dom/src/server/operator.ts
class ElementNode (line 3) | class ElementNode {
method constructor (line 8) | constructor(public tagName: string) {}
method insertBefore (line 10) | insertBefore(
method append (line 17) | append(...nodes: ServerNode[]) {
method renderToString (line 21) | renderToString(): string {
class TextNode (line 38) | class TextNode {
method constructor (line 39) | constructor(public nodeValue: string) {}
method renderToString (line 40) | renderToString() {
type ServerNode (line 45) | type ServerNode = ElementNode | TextNode;
type FiberDomServer (line 47) | interface FiberDomServer extends Fiber {
method nextElement (line 97) | nextElement() {}
method remove (line 99) | remove() {}
method nextSibling (line 100) | nextSibling() {}
FILE: packages/unis-example/src/Dialog.tsx
type DialogProps (line 4) | interface DialogProps {
function Dialog (line 10) | function Dialog(props: DialogProps) {
FILE: packages/unis-example/src/Todo.tsx
function ToDo (line 19) | function ToDo() {
FILE: packages/unis-example/src/TodoItem/index.tsx
type Item (line 5) | interface Item {
type TodoItemProps (line 12) | interface TodoItemProps {
FILE: packages/unis-router/src/components/BrowserRouter.tsx
type BrowserRouterProps (line 5) | interface BrowserRouterProps {
FILE: packages/unis-router/src/components/Link.tsx
type LinkProps (line 11) | type LinkProps = Partial<
FILE: packages/unis-router/src/components/NavLink.tsx
type LinkStyle (line 6) | type LinkStyle = (isActive: boolean) => HTMLAttributes["style"];
type LinkClassName (line 7) | type LinkClassName = (isActive: boolean) => HTMLAttributes["className"];
type NavLinkProps (line 9) | type NavLinkProps = Omit<LinkProps, "style" | "className"> & {
FILE: packages/unis-router/src/components/Redirect.tsx
type RedirectProps (line 5) | interface RedirectProps {
FILE: packages/unis-router/src/components/Route.tsx
type RouteProps (line 3) | type RouteProps = Omit<RouteData, "children"> & {
FILE: packages/unis-router/src/components/Routes.tsx
type RoutesProps (line 6) | type RoutesProps = Omit<RouteData, "children"> & {
function pick (line 25) | function pick(node: FiberNode): RouteData {
function flatChildren (line 34) | function flatChildren(children: JSX.Element | JSX.Element[]) {
FILE: packages/unis-router/src/context.tsx
type RouteContextValue (line 5) | interface RouteContextValue {
type RouterContextValue (line 15) | interface RouterContextValue {
type LocationContextValue (line 22) | interface LocationContextValue {
FILE: packages/unis-router/src/hooks/uTargetPath.tsx
type Options (line 5) | interface Options {
FILE: packages/unis-router/src/types.ts
type RouteData (line 1) | interface RouteData {
type MatchRoute (line 7) | interface MatchRoute extends RouteData {
FILE: packages/unis-router/src/utils.ts
constant SLASH (line 3) | const SLASH = "/";
constant DOT (line 4) | const DOT = ".";
FILE: packages/unis-transition/src/CSSTransition.ts
type TransitionProps (line 15) | interface TransitionProps {
FILE: packages/unis-transition/src/TransitionGroup.ts
type TransitionGroupProps (line 4) | interface TransitionGroupProps {
FILE: packages/unis-transition/src/hooks/uTransition.ts
constant UNMOUNTED (line 3) | const UNMOUNTED = "unmounted";
constant APPEARING (line 4) | const APPEARING = "appearing";
constant APPEARED (line 5) | const APPEARED = "appeared";
constant ENTERING (line 6) | const ENTERING = "entering";
constant ENTERED (line 7) | const ENTERED = "entered";
constant EXITING (line 8) | const EXITING = "exiting";
constant EXITED (line 9) | const EXITED = "exited";
type TimeoutObject (line 11) | type TimeoutObject = {
type TransitionTimeout (line 17) | type TransitionTimeout = number | TimeoutObject;
type uTransitionProps (line 19) | interface uTransitionProps {
function setTimer (line 89) | function setTimer(statusTimeout: number) {
FILE: packages/unis-vite-preset/src/index.ts
function unisPreset (line 5) | function unisPreset(): PluginOption[] {
Condensed preview — 145 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (231K chars).
[
{
"path": ".github/workflows/unis-babel-preset.yml",
"chars": 1404,
"preview": "name: \"@unis/babel-preset CI/CD\"\n\non:\n push:\n branches:\n - 'main'\n paths:\n - 'packages/unis-babel-prese"
},
{
"path": ".github/workflows/unis-core.yml",
"chars": 1262,
"preview": "name: \"@unis/core CI/CD\"\n\non:\n push:\n branches:\n - 'main'\n paths:\n - 'packages/unis-core/**'\n\njobs:\n\n "
},
{
"path": ".github/workflows/unis-dom.yml",
"chars": 1404,
"preview": "name: \"@unis/dom CI/CD\"\n\non:\n push:\n branches:\n - 'main'\n paths:\n - 'packages/unis-dom/**'\n\njobs:\n\n \"t"
},
{
"path": ".github/workflows/unis-router.yml",
"chars": 1421,
"preview": "name: \"@unis/router CI/CD\"\n\non:\n push:\n branches:\n - 'main'\n paths:\n - 'packages/unis-router/**'\n\njobs:"
},
{
"path": ".github/workflows/unis-transition.yml",
"chars": 867,
"preview": "name: \"@unis/transition CI/CD\"\n\non:\n push:\n branches:\n - 'main'\n paths:\n - 'packages/unis-transition/**"
},
{
"path": ".github/workflows/unis-vite-preset.yml",
"chars": 871,
"preview": "name: \"@unis/vite-preset CI/CD\"\n\non:\n push:\n branches:\n - 'main'\n paths:\n - 'packages/unis-vite-preset/"
},
{
"path": ".gitignore",
"chars": 50,
"preview": "# Dependency directories\nnode_modules/\n\n.DS_Store\n"
},
{
"path": ".vscode/settings.json",
"chars": 396,
"preview": "{\n \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n \"[typescriptreact]\": {\n \"editor.formatOnSave\": true\n },\n "
},
{
"path": "LICENSE",
"chars": 1070,
"preview": "MIT License\n\nCopyright (c) 2021-present anuoua\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "README-zh_CN.md",
"chars": 5611,
"preview": "<p align=\"center\">\n <img height=\"300\" src=\"./assets/logo.svg\">\n</p>\n\n[;\n\nexports.jsxDEV = h2;\nexports.Fragment = Fragment;\n"
},
{
"path": "packages/unis-core/jsx-runtime/jsx-dev-runtime.mjs",
"chars": 60,
"preview": "export { h2 as jsxDEV, Fragment } from \"../dist/index.mjs\";\n"
},
{
"path": "packages/unis-core/jsx-runtime/jsx-runtime.d.ts",
"chars": 41,
"preview": "declare module \"@unis/core/jsx-runtime\";\n"
},
{
"path": "packages/unis-core/jsx-runtime/jsx-runtime.js",
"chars": 122,
"preview": "const { h2, Fragment } = require(\"../dist/index.mjs\");\n\nexports.jsx = h2;\nexports.jsxs = h2;\nexports.Fragment = Fragment"
},
{
"path": "packages/unis-core/jsx-runtime/jsx-runtime.mjs",
"chars": 69,
"preview": "export { h2 as jsx, h2 as jsxs, Fragment } from \"../dist/index.mjs\";\n"
},
{
"path": "packages/unis-core/package.json",
"chars": 1810,
"preview": "{\n \"name\": \"@unis/core\",\n \"version\": \"1.2.5\",\n \"description\": \"Unis is a simpler and easier to use front-end framewor"
},
{
"path": "packages/unis-core/rollup.config.mjs",
"chars": 798,
"preview": "import dts from \"rollup-plugin-dts\";\nimport esbuild from \"rollup-plugin-esbuild\";\nimport { defineConfig } from \"rollup\";"
},
{
"path": "packages/unis-core/src/api/use.ts",
"chars": 549,
"preview": "import { getWF } from \"./utils\";\n\nexport function use<T extends (...args: any[]) => any>(fn: T): ReturnType<T>;\nexport f"
},
{
"path": "packages/unis-core/src/api/useContext.ts",
"chars": 795,
"preview": "import { Context } from \"../context\";\nimport { Fiber } from \"../fiber\";\nimport { use } from \"./use\";\n\nexport function us"
},
{
"path": "packages/unis-core/src/api/useEffect.ts",
"chars": 288,
"preview": "import { Effect, EFFECT_TYPE, getWF } from \"./utils\";\n\nexport const useEffect = (cb: Effect, depsFn?: () => any[]) => {\n"
},
{
"path": "packages/unis-core/src/api/useId.ts",
"chars": 347,
"preview": "import { Fiber } from \"../fiber\";\nimport { getWF } from \"./utils\";\nimport { use } from \"./use\";\nimport { generateId } fr"
},
{
"path": "packages/unis-core/src/api/useLayoutEffect.ts",
"chars": 227,
"preview": "import { useEffect } from \"./useEffect\";\nimport { Effect, EFFECT_TYPE } from \"./utils\";\n\nexport const useLayoutEffect = "
},
{
"path": "packages/unis-core/src/api/useMemo.ts",
"chars": 1242,
"preview": "import { use } from \"./use\";\nimport { Fiber, MemorizeState } from \"../fiber\";\nimport { arraysEqual } from \"../utils\";\nim"
},
{
"path": "packages/unis-core/src/api/useProps.ts",
"chars": 229,
"preview": "import { Fiber } from \"../fiber\";\nimport { use } from \"./use\";\n\nexport function useProps<T>(p: T) {\n return use(propsHo"
},
{
"path": "packages/unis-core/src/api/useReducer.ts",
"chars": 3228,
"preview": "import { Effect, markFiber } from \"./utils\";\nimport { use } from \"./use\";\nimport { Fiber, findRoot, findRuntime, Memoriz"
},
{
"path": "packages/unis-core/src/api/useRef.ts",
"chars": 207,
"preview": "export interface Ref<T> {\n current: T;\n}\n\nexport function useRef<T>(): Ref<T | undefined>;\nexport function useRef<T>(va"
},
{
"path": "packages/unis-core/src/api/useState.ts",
"chars": 456,
"preview": "import { use } from \"./use\";\nimport { reducerHof } from \"./useReducer\";\n\nexport const stateHof = <T extends any>(initial"
},
{
"path": "packages/unis-core/src/api/utils.ts",
"chars": 1690,
"preview": "import { Fiber, FLAG, mergeFlag } from \"../fiber\";\nimport { getWorkingFiber } from \"../reconcile\";\nimport { arraysEqual "
},
{
"path": "packages/unis-core/src/commit.ts",
"chars": 2793,
"preview": "import { clearEffects } from \"./api/utils\";\nimport {\n Fiber,\n findEls,\n FLAG,\n getContainerElFiber,\n graft,\n isCom"
},
{
"path": "packages/unis-core/src/context.ts",
"chars": 1507,
"preview": "import { useProps } from \"./api/useProps\";\nimport { useContext } from \"./api/useContext\";\nimport { PROVIDER, Fiber } fro"
},
{
"path": "packages/unis-core/src/createTokTik.ts",
"chars": 1483,
"preview": "export type Task = Function & { isTok?: any };\n\nexport const createTokTik = (options: {\n nextTick: (cb: VoidFunction, p"
},
{
"path": "packages/unis-core/src/diff.ts",
"chars": 9129,
"preview": "import {\n clearFlag,\n Fiber,\n FLAG,\n isElement,\n matchFlag,\n isPortal,\n isSame,\n mergeFlag,\n isComponent,\n Rec"
},
{
"path": "packages/unis-core/src/fiber.ts",
"chars": 7738,
"preview": "import { Effect } from \"./api/utils\";\nimport { Dependency } from \"./context\";\nimport { AttrDiff } from \"./diff\";\nimport "
},
{
"path": "packages/unis-core/src/h.ts",
"chars": 2440,
"preview": "import { isNum, isStr, keys, toArray } from \"./utils\";\nimport {\n COMPONENT,\n createFiber,\n ELEMENT,\n Fiber,\n FiberE"
},
{
"path": "packages/unis-core/src/index.ts",
"chars": 686,
"preview": "export * from \"./h\";\nexport * from \"./fiber\";\nexport * from \"./context\";\nexport * from \"./reconcile\";\nexport * from \"./u"
},
{
"path": "packages/unis-core/src/reconcile.ts",
"chars": 5673,
"preview": "import {\n clearAndRunEffects,\n clearEffects,\n Effect,\n EFFECT_TYPE,\n effectDepsEqual,\n runEffects,\n runStateEffec"
},
{
"path": "packages/unis-core/src/reconcileWalkHooks/context.ts",
"chars": 1097,
"preview": "import { createDependency, findDependency } from \"../context\";\nimport { createNext, Fiber, isProvider, WalkHook } from \""
},
{
"path": "packages/unis-core/src/reconcileWalkHooks/effect.ts",
"chars": 393,
"preview": "import { Fiber, WalkHook } from \"../fiber\";\n\nexport const pushEffect = (fiber: Fiber) => {\n fiber.reconcileState!.commi"
},
{
"path": "packages/unis-core/src/reconcileWalkHooks/preElFiber.ts",
"chars": 1432,
"preview": "import {\n Fiber,\n findLastElFiber,\n FLAG,\n isElement,\n isPortal,\n matchFlag,\n WalkHook,\n} from \"../fiber\";\n\nconst"
},
{
"path": "packages/unis-core/src/svg.ts",
"chars": 1121,
"preview": "// kebab svg attr keys\nexport const displayAttrs = [\n \"baselineShift\",\n \"alignmentBaseline\",\n \"clip\",\n \"clipPath\",\n "
},
{
"path": "packages/unis-core/src/utils.ts",
"chars": 2739,
"preview": "import type { CSArray, CSObject } from \"../types/jsx\";\nimport { displayAttrs } from \"./svg\";\n\nexport const keys = Object"
},
{
"path": "packages/unis-core/test/utils.test.ts",
"chars": 618,
"preview": "/**\n * @vitest-environment jsdom\n */\nimport { expect, it } from \"vitest\";\nimport { classes, svgKey, styleStr } from \"../"
},
{
"path": "packages/unis-core/tsconfig.build.json",
"chars": 87,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"include\": [\"src\"],\n \"exclude\": [\"**/*.test.*\"]\n}\n"
},
{
"path": "packages/unis-core/tsconfig.json",
"chars": 152,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"build\"\n },\n \"include\": [\"src\", \"test\", \"js"
},
{
"path": "packages/unis-core/types/jsx.d.ts",
"chars": 50422,
"preview": "// Note: this file is auto concatenated to the end of the bundled d.ts during\n// build.\n\n// This code is based on react "
},
{
"path": "packages/unis-core/vitest.config.ts",
"chars": 228,
"preview": "import { defineConfig } from \"vitest/config\";\n\nexport default defineConfig({\n test: {\n include: [\"**/*.{test,spec}.{"
},
{
"path": "packages/unis-dom/.gitignore",
"chars": 1630,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs."
},
{
"path": "packages/unis-dom/index.d.ts",
"chars": 53,
"preview": "import \"@unis/core\";\nexport * from \"./dist/browser\";\n"
},
{
"path": "packages/unis-dom/package.json",
"chars": 1727,
"preview": "{\n \"name\": \"@unis/dom\",\n \"version\": \"1.2.5\",\n \"description\": \"Unis is a simpler and easier to use front-end framework"
},
{
"path": "packages/unis-dom/rollup.config.mjs",
"chars": 995,
"preview": "import esbuild from \"rollup-plugin-esbuild\";\nimport dts from \"rollup-plugin-dts\";\nimport { defineConfig } from \"rollup\";"
},
{
"path": "packages/unis-dom/server.d.ts",
"chars": 31,
"preview": "export * from \"./dist/server\";\n"
},
{
"path": "packages/unis-dom/src/browser/__test__/context.test.tsx",
"chars": 2928,
"preview": "/**\n * @vitest-environment jsdom\n */\nimport { afterEach, beforeEach, expect, it } from \"vitest\";\nimport { useContext } f"
},
{
"path": "packages/unis-dom/src/browser/__test__/dom.test.tsx",
"chars": 1318,
"preview": "/**\n * @vitest-environment jsdom\n */\nimport { afterEach, beforeEach, expect, it } from \"vitest\";\nimport { useEffect } fr"
},
{
"path": "packages/unis-dom/src/browser/__test__/effect.test.tsx",
"chars": 884,
"preview": "/**\n * @vitest-environment jsdom\n */\nimport { afterEach, beforeEach, expect, it } from \"vitest\";\nimport { useEffect } fr"
},
{
"path": "packages/unis-dom/src/browser/__test__/hydrate.test.tsx",
"chars": 888,
"preview": "/**\n * @vitest-environment jsdom\n */\nimport { afterEach, beforeEach, expect, it } from \"vitest\";\nimport { rendered, test"
},
{
"path": "packages/unis-dom/src/browser/__test__/memo.test.tsx",
"chars": 1017,
"preview": "/**\n * @vitest-environment jsdom\n */\nimport { afterEach, beforeEach, expect, it } from \"vitest\";\nimport { useEffect } fr"
},
{
"path": "packages/unis-dom/src/browser/__test__/portal.test.tsx",
"chars": 1138,
"preview": "/**\n * @vitest-environment jsdom\n */\nimport { afterEach, beforeEach, expect, it } from \"vitest\";\nimport { useEffect } fr"
},
{
"path": "packages/unis-dom/src/browser/__test__/reconcile.test.tsx",
"chars": 1474,
"preview": "/**\n * @vitest-environment jsdom\n */\nimport { afterEach, beforeEach, expect, it } from \"vitest\";\nimport { useEffect } fr"
},
{
"path": "packages/unis-dom/src/browser/__test__/util.ts",
"chars": 656,
"preview": "import { readyForWork, createRoot, createTokTik } from \"@unis/core\";\nimport { createOperator } from \"../operator\";\n\ncons"
},
{
"path": "packages/unis-dom/src/browser/__test__/utils.test.ts",
"chars": 616,
"preview": "/**\n * @vitest-environment jsdom\n */\nimport { expect, it } from \"vitest\";\nimport { classes, svgKey, styleStr } from \"@un"
},
{
"path": "packages/unis-dom/src/browser/const.ts",
"chars": 46,
"preview": "export const UNIS_ROOT = Symbol(\"unis_root\");\n"
},
{
"path": "packages/unis-dom/src/browser/index.ts",
"chars": 72,
"preview": "export { UNIS_ROOT } from \"./const\";\nexport { render } from \"./render\";\n"
},
{
"path": "packages/unis-dom/src/browser/operator.ts",
"chars": 3477,
"preview": "import { Fiber, findEls, isPortal, isText, Operator } from \"@unis/core\";\nimport { getEventName, isEvent, isNullish } fro"
},
{
"path": "packages/unis-dom/src/browser/render.ts",
"chars": 591,
"preview": "import { readyForWork, createRoot, createTokTik } from \"@unis/core\";\nimport { createOperator } from \"./operator\";\nimport"
},
{
"path": "packages/unis-dom/src/browser/toktik.ts",
"chars": 354,
"preview": "export const nextTick = (cb: VoidFunction, pending = false) => {\n if (pending) {\n queueMicrotask(cb);\n } else if (w"
},
{
"path": "packages/unis-dom/src/server/__test__/server.test.tsx",
"chars": 478,
"preview": "import { expect, it } from \"vitest\";\nimport { renderToString } from \"..\";\n\nit(\"render to string\", () => {\n const App = "
},
{
"path": "packages/unis-dom/src/server/index.ts",
"chars": 600,
"preview": "import { createRoot, createTokTik, readyForWork } from \"@unis/core\";\nimport { createOperator, ElementNode } from \"./oper"
},
{
"path": "packages/unis-dom/src/server/operator.ts",
"chars": 2633,
"preview": "import { isEvent, isNullish, Operator, Fiber, isText } from \"@unis/core\";\n\nexport class ElementNode {\n children: Server"
},
{
"path": "packages/unis-dom/tsconfig.build.json",
"chars": 80,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"exclude\": [\"**/*.test.*\", \"**/__test__\"]\n}\n"
},
{
"path": "packages/unis-dom/tsconfig.json",
"chars": 111,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"build\"\n },\n \"include\": [\"src\"]\n}\n"
},
{
"path": "packages/unis-dom/vitest.config.ts",
"chars": 303,
"preview": "import { defineConfig } from \"vitest/config\";\nimport { unisPreset } from \"@unis/vite-preset\";\n\nexport default defineConf"
},
{
"path": "packages/unis-example/.gitignore",
"chars": 1641,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs."
},
{
"path": "packages/unis-example/index.html",
"chars": 381,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta http-equiv=\"X-UA-Compatible\" content=\"I"
},
{
"path": "packages/unis-example/other.d.ts",
"chars": 24,
"preview": "declare module \"*.css\";\n"
},
{
"path": "packages/unis-example/package.json",
"chars": 857,
"preview": "{\n \"name\": \"@unis/unis-example\",\n \"version\": \"0.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"private\": \"true\",\n"
},
{
"path": "packages/unis-example/postcss.config.js",
"chars": 83,
"preview": "module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n};\n"
},
{
"path": "packages/unis-example/src/Dialog.tsx",
"chars": 2598,
"preview": "import { createPortal, useProps, useRef } from \"@unis/core\";\nimport { Item } from \"./TodoItem\";\n\ninterface DialogProps {"
},
{
"path": "packages/unis-example/src/Todo.tsx",
"chars": 2402,
"preview": "import { useState } from \"@unis/core\";\nimport { CSSTransition, TransitionGroup } from \"@unis/transition\";\nimport { Dialo"
},
{
"path": "packages/unis-example/src/TodoItem/index.module.css",
"chars": 32,
"preview": ".deleteIcon {\n color: white;\n}\n"
},
{
"path": "packages/unis-example/src/TodoItem/index.tsx",
"chars": 3807,
"preview": "import { use, memo, useEffect, useProps, useRef } from \"@unis/core\";\nimport { Update } from \"../hooks/update\";\nimport s "
},
{
"path": "packages/unis-example/src/Welcome/index.module.css",
"chars": 462,
"preview": ".msg {\n font-size: 50px;\n font-weight: bold;\n background: -webkit-linear-gradient(0deg,#e339bc 25%,#4f5b94);\n backgr"
},
{
"path": "packages/unis-example/src/Welcome/index.tsx",
"chars": 197,
"preview": "import { Link } from \"@unis/router\";\nimport s from \"./index.module.css\";\n\nexport const Welcome = () => {\n return () => "
},
{
"path": "packages/unis-example/src/global.css",
"chars": 982,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nhtml,\nbody,\n#root {\n height: 100%;\n}\n\n.scale-appear {\n tra"
},
{
"path": "packages/unis-example/src/hooks/update.ts",
"chars": 179,
"preview": "import { useState } from \"@unis/core\";\n\nexport const Update = () => {\n let [count, setCount] = useState(1);\n const upd"
},
{
"path": "packages/unis-example/src/index.module.css",
"chars": 122,
"preview": ".background_img {\n background: url(./bg.jpeg);\n z-index: -1;\n background-size: cover;\n background-position: center;\n"
},
{
"path": "packages/unis-example/src/index.tsx",
"chars": 1563,
"preview": "import {\n Fragment,\n // useProps,\n // useState,\n // useEffect\n} from \"@unis/core\";\nimport { render } from \"@unis/dom"
},
{
"path": "packages/unis-example/tailwind.config.js",
"chars": 173,
"preview": "module.exports = {\n content: [\"./public/**/*.html\", \"./src/**/*.{js,jsx,ts,tsx,vue}\"],\n theme: {\n extend: {},\n },\n"
},
{
"path": "packages/unis-example/tsconfig.json",
"chars": 75,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"include\": [\"src\", \"other.d.ts\"]\n}\n"
},
{
"path": "packages/unis-example/vite.config.js",
"chars": 147,
"preview": "import { defineConfig } from \"vite\";\nimport { unisPreset } from \"@unis/vite-preset\";\n\nexport default defineConfig({\n pl"
},
{
"path": "packages/unis-router/.gitignore",
"chars": 22,
"preview": "build/\ncoverage/\ndist/"
},
{
"path": "packages/unis-router/README.md",
"chars": 760,
"preview": "# Unis Router\n\nRouter for unis, inspire by [React Router V6](https://github.com/remix-run/react-router).\n\n## Install\n\n``"
},
{
"path": "packages/unis-router/package.json",
"chars": 1416,
"preview": "{\n \"name\": \"@unis/router\",\n \"version\": \"0.1.0\",\n \"description\": \"Unis router component\",\n \"main\": \"dist/index.js\",\n "
},
{
"path": "packages/unis-router/rollup.config.mjs",
"chars": 1119,
"preview": "import esbuild from \"rollup-plugin-esbuild\";\nimport dts from \"rollup-plugin-dts\";\nimport { defineConfig } from \"rollup\";"
},
{
"path": "packages/unis-router/src/components/BrowserRouter.tsx",
"chars": 1193,
"preview": "import { useProps, useLayoutEffect, useState, useMemo } from \"@unis/core\";\nimport { createBrowserHistory, BrowserHistory"
},
{
"path": "packages/unis-router/src/components/Link.tsx",
"chars": 1064,
"preview": "import {\n AnchorHTMLAttributes,\n ElementAttrs,\n use,\n useContext,\n useProps,\n} from \"@unis/core\";\nimport { RouterCo"
},
{
"path": "packages/unis-router/src/components/NavLink.tsx",
"chars": 911,
"preview": "import { HTMLAttributes, use, useContext, useProps } from \"@unis/core\";\nimport { RouteContext } from \"../context\";\nimpor"
},
{
"path": "packages/unis-router/src/components/Outlet.tsx",
"chars": 481,
"preview": "import { use, useContext } from \"@unis/core\";\nimport { RouteContext } from \"../context\";\n\nexport const Outlet = () => {\n"
},
{
"path": "packages/unis-router/src/components/Redirect.tsx",
"chars": 652,
"preview": "import { useContext, useEffect, useProps } from \"@unis/core\";\nimport { RouteContext, RouterContext } from \"../context\";\n"
},
{
"path": "packages/unis-router/src/components/Route.tsx",
"chars": 188,
"preview": "import { RouteData } from \"../types\";\n\nexport type RouteProps = Omit<RouteData, \"children\"> & {\n children?: JSX.Element"
},
{
"path": "packages/unis-router/src/components/Routes.tsx",
"chars": 1122,
"preview": "import { cloneElement, FiberNode, use, useProps } from \"@unis/core\";\nimport { uRouter } from \"../hooks/uRouter\";\nimport "
},
{
"path": "packages/unis-router/src/context.tsx",
"chars": 635,
"preview": "import { createContext } from \"@unis/core\";\nimport { BrowserHistory, Location } from \"history\";\nimport { MatchRoute } fr"
},
{
"path": "packages/unis-router/src/hooks/uHistory.ts",
"chars": 193,
"preview": "import { useContext } from \"@unis/core\";\nimport { RouterContext } from \"../context\";\n\nexport const uHistory = () => {\n "
},
{
"path": "packages/unis-router/src/hooks/uLocation.ts",
"chars": 173,
"preview": "import { use } from \"@unis/core\";\nimport { uHistory } from \"./uHistory\";\n\nexport const uLocation = () => {\n let { locat"
},
{
"path": "packages/unis-router/src/hooks/uParams.ts",
"chars": 234,
"preview": "import { useContext } from \"@unis/core\";\nimport { RouteContext } from \"../context\";\n\nexport const uParams = <T = Record<"
},
{
"path": "packages/unis-router/src/hooks/uRouter.tsx",
"chars": 796,
"preview": "import { use, useContext } from \"@unis/core\";\nimport { RouteContext, RouterContext } from \"../context\";\nimport { Outlet "
},
{
"path": "packages/unis-router/src/hooks/uTargetPath.tsx",
"chars": 422,
"preview": "import { use, useContext } from \"@unis/core\";\r\nimport { RouteContext } from \"../context\";\r\nimport { resolvePath } from \""
},
{
"path": "packages/unis-router/src/index.ts",
"chars": 462,
"preview": "export * from \"./components/BrowserRouter\";\nexport * from \"./context\";\nexport * from \"./components/Routes\";\nexport * fro"
},
{
"path": "packages/unis-router/src/types.ts",
"chars": 206,
"preview": "export interface RouteData {\n path?: string;\n element?: JSX.Element;\n children?: RouteData[];\n}\n\nexport interface Mat"
},
{
"path": "packages/unis-router/src/utils.test.tsx",
"chars": 3776,
"preview": "import { expect, it } from \"vitest\";\nimport { RouteData } from \"./types\";\nimport { matchRoutes, resolvePath } from \"./ut"
},
{
"path": "packages/unis-router/src/utils.ts",
"chars": 4570,
"preview": "import { MatchRoute, RouteData } from \"./types\";\n\nconst SLASH = \"/\";\nconst DOT = \".\";\n\nconst trimSlash = (str: string) ="
},
{
"path": "packages/unis-router/tsconfig.json",
"chars": 110,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"build\"\n },\n \"include\": [\"src\"]\n}"
},
{
"path": "packages/unis-router/vitest.config.ts",
"chars": 303,
"preview": "import { defineConfig } from \"vitest/config\";\nimport { unisPreset } from \"@unis/vite-preset\";\n\nexport default defineConf"
},
{
"path": "packages/unis-transition/.gitignore",
"chars": 12,
"preview": "build/\ndist/"
},
{
"path": "packages/unis-transition/README.md",
"chars": 3331,
"preview": "# Unis Transition\n\nTransition component for unis inspired by `React Transition Group`.\n\n## Install\n\n```shell\nnpm i @uni"
},
{
"path": "packages/unis-transition/package.json",
"chars": 1197,
"preview": "{\n \"name\": \"@unis/transition\",\n \"version\": \"0.1.0\",\n \"description\": \"Unis transition component\",\n \"main\": \"dist/inde"
},
{
"path": "packages/unis-transition/rollup.config.mjs",
"chars": 1108,
"preview": "import dts from \"rollup-plugin-dts\";\nimport esbuild from \"rollup-plugin-esbuild\";\nimport { defineConfig } from \"rollup\";"
},
{
"path": "packages/unis-transition/src/CSSTransition.ts",
"chars": 3840,
"preview": "import { findEls, use, useLayoutEffect, useProps } from \"@unis/core\";\nimport { uInstance } from \"./hooks/uInstance\";\nimp"
},
{
"path": "packages/unis-transition/src/TransitionGroup.ts",
"chars": 2097,
"preview": "import { FiberNode, use, useEffect, useProps, useState } from \"@unis/core\";\nimport { CSSTransition } from \"./CSSTransiti"
},
{
"path": "packages/unis-transition/src/hooks/uInstance.ts",
"chars": 146,
"preview": "import { Fiber, use } from \"@unis/core\";\n\nexport const uInstance = () => {\n let instance = use((WF: Fiber) => WF);\n re"
},
{
"path": "packages/unis-transition/src/hooks/uTransition.ts",
"chars": 2627,
"preview": "import { use, useEffect, useState } from \"@unis/core\";\n\nexport const UNMOUNTED = \"unmounted\";\nexport const APPEARING = \""
},
{
"path": "packages/unis-transition/src/hooks/uUpdate.ts",
"chars": 197,
"preview": "import { useReducer } from \"@unis/core\";\n\nexport const uUpdate = () => {\n let [, dispatch] = useReducer((a) => a + 1, 0"
},
{
"path": "packages/unis-transition/src/hooks/uWatch.ts",
"chars": 380,
"preview": "import { use, useLayoutEffect } from \"@unis/core\";\n\nexport const uWatch = <T extends any>(\n handler: (currentValue: T, "
},
{
"path": "packages/unis-transition/src/index.ts",
"chars": 105,
"preview": "export * from \"./CSSTransition\";\nexport * from \"./TransitionGroup\";\nexport * from \"./hooks/uTransition\";\n"
},
{
"path": "packages/unis-transition/tsconfig.json",
"chars": 110,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"build\"\n },\n \"include\": [\"src\"]\n}"
},
{
"path": "packages/unis-vite-preset/.gitignore",
"chars": 6,
"preview": "build/"
},
{
"path": "packages/unis-vite-preset/README.md",
"chars": 304,
"preview": "# Unis Vite Preset\n\nUnis develop preset for vite.\n\n## Install\n\n```shell\nnpm add -D @unis/vite-preset\n```\n\n## Usage\n\nvite"
},
{
"path": "packages/unis-vite-preset/package.json",
"chars": 1199,
"preview": "{\n \"name\": \"@unis/vite-preset\",\n \"version\": \"0.1.0\",\n \"description\": \"Unis vite preset\",\n \"main\": \"build/index.js\",\n"
},
{
"path": "packages/unis-vite-preset/rollup.config.js",
"chars": 724,
"preview": "import { defineConfig } from \"rollup\";\nimport { nodeResolve } from \"@rollup/plugin-node-resolve\";\nimport esbuild from \"r"
},
{
"path": "packages/unis-vite-preset/src/index.ts",
"chars": 603,
"preview": "import type { PluginOption } from \"vite\";\nimport { reassign } from \"@callback-reassign/rollup-plugin\";\nimport { unisFns "
},
{
"path": "packages/unis-vite-preset/tsconfig.json",
"chars": 172,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"emitDeclarationOnly\": true,\n \"declarationMap\": fals"
},
{
"path": "pnpm-workspace.yaml",
"chars": 28,
"preview": "packages:\n - \"packages/**\"\n"
},
{
"path": "tsconfig.json",
"chars": 451,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"lib\": [\n \"DOM\",\n \"ESNext\"\n ],\n \"jsx\": \"react-jsx\","
}
]
About this extraction
This page contains the full source code of the anuoua/unis GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 145 files (203.9 KB), approximately 59.9k tokens, and a symbol index with 170 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.