From ad3fe496ef4d36fcf5cae14f2ab8b13f1de1cb3f Mon Sep 17 00:00:00 2001 From: xion Date: Tue, 10 Dec 2024 01:49:36 +0800 Subject: [PATCH] add demo --- package.json | 3 +- src/app.ts | 1 - src/main.tsx | 35 ++++++++++-------- src/routes/ai.ts | 7 ++++ src/routes/home.ts | 92 ++++++++++++++++++++++++++++++++++++++++++++++ src/routes/list.ts | 13 +++++++ vite.config.ts | 24 ++++++++++-- 7 files changed, 153 insertions(+), 22 deletions(-) create mode 100644 src/routes/ai.ts create mode 100644 src/routes/home.ts create mode 100644 src/routes/list.ts diff --git a/package.json b/package.json index 7eeee2d..4876de4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "browser-apps", - "version": "0.0.1", + "version": "0.0.2", "description": "", "main": "index.js", "scripts": { @@ -29,6 +29,7 @@ }, "devDependencies": { "@build/tailwind": "1.0.2-alpha-2", + "@kevisual/types": "^0.0.3", "@types/lodash-es": "^4.17.12", "@types/react": "^19.0.1", "@types/umami": "^2.10.0", diff --git a/src/app.ts b/src/app.ts index 0ae70f9..2061544 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,6 +1,5 @@ import { QueryRouterServer } from '@kevisual/router/browser'; import { useContextKey } from '@kevisual/store/config'; -import { useConfigKey } from '@kevisual/store/config'; import { EventEmitter } from 'eventemitter3'; export const app = useContextKey('app', () => { diff --git a/src/main.tsx b/src/main.tsx index 8220d7d..4ecaa17 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,33 +8,36 @@ import './page/index.css'; import './main.css'; import { Page } from '@kevisual/store/page'; -import { AiChat } from './page/AiChat'; +import { useContextKey } from '@kevisual/store/config'; const main = () => { start(); }; main(); -const page = new Page({ - path: '', - key: '', - basename: '', +const page = useContextKey('page', () => { + return new Page({ + path: '', + key: '', + basename: '', + }); }); page.addPage('/', 'home'); page.addPage('/ai', 'ai'); +page.addPage('/list', 'list'); -page.subscribe('home', (page) => { - const div = document.createElement('div'); - - div.className = 'text-4xl text-center p-4 bg-blue-200'; - div.innerHTML = 'Browser Apps'; - - document.body.appendChild(div); +page.subscribe('home', async (page) => { + const module = await import('./routes/home.ts'); + module?.route(page); }); -page.subscribe('ai', (page) => { - const aiChat = AiChat(); - console.log(aiChat); - document.body.appendChild(aiChat); +page.subscribe('ai', async (page) => { + const module = await import('./routes/ai.ts'); + module?.route(); +}); + +page.subscribe('list', async (page) => { + const module = await import('./routes/list.ts'); + module?.route(); }); diff --git a/src/routes/ai.ts b/src/routes/ai.ts new file mode 100644 index 0000000..07f54ff --- /dev/null +++ b/src/routes/ai.ts @@ -0,0 +1,7 @@ +import { AiChat } from '../page/AiChat'; + +export const route = () => { + const aiChat = AiChat(); + console.log(aiChat); + document.body.appendChild(aiChat); +}; diff --git a/src/routes/home.ts b/src/routes/home.ts new file mode 100644 index 0000000..2da9f85 --- /dev/null +++ b/src/routes/home.ts @@ -0,0 +1,92 @@ +import { QueryRouterServer } from '@kevisual/router/browser'; +import { useContextKey } from '@kevisual/store/config'; +type InitFn = () => any; +type InitModule = { + init?: boolean; + loaded?: boolean; + fn?: InitFn; + key?: string; +}; +class UseInit { + modules = new Map(); + callbacks = new Map(); + constructor() {} + async run(fn: InitFn, key: string, force = false) { + if (this.modules.has(key)) { + const module = this.modules.get(key)!; + if (module.init) { + if (force) { + await module.fn?.(); + } + return; + } + await module.fn?.(); + module.init = true; + } else { + this.modules.set(key, { + init: true, + fn, + key, + }); + await fn(); + } + } +} +const module = useContextKey('module', () => { + return new UseInit(); +}); +const app = useContextKey('app'); + +const loadApps = async () => { + app + .route({ + path: 'dev', + key: 'preview-container', + }) + .define(async (ctx) => { + const data = ctx.query?.data || {}; + const code = data.code || ''; + if (!code) { + ctx.throw?.(400, 'code is required'); + } + const url = URL.createObjectURL(new Blob([code], { type: 'application/javascript' })); + console.log('dev route'); + const module = await import(url); + URL.revokeObjectURL(url); + if (module?.render) { + module.render(); + } + }) + .addTo(app); +}; +export const route = async (page?: any) => { + console.log('home route page', page); + console.log('home route app', app); + const div = document.createElement('div'); + + div.className = 'text-4xl text-center p-4 bg-blue-200'; + div.innerHTML = 'Browser Apps'; + div.onclick = () => { + location.href = '/list'; + }; + document.body.appendChild(div); + module.run(() => { + loadApps(); + }, 'home'); + const res = await app.call({ + path: 'dev', + key: 'preview-container', + payload: { + data: { + code: ` + export const render = () => { + const div = document.createElement('div'); + div.className = 'text-4xl text-center p-4 bg-blue-200'; + div.innerHTML = 'Browser Apps Preview'; + document.body.appendChild(div); + }`, + }, + }, + }); + console.log('res', res); +}; diff --git a/src/routes/list.ts b/src/routes/list.ts new file mode 100644 index 0000000..3d6c54f --- /dev/null +++ b/src/routes/list.ts @@ -0,0 +1,13 @@ +import { useContextKey } from '@kevisual/store/config'; +import { Page } from '@kevisual/store/page'; + +export const route = () => { + console.log('list route'); + const div = document.createElement('div'); + div.className = 'text-4xl text-center p-4 bg-blue-200'; + div.innerHTML = 'Browser Apps List'; + div.onclick = () => { + location.href = '/'; + }; + document.body.appendChild(div); +}; diff --git a/vite.config.ts b/vite.config.ts index 5c6564c..678625c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,10 +4,12 @@ import path from 'path'; import tailwindcss from 'tailwindcss'; import autoprefixer from 'autoprefixer'; import nesting from 'tailwindcss/nesting'; - +import pkgs from './package.json' with { type: 'json' }; +const version = pkgs.version || '0.0.1'; +const isDev = process.env.NODE_ENV === 'development'; +console.log('isDev', isDev); export default defineConfig({ root: '.', - // plugins: [react()], css: { postcss: { // @ts-ignore @@ -26,15 +28,29 @@ export default defineConfig({ // '@kevisual/router/browser': 'https://kevisual.xiongxiao.me/system/lib/router-browser.js', // 将本地路径替换为远程 URL }, }, + base: './', build: { minify: false, outDir: './dist', rollupOptions: { - external: ['react', 'react-dom'], + external: ['react'], + output: { + chunkFileNames: (chunkInfo) => { + if(isDev) { + return '[name].js'; + } + if (chunkInfo.facadeModuleId && chunkInfo.facadeModuleId.includes('/routes/')) { + // 对以 routes/ 开头的代码分块使用不带 hash 的命名 + return `routes/[name]-${version}.js`; + } + // 其他代码分块继续使用 hash + return '[name].[hash].js'; + }, + }, }, }, optimizeDeps: { - exclude: ['react', 'react-dom'], // 排除 react 和 react-dom 以避免打包 + exclude: ['react'], // 排除 react 和 react-dom 以避免打包 }, esbuild: { jsxFactory: 'h',