diff --git a/README.md b/README.md index 95e2957..1c7ca66 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ -# vite-react-template \ No newline at end of file +webcontainers 备注 + +核心还是要联网,否则无法使用,因为使用的请求stackblitz.com 的api + + + + + diff --git a/index.html b/index.html index e4b78ea..03054df 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,27 @@ - - - - - Vite + React + TS - - -
- - - + + + + + + Vite + React + TS + + + + +
+ + + + \ No newline at end of file diff --git a/package.json b/package.json index b11320f..bcdbea1 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "license": "MIT", "dependencies": { "@kevisual/router": "0.0.9", + "@webcontainer/api": "^1.5.3", "clsx": "^2.1.1", "dayjs": "^1.11.13", "lodash-es": "^4.17.21", @@ -34,6 +35,7 @@ "@types/node": "^22.13.13", "@types/react": "^19.0.12", "@types/react-dom": "^19.0.4", + "@vitejs/plugin-basic-ssl": "^2.0.0", "@vitejs/plugin-react": "^4.3.4", "tailwindcss": "^4.0.16", "typescript": "^5.8.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47f8705..820e33c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@kevisual/router': specifier: 0.0.9 version: 0.0.9 + '@webcontainer/api': + specifier: ^1.5.3 + version: 1.5.3 clsx: specifier: ^2.1.1 version: 2.1.1 @@ -57,6 +60,9 @@ importers: '@types/react-dom': specifier: ^19.0.4 version: 19.0.4(@types/react@19.0.12) + '@vitejs/plugin-basic-ssl': + specifier: ^2.0.0 + version: 2.0.0(vite@6.2.3(@types/node@22.13.13)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.5.1)) '@vitejs/plugin-react': specifier: ^4.3.4 version: 4.3.4(vite@6.2.3(@types/node@22.13.13)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.5.1)) @@ -557,12 +563,21 @@ packages: '@types/react@19.0.12': resolution: {integrity: sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==} + '@vitejs/plugin-basic-ssl@2.0.0': + resolution: {integrity: sha512-gc9Tjg8bUxBVSTzeWT3Njc0Cl3PakHFKdNfABnZWiUgbxqmHDEn7uECv3fHVylxoYgNzAcmU7ZrILz+BwSo3sA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + peerDependencies: + vite: ^6.0.0 + '@vitejs/plugin-react@4.3.4': resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@webcontainer/api@1.5.3': + resolution: {integrity: sha512-f6Oq3ohtSC5RYABhpN8aVOVHpcKvJ1fB1jjuvODTBU5u6BcroYEhphnrywdw8RO+2Vy5ekCdKe5D4dCMdMSrzA==} + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -1454,6 +1469,10 @@ snapshots: dependencies: csstype: 3.1.3 + '@vitejs/plugin-basic-ssl@2.0.0(vite@6.2.3(@types/node@22.13.13)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.5.1))': + dependencies: + vite: 6.2.3(@types/node@22.13.13)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.5.1) + '@vitejs/plugin-react@4.3.4(vite@6.2.3(@types/node@22.13.13)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.5.1))': dependencies: '@babel/core': 7.26.0 @@ -1465,6 +1484,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@webcontainer/api@1.5.3': {} + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 diff --git a/src/files/demo.ts b/src/files/demo.ts new file mode 100644 index 0000000..1b294e1 --- /dev/null +++ b/src/files/demo.ts @@ -0,0 +1,63 @@ +/** @type {import('@webcontainer/api').FileSystemTree} */ +import { WebContainer } from '@webcontainer/api'; + +// Call only once +const webcontainerInstance = await WebContainer.boot(); + +export const files = { + 'package.json': { + file: { + contents: ` + { + "name": "vite-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^4.0.4" + } + }`, + }, + }, + 'index.html': { + file: { + contents: ` + + + + + + + Vite App + + +
+ + + `, + }, + }, +}; + +await webcontainerInstance.mount(files); + +async function startDevServer() { + const installProcess = await webcontainerInstance.spawn('npm', ['install']); + + const installExitCode = await installProcess.exit; + + if (installExitCode !== 0) { + throw new Error('Unable to run npm install'); + } + + // `npm run dev` + await webcontainerInstance.spawn('npm', ['run', 'dev']); + // webcontainerInstance.on('server-ready', (port, url) => (iframeEl.src = url)); +} + +startDevServer(); diff --git a/src/files/files.ts b/src/files/files.ts new file mode 100644 index 0000000..8fb11cd --- /dev/null +++ b/src/files/files.ts @@ -0,0 +1,54 @@ +export const files = { + 'package.json': { + file: { + contents: ` + { + "name": "vite-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^4.0.4", + "tailwindcss": "^3.3.5" + } + }`, + }, + }, + 'a.js': { + file: { + contents: ` + console.log('Hello, world!'); + `, + }, + }, + 'style.css': { + file: { + contents: ` + @import 'tailwindcss'; + `, + }, + }, + 'index.html': { + file: { + contents: ` + + + + + + Vite App + + +
这是一个app的内容
+ + + `, + }, + }, +}; diff --git a/src/files/main.ts b/src/files/main.ts new file mode 100644 index 0000000..f4e4053 --- /dev/null +++ b/src/files/main.ts @@ -0,0 +1,74 @@ +import { WebContainer } from '@webcontainer/api'; +import { files } from './files'; + +/** @type {import('@webcontainer/api').WebContainer} */ +let webcontainerInstance; + +/** @type {HTMLIFrameElement | null} */ +const iframeEl = document.querySelector('iframe'); + +/** @type {HTMLTextAreaElement | null} */ +const textareaEl = document.querySelector('textarea'); + +window.addEventListener('load', async () => { + // textareaEl.value = files['index.js'].file.contents; + // textareaEl.addEventListener('input', (e) => { + // writeIndexJS(e.currentTarget.value); + // }); + const hasLoaded = document.querySelector('.container') !== null; + if (hasLoaded) { + return; + } + console.log('@hasLoaded', document.querySelector('.container'), hasLoaded); + document.querySelector('#root')!.innerHTML = ` +
+
+ +
+
+ +
+
+`; + console.log('@root', document.querySelector('#root')); + console.log('@window.addEventListener', document.querySelector('#root')); + + // Call only once + webcontainerInstance = await WebContainer.boot(); + await webcontainerInstance.mount(files); + + const exitCode = await installDependencies(); + if (exitCode !== 0) { + throw new Error('Installation failed'); + } + console.log('Installation successful'); + startDevServer(); +}); + +async function installDependencies() { + // Install dependencies + const installProcess = await webcontainerInstance.spawn('npm', ['install']); + installProcess.output.pipeTo( + new WritableStream({ + write(data) { + console.log(data); + }, + }), + ); + // Wait for install command to exit + return installProcess.exit; +} + +async function startDevServer() { + // Run `npm run start` to start the Express app + await webcontainerInstance.spawn('npm', ['run', 'start']); + + // Wait for `server-ready` event + webcontainerInstance.on('server-ready', (port, url) => { + console.log('@server-ready', port, url); + const iframe = document.querySelector('iframe'); + if (iframe) { + iframe.src = url; + } + }); +} diff --git a/vite.config.ts b/vite.config.ts index c8db331..e23ed5b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,7 +3,7 @@ import react from '@vitejs/plugin-react'; import path from 'path'; import tailwindcss from '@tailwindcss/vite'; import pkgs from './package.json' with { type: 'json' }; - +import basicSsl from '@vitejs/plugin-basic-ssl'; const version = pkgs.version || '0.0.1'; const isDev = process.env.NODE_ENV === 'development'; @@ -12,7 +12,7 @@ const basename = isDev ? '/' : pkgs?.basename || '/'; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react(), tailwindcss()], + plugins: [react(), tailwindcss(), basicSsl() ], resolve: { alias: { '@': path.resolve(__dirname, './src'), @@ -28,8 +28,12 @@ export default defineConfig({ target: 'esnext', }, server: { - port: 6004, + port: 7004, host: '0.0.0.0', + headers: { + 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cross-Origin-Opener-Policy': 'same-origin', + }, proxy: { '/api': { target: 'http://localhost:3000',