feat: 动态组件
This commit is contained in:
		
							parent
							
								
									49f9a0a4d7
								
							
						
					
					
						commit
						907a80c914
					
				|  | @ -0,0 +1,24 @@ | ||||||
|  | import { spawn } from 'child_process' | ||||||
|  | const argv = [...process.argv].splice(2) | ||||||
|  | const argvs = argv.join(' ').replace(/(\S+\.js|\S+\.ts)/g, '') | ||||||
|  | const child1 = spawn( | ||||||
|  |   'tailwindcss -i ./src/input.css -o ./public/output.css --watch', | ||||||
|  |   [], | ||||||
|  |   { | ||||||
|  |     shell: true, | ||||||
|  |     stdio: 'inherit' | ||||||
|  |   } | ||||||
|  | ) | ||||||
|  | const child2 = spawn( | ||||||
|  |   'node --no-warnings=ExperimentalWarning --loader ts-node/esm image/main.ts', | ||||||
|  |   argvs.split(' '), | ||||||
|  |   { | ||||||
|  |     shell: true, | ||||||
|  |     stdio: 'inherit' | ||||||
|  |   } | ||||||
|  | ) | ||||||
|  | process.on('SIGINT', () => { | ||||||
|  |   if (child1.pid) process.kill(child1.pid) | ||||||
|  |   if (child2.pid) process.kill(child2.pid) | ||||||
|  |   if (process.pid) process.exit() | ||||||
|  | }) | ||||||
|  | @ -2,12 +2,11 @@ import '../src/init/require.js' | ||||||
| import '../src/init/config.js' | import '../src/init/config.js' | ||||||
| import '../src/init/logger.js' | import '../src/init/logger.js' | ||||||
| import '../src/init/redis.js' | import '../src/init/redis.js' | ||||||
| import './tailwindcss.js' |  | ||||||
| import Koa from 'koa' | import Koa from 'koa' | ||||||
| import KoaStatic from 'koa-static' | import KoaStatic from 'koa-static' | ||||||
| import Router from 'koa-router' | import Router from 'koa-router' | ||||||
| import { Component } from 'yunzai/utils' | import { Component } from 'yunzai/utils' | ||||||
| import { readdirSync } from 'fs' | import { Dirent, readdirSync } from 'fs' | ||||||
| import { join } from 'path' | import { join } from 'path' | ||||||
| import mount from 'koa-mount' | import mount from 'koa-mount' | ||||||
| 
 | 
 | ||||||
|  | @ -17,63 +16,88 @@ const router = new Router() | ||||||
| const Port = 8080 | const Port = 8080 | ||||||
| const PATH = process.cwd() | const PATH = process.cwd() | ||||||
| 
 | 
 | ||||||
