forked from opentiny/tiny-vue
* feat:同步代码 * chore: 优化本地 svg 加载 Co-authored-by: however <102494131+river-xiu@users.noreply.github.com>
This commit is contained in:
parent
196ab84bee
commit
f7ca5794d3
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Opentiny Solid 组件调试</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "@opentiny/docs-solid",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"solid-js": "^1.7.8",
|
||||||
|
"@opensolidtiny/solid": "~3.14.0",
|
||||||
|
"@opensolidtiny/solid-icon": "~3.14.0",
|
||||||
|
"@opensolidtiny/solid-common": "~3.14.0",
|
||||||
|
"@opentiny/vue-theme": "~3.14.0",
|
||||||
|
"@opentiny/vue-renderless": "~3.14.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vite": "^4.4.5",
|
||||||
|
"vite-plugin-solid": "^2.7.0",
|
||||||
|
"vite-plugin-inspect": "^0.7.10"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,35 @@
|
||||||
|
import { createMutable } from 'solid-js/store'
|
||||||
|
import { Button, Alert } from '@opensolidtiny/solid'
|
||||||
|
import { IconActivation } from '@opensolidtiny/solid-icon'
|
||||||
|
|
||||||
|
// 在这里导入组件,进行 api 调试
|
||||||
|
function App() {
|
||||||
|
const state = createMutable({
|
||||||
|
value: 1
|
||||||
|
})
|
||||||
|
const hanleClick = () => {
|
||||||
|
state.value++
|
||||||
|
}
|
||||||
|
|
||||||
|
const alertSlots = {
|
||||||
|
title: <span>插槽标题</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="app">
|
||||||
|
<h1>{state.value}</h1>
|
||||||
|
<Button onClick={hanleClick}>点击按钮</Button>
|
||||||
|
<Button type="primary">点击按钮</Button>
|
||||||
|
<Button type="success">点击按 钮</Button>
|
||||||
|
<Button type="danger">点击按钮</Button>
|
||||||
|
<Button type="danger" text={'Text'}></Button>
|
||||||
|
<Alert description="type 为默认值 info"></Alert>
|
||||||
|
<Alert type="warning" description="type 为默认值 info" size="large" slots={alertSlots}></Alert>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<IconActivation style={{ width: '48px', height: '48px' }}></IconActivation>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
|
@ -0,0 +1,4 @@
|
||||||
|
.app {
|
||||||
|
margin: 10px;
|
||||||
|
width: 500px;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { render } from 'solid-js/web'
|
||||||
|
import App from './App'
|
||||||
|
import './main.css'
|
||||||
|
|
||||||
|
const root = document.getElementById('root')
|
||||||
|
|
||||||
|
render(() => <App />, root)
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import solid from 'vite-plugin-solid'
|
||||||
|
import inspectPlugin from 'vite-plugin-inspect'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [inspectPlugin(), solid()],
|
||||||
|
define: {
|
||||||
|
'process.env': {}
|
||||||
|
}
|
||||||
|
})
|
|
@ -10,11 +10,20 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"solid-js": "^1.7.8",
|
"solid-js": "^1.7.8",
|
||||||
"@opentiny/solid": "workspace:~"
|
"@opentiny/solid": "workspace:~",
|
||||||
|
"@opentiny/solid-common": "workspace:~",
|
||||||
|
"@opentiny/vue-button": "workspace:~",
|
||||||
|
"@opentiny/vue-alert": "workspace:~",
|
||||||
|
"@opentiny/vue-badge": "workspace:~",
|
||||||
|
"@opentiny/vue-icon": "workspace:~",
|
||||||
|
"@opentiny/vue-switch": "workspace:~",
|
||||||
|
"@opentiny/vue-theme": "workspace:~"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"fs-extra": "^11.2.0",
|
||||||
"vite": "^4.4.5",
|
"vite": "^4.4.5",
|
||||||
"vite-plugin-solid": "^2.7.0",
|
"vite-plugin-solid": "^2.7.0",
|
||||||
"vite-plugin-svgr": "^3.2.0"
|
"vite-plugin-inspect": "^0.7.10",
|
||||||
|
"@opentiny/vue-vite-template2jsx": "workspace:~"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,54 @@
|
||||||
import { Button } from '@opentiny/solid'
|
import { createMutable } from 'solid-js/store'
|
||||||
|
import Button from './views/button'
|
||||||
|
import Alert from './views/alert'
|
||||||
|
import Icon from './views/icon'
|
||||||
|
import Switch from './views/switch'
|
||||||
|
import { Dynamic } from 'solid-js/web'
|
||||||
|
import './main.less'
|
||||||
|
|
||||||
// 在这里导入组件,进行 api 调试
|
// 在这里导入组件,进行 api 调试
|
||||||
function App() {
|
function App() {
|
||||||
|
const allViews = {
|
||||||
|
Button,
|
||||||
|
Alert,
|
||||||
|
Icon,
|
||||||
|
Switch
|
||||||
|
}
|
||||||
|
const state = createMutable({
|
||||||
|
value: null,
|
||||||
|
active: '',
|
||||||
|
menus: ['Button', 'Alert', 'Icon', 'Switch']
|
||||||
|
})
|
||||||
|
|
||||||
|
const getViewComponent = (name: string) => {
|
||||||
|
return allViews[name] || Button
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashchange = () => {
|
||||||
|
state.active = window.location.hash.replace('#/', '') || 'Button'
|
||||||
|
state.value = getViewComponent(state.active)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('hashchange', hashchange)
|
||||||
|
|
||||||
|
hashchange()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div class="app">
|
||||||
<Button>点击按钮</Button>
|
<p>
|
||||||
|
<For each={state.menus}>
|
||||||
|
{(item) => (
|
||||||
|
<>
|
||||||
|
<a classList={{ 'title': true, 'active': state.active === item }} href={`#/${item}`}>
|
||||||
|
{item}
|
||||||
|
</a>
|
||||||
|
<span>/</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</p>
|
||||||
|
<br />
|
||||||
|
<Dynamic component={state.value} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
.app {
|
||||||
|
margin: 40px auto;
|
||||||
|
width: 640px;
|
||||||
|
|
||||||
|
a.title {
|
||||||
|
color: #39f;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
margin:0 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import { render } from 'solid-js/web'
|
import { render } from 'solid-js/web'
|
||||||
import App from './App'
|
import App from './App'
|
||||||
import './main.css'
|
|
||||||
|
|
||||||
const root = document.getElementById('root')
|
const root = document.getElementById('root')
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import TinyAlert from '@opentiny/vue-alert'
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const alertSlots = {
|
||||||
|
title: <span>插槽标题</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
const alertSlots2 = {
|
||||||
|
description: (
|
||||||
|
<span>
|
||||||
|
<p style="color:red;">插槽描述</p>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const alertSlots3 = {
|
||||||
|
close: <span>插槽关闭</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="alert">
|
||||||
|
<TinyAlert description="type 为默认值 info"></TinyAlert>
|
||||||
|
<TinyAlert size="large" title="slot 自定义内容">
|
||||||
|
<span>自定义内容</span>
|
||||||
|
</TinyAlert>
|
||||||
|
<br />
|
||||||
|
<TinyAlert size="large" title="slot 自定义交互操作">
|
||||||
|
<a href="javascript: void(0);">确定</a>
|
||||||
|
<a href="javascript: void(0);">取消</a>
|
||||||
|
</TinyAlert>
|
||||||
|
<br />
|
||||||
|
<TinyAlert type="success" title="成功" size="large" description="提交结果页用于反馈一系列操作任务的处理结果。">
|
||||||
|
<a href="javascript: void(0);">继续提交</a>
|
||||||
|
<a href="javascript: void(0);">取消操作</a>
|
||||||
|
</TinyAlert>
|
||||||
|
<br />
|
||||||
|
<TinyAlert
|
||||||
|
type="error"
|
||||||
|
title="错误"
|
||||||
|
size="large"
|
||||||
|
description="提交结果页用于反馈一系列操作任务的处理结果。"></TinyAlert>
|
||||||
|
<br />
|
||||||
|
<TinyAlert type="warning" title="警告" size="large" description="提交结果页用于反馈一系列操作任务的处理结果。">
|
||||||
|
<a href="javascript: void(0);">继续提交</a>
|
||||||
|
<a href="javascript: void(0);">取消操作</a>
|
||||||
|
</TinyAlert>
|
||||||
|
<br />
|
||||||
|
<TinyAlert type="warning" description="type 为默认值 info" size="large" slots={alertSlots}></TinyAlert>
|
||||||
|
<br />
|
||||||
|
<TinyAlert type="warning" title="警告" size="large" slots={alertSlots2}></TinyAlert>
|
||||||
|
<br />
|
||||||
|
<TinyAlert
|
||||||
|
type="error"
|
||||||
|
title="错误"
|
||||||
|
size="large"
|
||||||
|
description="关闭按钮自定义文本"
|
||||||
|
closable={false}
|
||||||
|
slots={alertSlots3}></TinyAlert>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { createMutable } from 'solid-js/store'
|
||||||
|
import TinyButton from '@opentiny/vue-button'
|
||||||
|
import TinyIconSearch from '@opentiny/vue-icon/search/index.ts'
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const state = createMutable({
|
||||||
|
value: 1
|
||||||
|
})
|
||||||
|
const hanleClick = () => {
|
||||||
|
state.value++
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="button">
|
||||||
|
<TinyButton>默认按钮</TinyButton>
|
||||||
|
<TinyButton type="primary">主要按钮</TinyButton>
|
||||||
|
<TinyButton type="success">成功按钮</TinyButton>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<TinyButton type="info">信息按钮</TinyButton>
|
||||||
|
<TinyButton type="warning">警告按钮</TinyButton>
|
||||||
|
<TinyButton type="danger">危险按钮 </TinyButton>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<TinyButton plain> 朴素按钮 </TinyButton>
|
||||||
|
<TinyButton type="danger" text={'自定义 Text'}></TinyButton>
|
||||||
|
<TinyButton type="primary" loading>
|
||||||
|
加载中
|
||||||
|
</TinyButton>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<TinyButton icon={TinyIconSearch}></TinyButton>
|
||||||
|
<TinyButton type="danger" icon={TinyIconSearch} circle></TinyButton>
|
||||||
|
<TinyButton type="text">纯文本按钮</TinyButton>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<TinyButton type="primary" size="large" plain>
|
||||||
|
{' '}
|
||||||
|
超大按钮{' '}
|
||||||
|
</TinyButton>
|
||||||
|
<TinyButton type="primary" size="medium" plain>
|
||||||
|
{' '}
|
||||||
|
中等按钮{' '}
|
||||||
|
</TinyButton>
|
||||||
|
<TinyButton type="primary" size="small" plain>
|
||||||
|
{' '}
|
||||||
|
小型按钮{' '}
|
||||||
|
</TinyButton>
|
||||||
|
<TinyButton type="primary" size="mini" plain>
|
||||||
|
{' '}
|
||||||
|
超小按钮{' '}
|
||||||
|
</TinyButton>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<TinyButton onClick={hanleClick}>点击事件</TinyButton> 点击计数:{state.value}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
.icon-demo .tiny-svg {
|
||||||
|
fill: #8994aa;
|
||||||
|
margin: 20px 50px;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import TinyIconActivation from '@opentiny/vue-icon/activation/index.ts'
|
||||||
|
import TinyIconShare from '@opentiny/vue-icon/share/index.ts'
|
||||||
|
import TinyIconDel from '@opentiny/vue-icon/del/index.ts'
|
||||||
|
import TinyIconWriting from '@opentiny/vue-icon/writing/index.ts'
|
||||||
|
import TinyIconAscending from '@opentiny/vue-icon/ascending/index.ts'
|
||||||
|
import TinyIconClockWork from '@opentiny/vue-icon/clock-work/index.ts'
|
||||||
|
import './icon.less'
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return (
|
||||||
|
<div className="icon-demo">
|
||||||
|
<TinyIconActivation></TinyIconActivation>
|
||||||
|
<TinyIconShare></TinyIconShare>
|
||||||
|
<TinyIconDel></TinyIconDel>
|
||||||
|
<TinyIconWriting></TinyIconWriting>
|
||||||
|
<TinyIconAscending></TinyIconAscending>
|
||||||
|
<TinyIconClockWork></TinyIconClockWork>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import TinySwitch from '@opentiny/vue-switch'
|
||||||
|
import { createMutable } from 'solid-js/store'
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const state = createMutable({
|
||||||
|
value: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const switchSlots = {
|
||||||
|
open: <span>打开</span>,
|
||||||
|
close: <span>关闭</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="switch">
|
||||||
|
<TinySwitch></TinySwitch>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<TinySwitch modelValue={state.value}></TinySwitch>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<TinySwitch modelValue={state.value} slots={switchSlots}></TinySwitch>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,7 +1,18 @@
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import solid from 'vite-plugin-solid'
|
import solid from 'vite-plugin-solid'
|
||||||
import svgr from 'vite-plugin-svgr'
|
import path from 'node:path'
|
||||||
|
import inspectPlugin from 'vite-plugin-inspect'
|
||||||
|
import vueTemplate2jsx from '@opentiny/vue-vite-template2jsx'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [solid(), svgr()]
|
plugins: [inspectPlugin(), vueTemplate2jsx(), solid({ extensions: ['.js', '.ts', '.tsx', '.jsx', '.vue'] })],
|
||||||
|
define: {
|
||||||
|
'process.env': {}
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve('./src'),
|
||||||
|
'@opentiny/solid-common': path.resolve('../../packages/solid/src/common/src/index.ts')
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,278 @@
|
||||||
|
import { getBabelOutputPlugin } from '@rollup/plugin-babel'
|
||||||
|
import { createRequire } from 'node:module'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { build, defineConfig } from 'vite'
|
||||||
|
import { getAlias, pathFromWorkspaceRoot } from '../../config/vite'
|
||||||
|
import { external } from '../../shared/config'
|
||||||
|
import type { Module } from '../../shared/module-utils'
|
||||||
|
import { getAllIcons, getAllModules, getByName } from '../../shared/module-utils'
|
||||||
|
import { logGreen, kebabCase, capitalizeKebabCase, getPatchVersion, isValidVersion } from '../../shared/utils'
|
||||||
|
import generatePackageJsonPlugin from './rollup/generate-package-json'
|
||||||
|
import inlineChunksPlugin from './rollup/inline-chunks'
|
||||||
|
import replaceModuleNamePlugin from './rollup/replace-module-name'
|
||||||
|
import solid from 'vite-plugin-solid'
|
||||||
|
import vueTemplate2jsx from '@opentiny/vue-vite-template2jsx'
|
||||||
|
|
||||||
|
export const pathFromPackages = (...args) => pathFromWorkspaceRoot('packages', ...args)
|
||||||
|
export const require = createRequire(import.meta.url)
|
||||||
|
export const requireModules = (id: string) => require(require.resolve(pathFromWorkspaceRoot(id)))
|
||||||
|
|
||||||
|
// 需要打包的solid组件
|
||||||
|
const buildComponents = ['alert', 'button']
|
||||||
|
|
||||||
|
export interface BaseConfig {
|
||||||
|
buildTarget: string
|
||||||
|
npmScope?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getBaseConfig = ({ buildTarget }: BaseConfig) => {
|
||||||
|
const versionTarget = isValidVersion(buildTarget) ? buildTarget : `3.${buildTarget}`
|
||||||
|
const themeAndRenderlessVersion = isValidVersion(buildTarget) ? buildTarget : `3.${buildTarget}`
|
||||||
|
const isThemeOrRenderless = (key) => key.includes('@opentiny/vue-theme') || key.includes('@opentiny/vue-renderless')
|
||||||
|
|
||||||
|
return defineConfig({
|
||||||
|
publicDir: false,
|
||||||
|
plugins: [
|
||||||
|
inlineChunksPlugin({ deleteInlinedFiles: true }),
|
||||||
|
generatePackageJsonPlugin({
|
||||||
|
beforeWriteFile: (filePath, content) => {
|
||||||
|
const dependencies = {}
|
||||||
|
|
||||||
|
Object.entries(content.dependencies).forEach(([key, value]) => {
|
||||||
|
// dependencies里的@opentiny,统一使用:~x.x.0
|
||||||
|
if (isThemeOrRenderless(key)) {
|
||||||
|
dependencies[key] = getPatchVersion(themeAndRenderlessVersion)
|
||||||
|
} else if ((value as string).includes('workspace:~')) {
|
||||||
|
dependencies[
|
||||||
|
key.replace('@opentiny/vue', '@opensolidtiny/solid').replace('@opentiny/solid', '@opensolidtiny/solid')
|
||||||
|
] = getPatchVersion(versionTarget)
|
||||||
|
} else {
|
||||||
|
dependencies[key] = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const matchList = ['solid-icon', 'vue-icon-saas', 'solid']
|
||||||
|
|
||||||
|
// 如果是主入口、svg图标或者主题规范包则直接指向相同路径
|
||||||
|
if (matchList.includes(filePath)) {
|
||||||
|
content.main = './index.js'
|
||||||
|
content.module = './index.js'
|
||||||
|
} else {
|
||||||
|
content.main = './lib/index.js'
|
||||||
|
content.module = './lib/index.js'
|
||||||
|
}
|
||||||
|
|
||||||
|
content.name = content.name
|
||||||
|
.replace('@opentiny/vue', '@opensolidtiny/solid')
|
||||||
|
.replace('@opentiny/solid', '@opensolidtiny/solid')
|
||||||
|
content.version = versionTarget
|
||||||
|
content.dependencies = dependencies
|
||||||
|
|
||||||
|
delete content.devDependencies
|
||||||
|
delete content.private
|
||||||
|
delete content.exports
|
||||||
|
|
||||||
|
return {
|
||||||
|
filePath: filePath.replace(/[\\/]lib$/, ''),
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
replaceModuleNamePlugin(versionTarget),
|
||||||
|
vueTemplate2jsx(),
|
||||||
|
solid({ extensions: ['.js', '.ts', '.tsx', '.jsx', '.vue'] })
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.ts', '.tsx', '.vue'],
|
||||||
|
alias: {
|
||||||
|
...getAlias(3, '', '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
'process.env.BUILD_TARGET': JSON.stringify('component')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function batchBuildAll({ tasks, formats, message, emptyOutDir, buildTarget, npmScope }) {
|
||||||
|
const rootDir = pathFromPackages('')
|
||||||
|
const outDir = path.resolve(rootDir, `dist-solid/${npmScope}`)
|
||||||
|
await batchBuild({
|
||||||
|
tasks,
|
||||||
|
formats,
|
||||||
|
message,
|
||||||
|
emptyOutDir
|
||||||
|
})
|
||||||
|
|
||||||
|
function toEntry(libs) {
|
||||||
|
return libs.reduce((result, { libPath, path: file }) => {
|
||||||
|
const tLibPath = libPath.replace('-lib/', '/lib/')
|
||||||
|
result[tLibPath] = pathFromPackages(file)
|
||||||
|
return result
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function batchBuild({ tasks, formats, message, emptyOutDir }) {
|
||||||
|
if (tasks.length === 0) return
|
||||||
|
logGreen(`====== 开始构建 ${message} ======`)
|
||||||
|
const entry = toEntry(tasks)
|
||||||
|
|
||||||
|
await build({
|
||||||
|
configFile: false,
|
||||||
|
...getBaseConfig({ buildTarget }),
|
||||||
|
build: {
|
||||||
|
emptyOutDir,
|
||||||
|
minify: false,
|
||||||
|
rollupOptions: {
|
||||||
|
plugins: [
|
||||||
|
getBabelOutputPlugin({
|
||||||
|
presets: [['@babel/preset-env', { loose: true, modules: false }]]
|
||||||
|
}) as any
|
||||||
|
],
|
||||||
|
external: (source, importer, isResolved) => {
|
||||||
|
// vite打包入口文件或者没有解析过得包不能排除依赖
|
||||||
|
if (isResolved || !importer) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/vue-icon(-saas)?\/index/.test(importer)) {
|
||||||
|
// 图标入口排除子图标
|
||||||
|
return /^\.\//.test(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子图标排除周边引用, 这里注意不要排除svg图标
|
||||||
|
if (/vue-icon(-saas)?\/.+\/index/.test(importer)) {
|
||||||
|
return !/\.svg/.test(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/src\/index/.test(importer)) {
|
||||||
|
// 模块入口,pc/mobile 文件要分离,同时排除 node_modules 依赖
|
||||||
|
return /^\.\/(pc|mobile|mobile-first)/.test(source) || external(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @opentiny/vue 总入口,需要排除所有依赖
|
||||||
|
if (/vue\/(index|pc|mobile|mobile-first)\.ts$/.test(importer)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return external(source)
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
strict: false,
|
||||||
|
manualChunks: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lib: {
|
||||||
|
// 这里可以多入口打包,也可以单入口打包
|
||||||
|
entry,
|
||||||
|
formats,
|
||||||
|
fileName: (format, entryName) => `${entryName}.js`
|
||||||
|
},
|
||||||
|
outDir
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BuildUiOption {
|
||||||
|
buildTarget: string // 目标版本,必填, 不需要major位,因为需要同时打出vue2和vue3的包
|
||||||
|
formats: string[] // 打包的格式
|
||||||
|
clean: boolean // 是否清空build产物
|
||||||
|
scope?: string // npm的组织名称
|
||||||
|
min?: boolean // 是否压缩产物
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEntryTasks(): Module[] {
|
||||||
|
// 读取TinyVue组件库入口文件
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
path: `solid/index.ts`,
|
||||||
|
libPath: `solid/index`,
|
||||||
|
type: 'module',
|
||||||
|
name: kebabCase({ str: '@opensolidtiny/solid' }),
|
||||||
|
global: capitalizeKebabCase('opentinySolid'),
|
||||||
|
importName: '@opensolidtiny/solid'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSolidCommonTasks(): Module[] {
|
||||||
|
// 读取TinyVue组件库入口文件
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
path: `solid/src/common/src/index.ts`,
|
||||||
|
libPath: `common/lib/index`,
|
||||||
|
type: 'module',
|
||||||
|
name: kebabCase({ str: '@opensolidtiny/solid-common' }),
|
||||||
|
global: capitalizeKebabCase('opentinySolidCommon'),
|
||||||
|
importName: '@opensolidtiny/solid-common'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTasks(names: string[]): Module[] {
|
||||||
|
// 没有指定组件,则全量构建
|
||||||
|
if (names.length === 0) {
|
||||||
|
return [...getAllModules(false)]
|
||||||
|
}
|
||||||
|
|
||||||
|
return names
|
||||||
|
.map((name) =>
|
||||||
|
getByName({
|
||||||
|
name: kebabCase({ str: name.replace('@opentiny/vue-', '') }),
|
||||||
|
isSort: false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.flat()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TinyVue组件打包主入口
|
||||||
|
* @private
|
||||||
|
* @param {string[]} names 需要打包的名字,如果不传默认打包全量组件 例如只打包alert和button两个组件 pnpm build:ui alert button
|
||||||
|
* @param {BuildUiOption} buildUiOption 具体参数参考BuildUiOption接口
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function buildSolid(
|
||||||
|
names: string[] = [],
|
||||||
|
{ buildTarget = '3.14.0', formats = ['es'], clean = false, scope = '@opensolidtiny' }: BuildUiOption
|
||||||
|
) {
|
||||||
|
// 是否清空构建目录
|
||||||
|
let emptyOutDir = clean
|
||||||
|
// 要构建的模块
|
||||||
|
let tasks = getTasks(names).filter((item) => !item.path.includes('mobile'))
|
||||||
|
|
||||||
|
// 如果指定了打包icon或者没有传入任何组件
|
||||||
|
if (names.some((name) => name.includes('icon')) || !names.length) {
|
||||||
|
const icons = getAllIcons()
|
||||||
|
icons.forEach((item) => {
|
||||||
|
item.libPath = item.libPath.replace('vue-icon', 'solid-icon')
|
||||||
|
})
|
||||||
|
|
||||||
|
tasks.push(...icons)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过虑出需要打包的solid组件入口
|
||||||
|
tasks = tasks
|
||||||
|
.filter((item) =>
|
||||||
|
buildComponents.some((value) => item.path.includes(`/${value}/`) || item.path.includes('vue-icon'))
|
||||||
|
)
|
||||||
|
.filter((item) => !item.path.includes('icon-saas'))
|
||||||
|
|
||||||
|
tasks.forEach((item) => {
|
||||||
|
if (item.libPath.includes('vue-icon')) {
|
||||||
|
item.libPath = item.libPath.replace('vue-icon', 'solid-icon')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 打包入口文件
|
||||||
|
if (names.length === 0 || names.some((name) => ['@opensolidtiny/solid', 'solid'].includes(name))) {
|
||||||
|
tasks.push(...getEntryTasks(), ...getSolidCommonTasks())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 要构建的vue框架版本
|
||||||
|
const message = `TINY for solid: ${JSON.stringify(names.length ? names : '全量')}`
|
||||||
|
await batchBuildAll({ tasks, formats, message, emptyOutDir, buildTarget, npmScope: scope })
|
||||||
|
// 确保只运行一次
|
||||||
|
emptyOutDir = false
|
||||||
|
}
|
|
@ -4,3 +4,4 @@ export * from './build-runtime'
|
||||||
export * from './build-ui-react'
|
export * from './build-ui-react'
|
||||||
export * from './build-entry-react'
|
export * from './build-entry-react'
|
||||||
export * from './build-chart-theme'
|
export * from './build-chart-theme'
|
||||||
|
export * from './build-ui-solid'
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# vue-vite-template2jsx
|
||||||
|
|
||||||
|
## 一个可以将vue模板转化成jsx语法的vite插件
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { createRequire } from 'node:module'
|
||||||
|
import { readFileSync, existsSync } from 'node:fs'
|
||||||
|
import { transformVueTemplateToSolid } from './src/plugin.js'
|
||||||
|
import { dirname } from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
|
const __dirname = dirname(__filename)
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url)
|
||||||
|
|
||||||
|
const indexTemplatePath = path.resolve(__dirname, './template/src/index.tss')
|
||||||
|
const pcTemplatePath = path.resolve(__dirname, './template/src/pc.jsxs')
|
||||||
|
|
||||||
|
// 多模板入口模板
|
||||||
|
const indexTemplate = readFileSync(indexTemplatePath, { encoding: 'utf-8' })
|
||||||
|
|
||||||
|
// PC模板入口
|
||||||
|
const pcTemplate = readFileSync(pcTemplatePath, { encoding: 'utf-8' })
|
||||||
|
|
||||||
|
const matchComponentRegx = /vue\/src\/([a-z]+)\/src/
|
||||||
|
|
||||||
|
const fundProps = (code, findStr) => {
|
||||||
|
let findIndex = code.indexOf(findStr)
|
||||||
|
let props = ''
|
||||||
|
|
||||||
|
if (~findIndex) {
|
||||||
|
const endStr = 'export '
|
||||||
|
let constantsPart = code.substring(findIndex + findStr.length, code.length)
|
||||||
|
props = constantsPart.substring(0, constantsPart.indexOf(endStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSvgContent = (svg) => {
|
||||||
|
return svg.substring(svg.indexOf('>') + 1, svg.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDefaultValueFromEntry = (id) => {
|
||||||
|
const componentMatchs = id.match(matchComponentRegx)
|
||||||
|
const entryPath = id.replace('pc.vue', 'index.ts')
|
||||||
|
if (existsSync(entryPath)) {
|
||||||
|
if (componentMatchs?.[1]) {
|
||||||
|
const code = readFileSync(entryPath, { encoding: 'utf-8' })
|
||||||
|
let constants = fundProps(code, '$constants =') || '{}'
|
||||||
|
let propsContent = fundProps(code, 'Props =') || fundProps(code, 'props :')
|
||||||
|
let props = {}
|
||||||
|
|
||||||
|
if (propsContent) {
|
||||||
|
const propsContentParts = propsContent.split(/\n/)
|
||||||
|
propsContentParts.forEach((item, index) => {
|
||||||
|
if (item.indexOf('type:') && ['Boolean', 'String', 'Number'].some((type) => item.includes(type))) {
|
||||||
|
const name = propsContentParts[index - 1].replace(/\W/g, '')
|
||||||
|
let value = propsContentParts[index + 1]
|
||||||
|
|
||||||
|
if (name && value.includes('default:')) {
|
||||||
|
const type = /['"]/.test(value) ? 'Char' : 'Other'
|
||||||
|
value = value.replace('default:', '').replace(/["|'|\s,]/g, '')
|
||||||
|
props[name] = { type, value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
$constants: constants,
|
||||||
|
$props: props
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
$constants: '{}',
|
||||||
|
$props: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function vueTemplate2Jsx() {
|
||||||
|
return {
|
||||||
|
name: '@opentiny/vue-vite-template2jsx',
|
||||||
|
enforce: 'pre',
|
||||||
|
transform(code, id) {
|
||||||
|
if (id.includes('vue-icon/src')) {
|
||||||
|
const svgName = code.match(/([a-z1-9-]+)\.svg/)
|
||||||
|
|
||||||
|
if (svgName && svgName[0]) {
|
||||||
|
let svgPath
|
||||||
|
|
||||||
|
try {
|
||||||
|
svgPath = require.resolve(`@opentiny/vue-theme/svgs/${svgName[0]}`)
|
||||||
|
} catch (e) {
|
||||||
|
svgPath = path.resolve(__dirname, `../../packages/theme/src/svgs/${svgName[0]}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsSync(svgPath)) {
|
||||||
|
let svgContent = readFileSync(svgPath, { encoding: 'utf-8' })
|
||||||
|
|
||||||
|
if (svgContent.includes('<!--')) {
|
||||||
|
svgContent = svgContent.replace(/<!--[\s\S]+-->/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svgContent.includes('<?xml')) {
|
||||||
|
svgContent = getSvgContent(svgContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svgContent.includes('<!DOCTYPE')) {
|
||||||
|
svgContent = getSvgContent(svgContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svgContent.includes('<style')) {
|
||||||
|
svgContent = svgContent.replace(/<style[\s\S]+<\/style>/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewBox = svgContent.match(/viewBox="[\d|\s]+"/)?.[0] || `viewBox="0 0 24 24"`
|
||||||
|
|
||||||
|
return `export default function(props) {
|
||||||
|
return (<svg style={props.style} xmlns="http://www.w3.org/2000/svg" onClick={props.onClick} ${viewBox} xml:space="preserve" class={'tiny-svg svgs-icon ' + props.class}>
|
||||||
|
${getSvgContent(svgContent)}
|
||||||
|
)
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.includes(`src/index.ts`) && !id.includes(`common/src/index.ts`)) {
|
||||||
|
return indexTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.includes('pc.vue')) {
|
||||||
|
const componentMatchs = id.match(matchComponentRegx)
|
||||||
|
if (componentMatchs?.[1]) {
|
||||||
|
const mapData = getDefaultValueFromEntry(id)
|
||||||
|
const pcComponent = transformVueTemplateToSolid(pcTemplate, code, componentMatchs[1], mapData)
|
||||||
|
return pcComponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "@opentiny/vue-vite-template2jsx",
|
||||||
|
"version": "1.1.5",
|
||||||
|
"description": "A TinyVue vite import plugin",
|
||||||
|
"main": "index.js",
|
||||||
|
"module": "index.js",
|
||||||
|
"author": "Tiny Vue Team",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git@github.com:opentiny/tiny-vue.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"vite-plugin",
|
||||||
|
"TinyVue",
|
||||||
|
"vite"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"fs-extra": "^11.2.0",
|
||||||
|
"vue-template-compiler": "2.6.14",
|
||||||
|
"html-dom-parser": "~5.0.8"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vite": ">=4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
export const builtinDirective = ['v-if', 'v-for', 'v-model', 'v-slot', 'v-else', 'v-else-if']
|
||||||
|
export const builtinDirectivesWithoutVModel = builtinDirective.filter((x) => x !== 'v-model')
|
||||||
|
|
||||||
|
export const isNamedSlot = function (dObj) {
|
||||||
|
return Object.keys(dObj.attribs).find(isSlotAttribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSlotName(dObj) {
|
||||||
|
if (!dObj.attribs) return 'default'
|
||||||
|
const attr = Object.keys(dObj.attribs).find(isSlotAttribute)
|
||||||
|
if (!isNamedSlot(dObj)) return 'default'
|
||||||
|
const prefix = attr.startsWith('#') ? '#' : 'v-slot:'
|
||||||
|
return attr.split(prefix)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSlotProps(dObj) {
|
||||||
|
const attr = Object.keys(dObj.attribs).find(isSlotAttribute)
|
||||||
|
return dObj.attribs[attr]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCondition = function (dObj) {
|
||||||
|
return dObj.attribs['v-if'] || dObj.attribs['v-else-if']
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isConditional(dObj) {
|
||||||
|
return isVIf(dObj) || isVElse(dObj) || isVElseIf(dObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isVIf(dObj) {
|
||||||
|
return dObj.type !== 'text' && Object.prototype.hasOwnProperty.call(dObj.attribs, 'v-if')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isVElseIf(dObj) {
|
||||||
|
return dObj.type !== 'text' && Object.prototype.hasOwnProperty.call(dObj.attribs, 'v-else-if')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isVElse(dObj) {
|
||||||
|
return dObj.type !== 'text' && Object.prototype.hasOwnProperty.call(dObj.attribs, 'v-else')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isVifOrVElseIf(dObj) {
|
||||||
|
return isVIf(dObj) || isVElseIf(dObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isVFor = function (dObj) {
|
||||||
|
return dObj.attribs && Object.prototype.hasOwnProperty.call(dObj.attribs, 'v-for')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isSlotAttribute = function (attr) {
|
||||||
|
return attr.startsWith('#') || attr.startsWith('v-slot')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isValueAttribute(attr) {
|
||||||
|
return !['v-if', 'v-else', 'v-else-if', 'v-for', 'v-slot'].includes(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTextBlank(txt) {
|
||||||
|
for (let i of txt) {
|
||||||
|
if (i !== ' ' && i !== '\n') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTag(dObj) {
|
||||||
|
return dObj.name
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFirstChild(dObj) {
|
||||||
|
return dObj.children.length && dObj.children[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEmptyObject(dObj) {
|
||||||
|
return dObj.type === 'text' && isTextBlank(dObj.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isText(dObj) {
|
||||||
|
return dObj.type === 'text'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isComponent(dObj) {
|
||||||
|
return dObj.name === 'component'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTransition(dObj) {
|
||||||
|
return ['transition-group', 'transition'].includes(dObj.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getText(dObj) {
|
||||||
|
return dObj.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertText(text) {
|
||||||
|
return text.replace(/{{/g, '{').replace(/}}/g, '}').replace(/\$t/g, 't')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setDynamicComponent(dObj) {
|
||||||
|
let component = dObj.attribs[':is']
|
||||||
|
dObj.attribs[':component'] = `resolveComponent(${component}, useIcons)`
|
||||||
|
delete dObj.attribs[':is']
|
||||||
|
dObj.name = 'Dynamic'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBindClass(attrName) {
|
||||||
|
return attrName === ':class'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBindAll(attrName) {
|
||||||
|
return attrName === 'v-bind'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLoopCommand(dObj) {
|
||||||
|
const command = dObj.attribs['v-for']
|
||||||
|
const separator = command.includes(' in ') ? ' in ' : ' of '
|
||||||
|
const s = command.split(separator)
|
||||||
|
return `${s[1].trim()}.map(${s[0].trim()} `
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBindingAttrs(attrName) {
|
||||||
|
return attrName.startsWith(':')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toCamelCase = function (s) {
|
||||||
|
let res = ''
|
||||||
|
for (let i = 0; i < s.length; i++) {
|
||||||
|
if (i > 0 && s[i - 1] === '-') {
|
||||||
|
res += s[i].toUpperCase()
|
||||||
|
} else if (s[i] !== '-') res += s[i]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAttrName(attrName, prefix) {
|
||||||
|
return toCamelCase(attrName.split(prefix)[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEventListener(attrName) {
|
||||||
|
return attrName.startsWith('@')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getListenerName(attrName) {
|
||||||
|
const idx = attrName.indexOf('.')
|
||||||
|
if (idx === -1) return attrName.substr(1)
|
||||||
|
else return attrName.substr(1, idx - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getModifiers(attrName) {
|
||||||
|
let res = attrName.split('.')
|
||||||
|
res.shift()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export const convertEventListenerName = function (attrName) {
|
||||||
|
const name = getListenerName(attrName)
|
||||||
|
return toCamelCase(`on-${name}`).replace(/modelvalue/g, 'modelValue')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const convertListener = function (listener) {
|
||||||
|
const isFunction = listener.includes('=>') || (!listener.includes('(') && !listener.includes('='))
|
||||||
|
return isFunction
|
||||||
|
? listener
|
||||||
|
: listener.includes('$event')
|
||||||
|
? `(v) => {${listener.replace(/\$event/g, 'v')}}`
|
||||||
|
: `() => ${listener}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const convertEventListener = function (listener, modifiers = []) {
|
||||||
|
const eventName = convertListener(listener)
|
||||||
|
return modifiers.length === 0
|
||||||
|
? `${eventName}`
|
||||||
|
: `(event) => withModifiers(event, ${eventName}, [${modifiers.map((m) => `'${m}'`).join(', ')}])`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isVModel = function (attrName) {
|
||||||
|
return attrName.startsWith('v-model')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isBooleanAttrs = function (attrName) {
|
||||||
|
return attrName === ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isRef = function (attrName) {
|
||||||
|
return attrName === 'ref'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isDynamic = function (attrName) {
|
||||||
|
return attrName === ':is'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const convertRefName = function (name) {
|
||||||
|
return toCamelCase(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = fileURLToPath(import.meta.url)
|
||||||
|
export const dirname = path.dirname(filename)
|
||||||
|
|
||||||
|
export const capitalize = function (str) {
|
||||||
|
return typeof str === 'string' ? str.slice(0, 1).toUpperCase() + str.slice(1) : str
|
||||||
|
}
|
||||||
|
|
||||||
|
export const capitalizeKebabCase = function (str, splitChar = '-') {
|
||||||
|
return typeof str === 'string' ? str.split(splitChar).map(capitalize).join('') : str
|
||||||
|
}
|
||||||
|
|
||||||
|
export const kebabCase = (str, splitChar = '-') => {
|
||||||
|
if (!str || typeof str !== 'string') return str
|
||||||
|
|
||||||
|
return str
|
||||||
|
.split('')
|
||||||
|
.map((char, index) => {
|
||||||
|
const charCod = char.charCodeAt(0)
|
||||||
|
|
||||||
|
if (charCod < 65 || charCod > 122) return char
|
||||||
|
|
||||||
|
return charCod >= 65 && charCod <= 90 ? (index !== 0 ? splitChar : '') + char.toLowerCase() : char
|
||||||
|
})
|
||||||
|
.join('')
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
import parse from 'html-dom-parser'
|
||||||
|
import renderVueDomObject from './render.js'
|
||||||
|
import { parseComponent } from 'vue-template-compiler'
|
||||||
|
import { template } from 'lodash-es'
|
||||||
|
|
||||||
|
// 处理computed操作,如果想要具有响应式需要特殊处理
|
||||||
|
const computedMap = {
|
||||||
|
button: ['buttonDisabled', 'plain', 'formDisabled'],
|
||||||
|
alert: ['getIcon', 'getTitle', 'alertClass', 'alertStyle'],
|
||||||
|
switch: ['wrapClasses', 'isDisplayOnly', 'innerClasses']
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transformVueTemplateToSolid = (jsxTemplate, vueCode, componentName, mapData) => {
|
||||||
|
const component = parseComponent(vueCode)
|
||||||
|
|
||||||
|
if (component.template && component.script) {
|
||||||
|
const useCommons = [
|
||||||
|
'useSetup',
|
||||||
|
'getClassList',
|
||||||
|
'mergeProps',
|
||||||
|
'resolveComponent',
|
||||||
|
'$prefix',
|
||||||
|
'TransitionGroup',
|
||||||
|
'Transition',
|
||||||
|
't',
|
||||||
|
'withModifiers'
|
||||||
|
]
|
||||||
|
const script = component.script.content
|
||||||
|
const startStr = 'props: ['
|
||||||
|
const sliceStr = script.substring(script.indexOf(startStr) + startStr.length)
|
||||||
|
const props = sliceStr
|
||||||
|
.substring(0, sliceStr.indexOf(']'))
|
||||||
|
.replace('...props,', '')
|
||||||
|
.replace(/[\n\s']/g, '')
|
||||||
|
.split(',')
|
||||||
|
|
||||||
|
const specialVars = ['useIcons']
|
||||||
|
const node = parse(component.template.content, { recognizeSelfClosing: true })[0]
|
||||||
|
const renderNode = renderVueDomObject(node)
|
||||||
|
const useApi = renderNode.useAttrs.realAttrs.filter(
|
||||||
|
(item) => !props.includes(item) && !useCommons.includes(item) && !specialVars.includes(item)
|
||||||
|
)
|
||||||
|
let useProps = props.filter((item) => renderNode.useAttrs.realAttrs.includes(item))
|
||||||
|
let jsxContent = renderNode.default.content.replace(/slots\.default/g, 'children')
|
||||||
|
let defaultProps = []
|
||||||
|
let importIcons = script.match(/import\s?{\s?ico.+/)
|
||||||
|
let useIcons = '{}'
|
||||||
|
|
||||||
|
if (computedMap[componentName]) {
|
||||||
|
computedMap[componentName].forEach((item) => {
|
||||||
|
jsxContent = jsxContent.replaceAll(`state.${item}`, `state.${item}()`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapData.$props) {
|
||||||
|
const getKeyValue = (key, props, char = '=') => {
|
||||||
|
return `${key} ${char} ${props.type === 'Char' ? `'${props.value}'` : props.value}`
|
||||||
|
}
|
||||||
|
|
||||||
|
useProps = useProps.map((item) => {
|
||||||
|
const props = mapData.$props[item]
|
||||||
|
if (props) {
|
||||||
|
return getKeyValue(item, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let key in mapData.$props) {
|
||||||
|
const props = mapData.$props[key]
|
||||||
|
defaultProps.push(getKeyValue(key, props, ':'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
importIcons = importIcons?.[0]
|
||||||
|
if (importIcons) {
|
||||||
|
importIcons = importIcons.replace(/icon/g, 'Icon')
|
||||||
|
useIcons = importIcons.match(/\{(.+)\}/)[0] || '{}'
|
||||||
|
}
|
||||||
|
|
||||||
|
const realComponet = template(jsxTemplate)({
|
||||||
|
USEAPI: useApi.join(','),
|
||||||
|
USEPROPS: useProps.join(','),
|
||||||
|
JSX: jsxContent,
|
||||||
|
NAME: componentName,
|
||||||
|
CONSTANTS: mapData.$constants || '{}',
|
||||||
|
DEFAULTPROPS: defaultProps.join(','),
|
||||||
|
IMPORTICONS: importIcons,
|
||||||
|
USEICONS: useIcons,
|
||||||
|
USECOMMONS: useCommons.join(',')
|
||||||
|
})
|
||||||
|
|
||||||
|
return realComponet
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,276 @@
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import {
|
||||||
|
builtinDirectivesWithoutVModel,
|
||||||
|
isVModel,
|
||||||
|
convertText,
|
||||||
|
isSlotAttribute,
|
||||||
|
isBooleanAttrs,
|
||||||
|
isVIf,
|
||||||
|
isVElse,
|
||||||
|
isVFor,
|
||||||
|
isTextBlank,
|
||||||
|
isRef,
|
||||||
|
convertRefName,
|
||||||
|
getCondition,
|
||||||
|
getTag,
|
||||||
|
getFirstChild,
|
||||||
|
isEmptyObject,
|
||||||
|
isText,
|
||||||
|
getText,
|
||||||
|
isVElseIf,
|
||||||
|
getLoopCommand,
|
||||||
|
getSlotProps,
|
||||||
|
getSlotName,
|
||||||
|
isBindingAttrs,
|
||||||
|
getAttrName,
|
||||||
|
isEventListener,
|
||||||
|
getModifiers,
|
||||||
|
convertEventListenerName,
|
||||||
|
convertEventListener,
|
||||||
|
isBindClass,
|
||||||
|
isBindAll,
|
||||||
|
isComponent,
|
||||||
|
setDynamicComponent,
|
||||||
|
capitalizeKebabCase,
|
||||||
|
isTransition
|
||||||
|
} from './helpers.js'
|
||||||
|
|
||||||
|
let renderDomAttrs = []
|
||||||
|
|
||||||
|
const renderSlots = (slots) => {
|
||||||
|
if (Object.keys(slots).length === 0) return ''
|
||||||
|
let res = ` v-slots={{ `
|
||||||
|
Object.keys(slots).forEach((slotName) => {
|
||||||
|
let content = slots[slotName].content
|
||||||
|
if (!content.startsWith('<>')) content = `<> ${content} </>\n`
|
||||||
|
if (!isTextBlank(slots[slotName].content))
|
||||||
|
res = res + `'${slotName}': (${slots[slotName].slotProps}) => ${content}, \n`
|
||||||
|
})
|
||||||
|
res += ` }}`
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertAttrs = function (attrs) {
|
||||||
|
const res = {}
|
||||||
|
|
||||||
|
Object.keys(attrs).forEach((attrName) => {
|
||||||
|
if (isSlotAttribute(attrName) || isBindAll(attrName)) return
|
||||||
|
if (isBindingAttrs(attrName)) {
|
||||||
|
if (isBindClass(attrName)) {
|
||||||
|
res.classList = `{ getClassList(${attrs[attrName]}) }`
|
||||||
|
} else {
|
||||||
|
res[getAttrName(attrName, ':')] = `{ ${attrs[attrName]} }`
|
||||||
|
}
|
||||||
|
} else if (isEventListener(attrName)) {
|
||||||
|
const eventListenerName = convertEventListenerName(attrName)
|
||||||
|
const modifiers = getModifiers(attrName)
|
||||||
|
const listener = convertEventListener(attrs[attrName], modifiers)
|
||||||
|
res[eventListenerName] = `{${listener}}`
|
||||||
|
} else if (isVModel(attrName)) {
|
||||||
|
res[attrName] = `{${attrs[attrName]}}`
|
||||||
|
} else if (isRef(attrName)) {
|
||||||
|
res[attrName] = `{${convertRefName(attrs[attrName])}}`
|
||||||
|
} else if (isBooleanAttrs(attrs[attrName])) {
|
||||||
|
res[attrName] = ''
|
||||||
|
} else {
|
||||||
|
res[attrName] = `"${attrs[attrName]}"`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderAttrs = function (attrs) {
|
||||||
|
let res = ''
|
||||||
|
Object.keys(attrs).forEach((attrName) => {
|
||||||
|
res += attrs[attrName] === '' ? `${attrName} ` : `${attrName}=${attrs[attrName]} ` // boolean attrs
|
||||||
|
})
|
||||||
|
return res === '' ? '' : ` ${res}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderDomObj = function (dObj, needWrap = true) {
|
||||||
|
if (isEmptyObject(dObj)) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isText(dObj)) return convertText(getText(dObj))
|
||||||
|
|
||||||
|
if (isComponent(dObj)) {
|
||||||
|
setDynamicComponent(dObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagName = getTag(dObj)
|
||||||
|
const slots = renderVueDomObject(getFirstChild(dObj))
|
||||||
|
const attrs = convertAttrs(cloneDeep(dObj.attribs))
|
||||||
|
let tag = tagName === 'template' ? '' : tagName
|
||||||
|
|
||||||
|
builtinDirectivesWithoutVModel.forEach((attr) => delete attrs[attr])
|
||||||
|
|
||||||
|
if (Object.keys(dObj.attribs).length) {
|
||||||
|
renderDomAttrs.push(dObj.attribs)
|
||||||
|
if (dObj.name === 'slot') {
|
||||||
|
renderDomAttrs.push({ 'v-slot': `slots.${dObj.attribs.name}` })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagName.indexOf('icon-') === 0) {
|
||||||
|
tag = capitalizeKebabCase(tag)
|
||||||
|
} else if (isTransition(dObj)) {
|
||||||
|
tag = capitalizeKebabCase(tag)
|
||||||
|
attrs.onExit = '{ (el, done) => { setTimeout(done, 300) } }'
|
||||||
|
|
||||||
|
const getRealClassName = (part) => `"${(attrs.name + part).replace(/"/g, '')}"`
|
||||||
|
attrs.enterToClass = getRealClassName('-enter-from')
|
||||||
|
attrs.exitActiveClass = getRealClassName('-leave-active')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(slots).length < 2) {
|
||||||
|
let content = slots.default?.content || ''
|
||||||
|
if (!tag) {
|
||||||
|
let slotName = 'children'
|
||||||
|
|
||||||
|
if (attrs.name) {
|
||||||
|
slotName = `slots.${attrs.name.replace(/["']/g, '')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
return `\n { ${slotName} } \n`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/<[^>]+>/.test(content)) {
|
||||||
|
content = `<span>${content}</span>`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needWrap) {
|
||||||
|
return `{ ${slotName} || ${content} }`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `(${slotName} || ${content})`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<${tag}${renderAttrs(attrs)}> \n ${content}</${tag}>\n`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<${tag}${renderAttrs(attrs)}${renderSlots(slots)}></${tag}>\n`
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderVIfSegment = function (first, last, nodes, isRoot) {
|
||||||
|
const open = isRoot ? '{' : '('
|
||||||
|
const close = isRoot ? '}' : ')'
|
||||||
|
const domContent = renderDomObj(nodes[first], false)
|
||||||
|
|
||||||
|
if (first + 1 === last) {
|
||||||
|
return `${open} \n (${getCondition(nodes[first])}) && \n ${domContent} ${close} \n`
|
||||||
|
} else {
|
||||||
|
if (isVElse(nodes[first + 1])) {
|
||||||
|
return `${open} \n (${getCondition(nodes[first])}) ? \n ${domContent} : \n
|
||||||
|
${renderDomObj(nodes[first + 1])} ${close} \n`
|
||||||
|
} else {
|
||||||
|
return `${open} \n (${getCondition(nodes[first])}) ? \n ${domContent} : \n
|
||||||
|
${renderVIfSegment(first + 1, last, nodes)} ${close} \n`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderVFor = function (dObj) {
|
||||||
|
return `{${getLoopCommand(dObj)} => \n ${renderDomObj(dObj)} )}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderVueDomObject = function (dObj) {
|
||||||
|
if (!dObj) return ''
|
||||||
|
let _dObj = cloneDeep(dObj)
|
||||||
|
let slots = {}
|
||||||
|
|
||||||
|
slots.default = { slotProps: '', content: '', nodes: [] }
|
||||||
|
|
||||||
|
function addNode(dObj) {
|
||||||
|
if (isEmptyObject(_dObj)) return
|
||||||
|
const slotName = getSlotName(dObj)
|
||||||
|
|
||||||
|
if (dObj.name === 'slot') {
|
||||||
|
dObj.name = 'template'
|
||||||
|
}
|
||||||
|
|
||||||
|
slots[slotName] = slots[slotName] || { slotProps: getSlotProps(dObj), nodes: [] }
|
||||||
|
slots[slotName].nodes.push(dObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
addNode(_dObj)
|
||||||
|
while (_dObj.next) {
|
||||||
|
_dObj = _dObj.next
|
||||||
|
addNode(_dObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNodes(nodes) {
|
||||||
|
let res = ''
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
if (isVIf(nodes[i])) {
|
||||||
|
let j = i + 1
|
||||||
|
while (j < nodes.length && isVElseIf(nodes[j])) j++
|
||||||
|
if (j < nodes.length) {
|
||||||
|
if (isVElse(nodes[j])) j++
|
||||||
|
}
|
||||||
|
res += renderVIfSegment(i, j, nodes, true)
|
||||||
|
i = j - 1
|
||||||
|
} else if (isVFor(nodes[i])) {
|
||||||
|
res = res + renderVFor(nodes[i])
|
||||||
|
} else {
|
||||||
|
res = res + renderDomObj(nodes[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(slots).forEach((slotName) => {
|
||||||
|
let nodes = slots[slotName].nodes
|
||||||
|
let content = renderNodes(nodes)
|
||||||
|
slots[slotName].content = content
|
||||||
|
})
|
||||||
|
return slots
|
||||||
|
}
|
||||||
|
|
||||||
|
const vueOperators = ':,v-,@'.split(',')
|
||||||
|
const globalProps = 'true,false'.split(',')
|
||||||
|
const charReg = /[\n\+\[\&\]!=\{\},\?\:\|]/g
|
||||||
|
const specialProps = 'a,$attrs,state,children,slots'.split(',')
|
||||||
|
|
||||||
|
const getUseAttrs = function () {
|
||||||
|
let attrs = renderDomAttrs
|
||||||
|
.map((item) => {
|
||||||
|
return Object.entries(item)
|
||||||
|
.filter(([key, _]) => vueOperators.some((val) => key.indexOf(val) === 0))
|
||||||
|
.map((arr) => arr[1])
|
||||||
|
.map((key) =>
|
||||||
|
key
|
||||||
|
.replace(charReg, ' ')
|
||||||
|
.split(' ')
|
||||||
|
.filter((name) => name && !name.includes('-') && !name.includes("'"))
|
||||||
|
)
|
||||||
|
.flat()
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
.map((item) => {
|
||||||
|
return item.split(/[\(\)]/)
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
.filter((name) => name.length && !globalProps.includes(name))
|
||||||
|
|
||||||
|
attrs = [...new Set(attrs)]
|
||||||
|
|
||||||
|
const realAttrs = [...new Set(attrs.map((attr) => attr.split('.')[0]))].filter((name) => !specialProps.includes(name))
|
||||||
|
|
||||||
|
return {
|
||||||
|
attrs,
|
||||||
|
realAttrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function (dObj) {
|
||||||
|
renderDomAttrs = []
|
||||||
|
const component = renderVueDomObject(dObj)
|
||||||
|
const useAttrs = getUseAttrs()
|
||||||
|
|
||||||
|
return {
|
||||||
|
...component,
|
||||||
|
useAttrs
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import <%=UPPERNAME%> from './src'
|
||||||
|
|
||||||
|
export default <%=UPPERNAME%>
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "@opentiny/solid-<%=NAME%>",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.ts",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@opentiny/vue-renderless": "workspace:~",
|
||||||
|
"@opentiny/solid-common": "workspace:~",
|
||||||
|
"@opentiny/vue-theme": "workspace:~"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import pc from './pc.vue'
|
||||||
|
|
||||||
|
export default function (props) {
|
||||||
|
const { tiny_mode = 'pc' } = props
|
||||||
|
|
||||||
|
const S = {
|
||||||
|
pc
|
||||||
|
}[tiny_mode]
|
||||||
|
|
||||||
|
return S(props)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { renderless } from '@opentiny/vue-renderless/<%=NAME%>/vue'
|
||||||
|
import { <%=USECOMMONS%> } from '@opentiny/solid-common'
|
||||||
|
import '@opentiny/vue-theme/<%=NAME%>/index.less'
|
||||||
|
<%=IMPORTICONS%>
|
||||||
|
const useIcons = <%=USEICONS%>
|
||||||
|
const $constants = <%=CONSTANTS%>
|
||||||
|
export default function (props) {
|
||||||
|
const { children, slots = {}, <%=USEPROPS%> } = props
|
||||||
|
const { state, <%=USEAPI%> } = useSetup({
|
||||||
|
props: mergeProps({<%=DEFAULTPROPS%>}, props),
|
||||||
|
renderless,
|
||||||
|
constants: $constants,
|
||||||
|
})
|
||||||
|
return (<><%=JSX%></>)
|
||||||
|
}
|
|
@ -143,8 +143,10 @@
|
||||||
"prettier": "prettier --config .prettierrc --write .",
|
"prettier": "prettier --config .prettierrc --write .",
|
||||||
"// ---------- openinula 相关脚本命令 ----------": "",
|
"// ---------- openinula 相关脚本命令 ----------": "",
|
||||||
"dev:openinula": "pnpm -C examples/openinula-docs run dev",
|
"dev:openinula": "pnpm -C examples/openinula-docs run dev",
|
||||||
"// ---------- solid 相关脚本命令 ----------": "",
|
"// ---------- 预览发布后的solid组件 ----------": "",
|
||||||
"dev:solid": "pnpm -C examples/solid-docs run dev"
|
"preview:solid": "pnpm -C examples/solid-demo run dev",
|
||||||
|
"build:solid": "pnpm -C internals/cli build:solid",
|
||||||
|
"pub:solid": "pnpm --filter=\"./packages/dist-solid/**\" publish --no-git-checks --access=public --registry=https://registry.npmjs.org"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/composition-api": "1.7.2",
|
"@vue/composition-api": "1.7.2",
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import Button from '@opentiny/solid-button'
|
import Button from '@opentiny/solid-button'
|
||||||
|
import Alert from '@opentiny/solid-alert'
|
||||||
|
|
||||||
export const version = '1.0.0'
|
export const version = '1.0.0'
|
||||||
|
|
||||||
export { Button }
|
export { Button, Alert }
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Button
|
Button,
|
||||||
|
Alert
|
||||||
} as any
|
} as any
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentiny/solid-common": "workspace:~",
|
"@opentiny/solid-common": "workspace:~",
|
||||||
"@opentiny/solid-button": "workspace:~"
|
"@opentiny/vue-button": "workspace:~",
|
||||||
|
"@opentiny/vue-alert": "workspace:~"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentiny/vue-renderless": "workspace:~",
|
"@opentiny/vue-renderless": "workspace:~",
|
||||||
"@opentiny/vue-theme": "workspace:~",
|
"@opentiny/vue-theme": "workspace:~",
|
||||||
"classnames": "^2.3.2",
|
"solid-transition-group": "0.2.3",
|
||||||
"solid-js": "^1.7.8"
|
"solid-js": "^1.7.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import * as hooks from 'solid-js'
|
import * as hooks from 'solid-js'
|
||||||
import { createSignal, onCleanup, createMemo } from 'solid-js'
|
import { onCleanup, onMount, createResource, createEffect, on, mergeProps } from 'solid-js'
|
||||||
|
import { createMutable } from 'solid-js/store'
|
||||||
|
import { TransitionGroup, Transition } from 'solid-transition-group'
|
||||||
import '@opentiny/vue-theme/base/index.less'
|
import '@opentiny/vue-theme/base/index.less'
|
||||||
|
|
||||||
const EVENTS_PREFIX = 'on'
|
const EVENTS_PREFIX = 'on'
|
||||||
|
@ -7,41 +9,12 @@ const EVENTS_PREFIX = 'on'
|
||||||
// 处理solid事件触发机制
|
// 处理solid事件触发机制
|
||||||
export const emit =
|
export const emit =
|
||||||
(props) =>
|
(props) =>
|
||||||
(evName, ...args) => {
|
(evName, ...args) => {
|
||||||
const eventsName = `${EVENTS_PREFIX}${evName[0].toLocaleUpperCase()}${evName.slice(1)}`
|
const eventsName = `${EVENTS_PREFIX}${evName[0].toLocaleUpperCase()}${evName.slice(1)}`
|
||||||
if (props[eventsName] && typeof props[eventsName] === 'function') {
|
if (props[eventsName] && typeof props[eventsName] === 'function') {
|
||||||
props[eventsName](...args)
|
props[eventsName](...args)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useSetState = (initialState) => {
|
|
||||||
const [state, setState] = createSignal(initialState, { equals: false })
|
|
||||||
|
|
||||||
return [state, setState]
|
|
||||||
}
|
|
||||||
|
|
||||||
// props 应该不用做处理, props 都是 . 访问。
|
|
||||||
export const reactive = (staticObject) => {
|
|
||||||
const [state, setState] = useSetState(staticObject)
|
|
||||||
|
|
||||||
return new Proxy(state(), {
|
|
||||||
get(target, property) {
|
|
||||||
if (property === 'solidState') {
|
|
||||||
return state
|
|
||||||
}
|
}
|
||||||
if (typeof target[property] === 'function') {
|
|
||||||
return target[property](target)
|
|
||||||
} else {
|
|
||||||
return target[property]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
set(target, property, value) {
|
|
||||||
Reflect.set(target, property, value)
|
|
||||||
setState((val) => val)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// nextTick, 等待 dom 更新后触发回调
|
// nextTick, 等待 dom 更新后触发回调
|
||||||
export const useNextTick = (callback) => {
|
export const useNextTick = (callback) => {
|
||||||
|
@ -62,30 +35,142 @@ export const emitEvent = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const computed = (callback) => {
|
const watch = (valueFn, callback, options) => {
|
||||||
try {
|
createEffect(on(valueFn, callback, { defer: !options.immediate }))
|
||||||
return createMemo(callback)
|
|
||||||
} catch (error) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSetup = ({ props, renderless, extendOptions = { framework: 'Solid' } }) => {
|
export const t = (str) => str
|
||||||
|
|
||||||
|
const reactive = (state) => {
|
||||||
|
const proxy = createMutable(state)
|
||||||
|
// 暂时解决嵌套computed导致禁用问题,后期再完善
|
||||||
|
if (proxy.formDisabled) {
|
||||||
|
proxy.formDisabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proxy.disabled) {
|
||||||
|
proxy.disabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSetup = ({ props, renderless, extendOptions = { framework: 'Solid' }, constants }) => {
|
||||||
const render = typeof props.tiny_renderless === 'function' ? props.tiny_renderless : renderless
|
const render = typeof props.tiny_renderless === 'function' ? props.tiny_renderless : renderless
|
||||||
const utils = {
|
const utils = {
|
||||||
parent: {},
|
parent: {},
|
||||||
emit: emit(props)
|
emit: emit(props),
|
||||||
|
constants,
|
||||||
|
nextTick: useNextTick,
|
||||||
|
t,
|
||||||
|
mode: 'pc'
|
||||||
}
|
}
|
||||||
const sdk = render(
|
const sdk = render(
|
||||||
props,
|
props,
|
||||||
{ ...hooks, reactive, computed, useNextTick, inject: () => {}, watch: () => {}, onBeforeUnmount: onCleanup },
|
{
|
||||||
|
...hooks,
|
||||||
|
reactive,
|
||||||
|
computed: (callback) => {
|
||||||
|
const [computedValue, { mutate }] = createResource(() => {
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
|
return callback()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
createEffect(() => {
|
||||||
|
mutate(callback())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return computedValue
|
||||||
|
},
|
||||||
|
inject: () => { },
|
||||||
|
watch,
|
||||||
|
watchEffect: createEffect,
|
||||||
|
onMounted: onMount,
|
||||||
|
onBeforeUnmount: onCleanup
|
||||||
|
},
|
||||||
utils,
|
utils,
|
||||||
extendOptions
|
extendOptions
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...sdk,
|
...sdk,
|
||||||
state: sdk.state.solidState,
|
|
||||||
type: props.type ?? 'default'
|
type: props.type ?? 'default'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getType = (str, type = 'object') => {
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
'[object Number]': 'number',
|
||||||
|
'[object String]': 'string',
|
||||||
|
'[object Boolean]': 'boolean',
|
||||||
|
'[object Undefined]': 'undefined',
|
||||||
|
'[object Null]': 'null',
|
||||||
|
'[object Array]': 'array',
|
||||||
|
'[object Arguments]': 'arguments',
|
||||||
|
'[object Function]': 'function',
|
||||||
|
'[object Error]': 'error',
|
||||||
|
'[object Date]': 'date',
|
||||||
|
'[object RegExp]': 'regexp',
|
||||||
|
'[object Object]': 'object'
|
||||||
|
}[Object.prototype.toString.call(str)] === type
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getClassList = (cls) => {
|
||||||
|
if (getType(cls)) {
|
||||||
|
return cls
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(cls)) {
|
||||||
|
const classList = {}
|
||||||
|
|
||||||
|
cls.forEach((cls) => {
|
||||||
|
if (!cls) return
|
||||||
|
if (typeof cls === 'string') {
|
||||||
|
classList[cls] = true
|
||||||
|
} else if (typeof cls === 'object') {
|
||||||
|
if (Array.isArray(cls)) {
|
||||||
|
Object.assign(classList, getClassList(cls))
|
||||||
|
} else {
|
||||||
|
Object.assign(classList, cls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return classList
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
[cls]: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const capitalize = function (str) {
|
||||||
|
return typeof str === 'string' ? str.slice(0, 1).toUpperCase() + str.slice(1) : str
|
||||||
|
}
|
||||||
|
|
||||||
|
export const capitalizeKebabCase = function (str, splitChar = '-') {
|
||||||
|
return typeof str === 'string' ? str.split(splitChar).map(capitalize).join('') : str
|
||||||
|
}
|
||||||
|
|
||||||
|
export const resolveComponent = (component, useComponents) => {
|
||||||
|
if (typeof component === 'string' && component.indexOf('icon-') === 0) {
|
||||||
|
const componentName = capitalizeKebabCase(component)
|
||||||
|
return useComponents[componentName] || componentName
|
||||||
|
}
|
||||||
|
return component
|
||||||
|
}
|
||||||
|
|
||||||
|
export const $prefix = 'Tiny'
|
||||||
|
|
||||||
|
export const withModifiers = function (event, eventCallback, keys) {
|
||||||
|
if (typeof eventCallback === 'function') {
|
||||||
|
eventCallback(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { mergeProps, TransitionGroup, Transition }
|
||||||
|
|
|
@ -2,3 +2,4 @@ packages:
|
||||||
- packages/**
|
- packages/**
|
||||||
- examples/*
|
- examples/*
|
||||||
- internals/*
|
- internals/*
|
||||||
|
- solid-demo/*
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@opentiny/vue-autonavi-map": ["packages/vue/src/chart/autonavi-map"],
|
||||||
|
"@opentiny/vue-baidu-map": ["packages/vue/src/chart/baidu-map"],
|
||||||
|
"@opentiny/vue-chart-*": ["packages/vue/src/chart/chart-*"],
|
||||||
|
"@opentiny/vue-*": ["packages/vue-*", "packages/vue/src/*"],
|
||||||
|
"@opentiny/vue-renderless/types*": ["packages/renderless/types*"],
|
||||||
|
"@opentiny/vue-renderless*": ["packages/renderless/src*"],
|
||||||
|
"virtual:common/adapter/vue": ["packages/vue-common/src/adapter/vue3/index.ts"],
|
||||||
|
"virtual:locale/vue": ["packages/vue-locale/src/vue3/index.ts"]
|
||||||
|
},
|
||||||
|
"types": ["node", "vite/client"]
|
||||||
|
},
|
||||||
|
"vueCompilerOptions": {
|
||||||
|
"target": 3
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"packages/**/*.ts",
|
||||||
|
"packages/**/*.tsx",
|
||||||
|
"packages/**/*.vue",
|
||||||
|
"examples/vue3/shims-app.d.ts",
|
||||||
|
"examples/vue3/shims-vue.d.ts"
|
||||||
|
],
|
||||||
|
"exclude": ["**/node_modules", "**/dist*", "**/*.md"]
|
||||||
|
}
|
Loading…
Reference in New Issue