|  | const Dynamic = async (Router: Dirent) => { | ||||||
|  |   const modulePath = `file://${join(Router.parentPath, Router.name)}?update=${Date.now()}` | ||||||
|  |   return (await import(modulePath))?.default | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // 得到plugins目录
 | // 得到plugins目录
 | ||||||
| const flies = readdirSync(join(process.cwd(), 'plugins'), { | const flies = readdirSync(join(process.cwd(), 'plugins'), { | ||||||
|   withFileTypes: true |   withFileTypes: true | ||||||
| }).filter(flie => !flie.isFile()) | }) | ||||||
|  |   .filter(flie => !flie.isFile()) | ||||||
|  |   .map(flie => { | ||||||
|  |     const dir = flie?.path ?? flie?.parentPath | ||||||
|  |     flie.parentPath = dir | ||||||
|  |     return flie | ||||||
|  |   }) // 增加兼容性
 | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | const Routers = [] | ||||||
| 
 | 
 | ||||||
| // 解析路由
 | // 解析路由
 | ||||||
| for (const flie of flies) { | for (const flie of flies) { | ||||||
|   const dir = flie?.path ?? flie?.parentPath |   const plugins = readdirSync(join(flie?.parentPath, flie.name), { | ||||||
|   if (!dir) { |  | ||||||
|     console.log('flie.name', flie.name, '识别错误') |  | ||||||
|     continue |  | ||||||
|   } |  | ||||||
|   /** |  | ||||||
|    * |  | ||||||
|    */ |  | ||||||
|   const plugins = readdirSync(join(dir, flie.name), { |  | ||||||
|     withFileTypes: true |     withFileTypes: true | ||||||
|   }).filter(flie => flie.isFile()) |   }) | ||||||
|  |     .filter( | ||||||
|  |       flie => flie.isFile() && /^(routes.jsx|routes.tsx)$/.test(flie.name) | ||||||
|  |     ) | ||||||
|  |     .map(flie => { | ||||||
|  |       const dir = flie?.path ?? flie?.parentPath | ||||||
|  |       flie.parentPath = dir | ||||||
|  |       return flie | ||||||
|  |     }) // 增加兼容性
 | ||||||
|  | 
 | ||||||
|  |   //
 | ||||||
|   for (const plugin of plugins) { |   for (const plugin of plugins) { | ||||||
|     /** |     const routes = await Dynamic(plugin) | ||||||
|      * |     // 不存在
 | ||||||
|      */ |  | ||||||
|     if (/^(routes.jsx|routes.tsx)$/.test(plugin.name)) { |  | ||||||
|       const routes = (await import(`file://${join(plugin.path, plugin.name)}`)) |  | ||||||
|         ?.default |  | ||||||
|     if (!routes) continue |     if (!routes) continue | ||||||
|       /** |     // 不是数组
 | ||||||
|        * |     if (!Array.isArray(routes)) continue | ||||||
|        */ |     //
 | ||||||
|       if (Array.isArray(routes)) { |  | ||||||
|         /** |  | ||||||
|          * |  | ||||||
|          */ |  | ||||||
|     for (const item of routes) { |     for (const item of routes) { | ||||||
|       const url = `/${flie.name}${item.url}` |       const url = `/${flie.name}${item.url}` | ||||||
|       console.log(`http://127.0.0.1:${Port}${url}`) |       console.log(`http://127.0.0.1:${Port}${url}`) | ||||||
|  |       Routers.push({ | ||||||
|  |         parentPath: plugin.parentPath, | ||||||
|  |         name: plugin.name, | ||||||
|  |         uri: url, | ||||||
|  |         url: item.url | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | for (const Router of Routers) { | ||||||
|  |   router.get(Router.uri, async ctx => { | ||||||
|  |     // 动态加载
 | ||||||
|  |     const routes = await Dynamic(Router) | ||||||
|  |     // 不存在
 | ||||||
|  |     if (!routes) return | ||||||
|  |     // 不是数组
 | ||||||
|  |     if (!Array.isArray(routes)) return | ||||||
|  |     // 查找
 | ||||||
|  |     const item = routes.find(i => i.url == Router.url) | ||||||
|  |     // 丢失了
 | ||||||
|  |     if (!item) return | ||||||
|     /** |     /** | ||||||
|            * 推送接口 |      * 渲染html | ||||||
|      */ |      */ | ||||||
|           router.get(url, ctx => { |  | ||||||
|     const options = item?.options ?? {} |     const options = item?.options ?? {} | ||||||
|     const HTML = Com.create(item.element, { |     const HTML = Com.create(item.element, { | ||||||
|       ...options, |       ...options, | ||||||
|               html_head: options?.html_head ?? '', |  | ||||||
|       file_create: false |       file_create: false | ||||||
|     }) |     }) | ||||||
|     // 转义路径中的所有反斜杠
 |     // 转义路径中的所有反斜杠
 | ||||||
|     const escapedPath = PATH.replace(/\\/g, '\\\\') |     const escapedPath = PATH.replace(/\\/g, '\\\\') | ||||||
|             // 创建一个正则表达式,'g' 表示全局匹配
 |     // 创建一个正则表达式
 | ||||||
|     const regex = new RegExp(escapedPath, 'g') |     const regex = new RegExp(escapedPath, 'g') | ||||||
|  |     // 置换为file请求
 | ||||||
|     ctx.body = HTML.replace(regex, '/file') |     ctx.body = HTML.replace(regex, '/file') | ||||||
|   }) |   }) | ||||||
| } | } | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // static
 | // static
 | ||||||
| app.use(mount('/file', KoaStatic(PATH))) | app.use(mount('/file', KoaStatic(PATH))) | ||||||
|  | @ -83,6 +107,11 @@ app.use(router.routes()) | ||||||
| 
 | 
 | ||||||
| // listen 8000
 | // listen 8000
 | ||||||
| app.listen(Port, () => { | app.listen(Port, () => { | ||||||
|  |   console.log('______________') | ||||||
|   console.log('Server is running on port ' + Port) |   console.log('Server is running on port ' + Port) | ||||||
|   console.log('默认浏览器尺寸 800 X 1280 100%') |   console.log('______________') | ||||||
|  |   console.log('自行调整默认浏览器尺寸 800 X 1280 100%') | ||||||
|  |   console.log('如果需要运行时重新计算className') | ||||||
|  |   console.log('请确保一直打开此程序') | ||||||
|  |   console.log('______________') | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| { |  | ||||||
|   "watch": ["plugins"], |  | ||||||
|   "ext": "tsx,jsx", |  | ||||||
|   "exec": "node --no-warnings=ExperimentalWarning --loader ts-node/esm image/main.ts", |  | ||||||
|   "env": { |  | ||||||
|     "NODE_ENV": "development" |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,25 +0,0 @@ | ||||||
| import { spawn } from 'child_process' |  | ||||||
| /** |  | ||||||
|  * ********** |  | ||||||
|  * 生成css文件 |  | ||||||
|  * ********** |  | ||||||
|  */ |  | ||||||
| // exec('')
 |  | ||||||
| 
 |  | ||||||
| const child = spawn( |  | ||||||
|   'tailwindcss -i ./src/input.css -o ./public/output.css --watch', |  | ||||||
|   [], |  | ||||||
|   { |  | ||||||
|     shell: true, |  | ||||||
|     stdio: 'inherit' |  | ||||||
|   } |  | ||||||
| ) |  | ||||||
| /** |  | ||||||
|  * ************* |  | ||||||
|  * exit |  | ||||||
|  * ************* |  | ||||||
|  */ |  | ||||||
| process.on('SIGINT', () => { |  | ||||||
|   if (child.pid) process.kill(child.pid) |  | ||||||
|   if (process.pid) process.exit() |  | ||||||
| }) |  | ||||||
|  | @ -16,12 +16,13 @@ | ||||||
|     "logs": "pm2 logs", |     "logs": "pm2 logs", | ||||||
|     "monit": "pm2 monit", |     "monit": "pm2 monit", | ||||||
|     "pm2": "pm2", |     "pm2": "pm2", | ||||||
|     "image": "nodemon --config image/nodemon.json", |     "image": "node image/index.js", | ||||||
|     "css": "tailwindcss -i ./src/input.css -o ./public/output.css", |     "css": "tailwindcss -i ./src/input.css -o ./public/output.css", | ||||||
|     "format": "prettier --write .", |     "format": "prettier --write .", | ||||||
|     "prepare": "husky" |     "prepare": "husky" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "@loadable/component": "^5.16.4", | ||||||
|     "art-template": "^4.13.2", |     "art-template": "^4.13.2", | ||||||
|     "chalk": "^5.3.0", |     "chalk": "^5.3.0", | ||||||
|     "chokidar": "^3.6.0", |     "chokidar": "^3.6.0", | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| import { createRequire as cRequire } from 'module' | import { createRequire as cRequire } from 'module' | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * @deprecated 已废弃 |  * @deprecated 已废弃 | ||||||
|  * @param basePath |  * @param basePath | ||||||
|  | @ -6,6 +7,7 @@ import { createRequire as cRequire } from 'module' | ||||||
| export function createRequire(basePath: string) { | export function createRequire(basePath: string) { | ||||||
|   return cRequire(basePath) |   return cRequire(basePath) | ||||||
| } | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * @deprecated 已废弃 |  * @deprecated 已废弃 | ||||||
|  * @param path |  * @param path | ||||||
|  | @ -16,3 +18,23 @@ export function require(path: string) { | ||||||
|     return cRequire(url)(path) |     return cRequire(url)(path) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | const now = () => `?update=${Date.now()}` | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * *********** | ||||||
|  |  * 创建动态模块 | ||||||
|  |  * *********** | ||||||
|  |  * 动态模块每次访问都将重新加载, | ||||||
|  |  * 如果动态模块内包含动态模块, | ||||||
|  |  * 内部模块也会跟着重新加载, | ||||||
|  |  * *********** | ||||||
|  |  * 请确保你的模块是可预测 | ||||||
|  |  * *********** | ||||||
|  |  * 请确保当前模块是可被执行的 | ||||||
|  |  * @param basePath | ||||||
|  |  * @returns | ||||||
|  |  */ | ||||||
|  | export const createDynamic = (basePath: string) => { | ||||||
|  |   return (path: string) => import(new URL(`${path}${now()}`, basePath).href) | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue