From fc66d2e1b360abf1a943278d3679b4a790c44adb Mon Sep 17 00:00:00 2001 From: Danny McGee Date: Fri, 29 Sep 2023 18:19:22 -0400 Subject: [PATCH] Share assets between Bevy app and web frontend --- .../vite-plugins/multiple-assets/build.ts | 25 ++++++ .../vite-plugins/multiple-assets/index.ts | 23 +++++ .../vite-plugins/multiple-assets/server.ts | 85 +++++++++++++++++++ hello-web/vite.config.ts | 7 ++ hello-world/project.json | 8 +- hello-world/scripts/copy-assets.mjs | 13 +++ 6 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 hello-web/vite-plugins/multiple-assets/build.ts create mode 100644 hello-web/vite-plugins/multiple-assets/index.ts create mode 100644 hello-web/vite-plugins/multiple-assets/server.ts create mode 100644 hello-world/scripts/copy-assets.mjs diff --git a/hello-web/vite-plugins/multiple-assets/build.ts b/hello-web/vite-plugins/multiple-assets/build.ts new file mode 100644 index 0000000..b478c9d --- /dev/null +++ b/hello-web/vite-plugins/multiple-assets/build.ts @@ -0,0 +1,25 @@ +import fs from "fs"; +import {NormalizedOutputOptions} from "rollup"; + +function copyRecursively(src: string, dst: string) { + if (fs.statSync(src).isDirectory()) { + // folder + if (!fs.existsSync(dst)) { + fs.mkdirSync(dst); + } + for (const file of fs.readdirSync(src)) { + copyRecursively(src + "/" + file, dst + "/" + file); + } + } else { + // file + fs.copyFileSync(src, dst); + } +} + +export function buildMiddleWare(options: NormalizedOutputOptions, assets: string[] = []) { + const dst = options.dir; + if (!dst) throw new Error("options.dir is undefined"); + for (const dir of assets) { + copyRecursively(dir, dst) + } +} diff --git a/hello-web/vite-plugins/multiple-assets/index.ts b/hello-web/vite-plugins/multiple-assets/index.ts new file mode 100644 index 0000000..066c88a --- /dev/null +++ b/hello-web/vite-plugins/multiple-assets/index.ts @@ -0,0 +1,23 @@ +// NOTE: This is an inline fork of this npm package: +// https://github.com/nguyenbatranvan/vite-multiple-assets +// +// The published version at time of writing doesn't include a mime type for +// WGSL, which is fixed in this copy. This module can be removed and replaced +// with the npm dependency if/when this PR is accepted: +// https://github.com/nguyenbatranvan/vite-multiple-assets/pull/5 + +import {PluginOption, ViteDevServer} from "vite"; +import {ServerMiddleWare} from "./server"; +import {buildMiddleWare} from "./build"; + +export default function DynamicPublicDirectory(assets: string[]): PluginOption { + return { + configureServer(server: ViteDevServer) { + return ServerMiddleWare(server, assets) + }, + async writeBundle(options) { + buildMiddleWare(options, assets) + }, + name: "dynamic assets" + }; +} diff --git a/hello-web/vite-plugins/multiple-assets/server.ts b/hello-web/vite-plugins/multiple-assets/server.ts new file mode 100644 index 0000000..6733308 --- /dev/null +++ b/hello-web/vite-plugins/multiple-assets/server.ts @@ -0,0 +1,85 @@ +import fs from "fs"; +import path from "path"; +import { ViteDevServer } from "vite"; + +interface IPropsFile { + name: string; + files: string[]; +} + +function getFiles(dir, files_): string[] { + files_ = files_ || []; + const files = fs.readdirSync(dir); + for (const i in files) { + const name = dir + "/" + files[i]; + if (fs.statSync(name).isDirectory()) { + getFiles(name, files_); + } else { + files_.push(name); + } + } + return files_; +} + +const mimeTypes = { + ".html": "text/html", + ".js": "text/javascript", + ".css": "text/css", + ".png": "image/png", + ".jpg": "image/jpeg", + ".gif": "image/gif", + ".svg": "image/svg+xml", + ".json": "application/json", + ".woff": "font/woff", + ".woff2": "font/woff2", + ".ttf": "font/ttf", + ".eot": "font/eot", + ".otf": "font/otf", + ".wasm": "application/wasm", + ".mjs": "text/javascript", + ".txt": "text/plain", + ".xml": "text/xml", + ".mp3": "audio/mpeg", + ".mp4": "video/mp4", + ".webm": "video/webm", + ".webp": "image/webp", + ".wgsl": "text/wgsl", + ".ico": "image/x-icon", + ".tiff": "image/tiff", + ".gz": "application/gzip", + ".zip": "application/zip", + ".rar": "application/x-rar-compressed", + ".7z": "application/x-7z-compressed", +} + +export function ServerMiddleWare(server: ViteDevServer, assets: string[] = []) { + if (!assets || !assets.length) + return; + + const fileObject: IPropsFile[] = []; + for (let i = 0; i < assets.length; i++) { + const files = getFiles(path.join(process.cwd(), `/${assets[i]}`), []); + fileObject.push({ + name: assets[i], + files + }); + } + + return () => { + server.middlewares.use(async (req, res, next) => { + for (let i = 0; i < fileObject.length; i++) { + const file = path.join(process.cwd(), `${fileObject[i].name}/${req.originalUrl}`); + if (fileObject[i].files.some(f => path.relative(f, file) === "")) { + const extension = file.substring(file.lastIndexOf(".")); + res.setHeader("Cache-Control", "max-age=31536000, immutable"); + res.setHeader("Content-Type", mimeTypes[extension]); + res.writeHead(200); + res.write(fs.readFileSync(path.join(process.cwd(), "/" + fileObject[i].name + req.originalUrl))); + res.end(); + break; + } + } + next(); + }); + }; +} diff --git a/hello-web/vite.config.ts b/hello-web/vite.config.ts index 3ed490f..b6c1f63 100644 --- a/hello-web/vite.config.ts +++ b/hello-web/vite.config.ts @@ -1,10 +1,13 @@ /// import { defineConfig } from "vite"; + import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin"; import wasm from "vite-plugin-wasm"; import topLevelAwait from "vite-plugin-top-level-await"; +import mergeAssetsDirs from "./vite-plugins/multiple-assets"; + export default defineConfig({ cacheDir: "../node_modules/.vite/hello-web", server:{ @@ -19,5 +22,9 @@ export default defineConfig({ nxViteTsPaths(), wasm(), topLevelAwait(), + mergeAssetsDirs([ + "hello-web/public", + "dist/hello-world/public", + ]), ], }); diff --git a/hello-world/project.json b/hello-world/project.json index c3e6a6a..8e9a846 100644 --- a/hello-world/project.json +++ b/hello-world/project.json @@ -18,9 +18,13 @@ "build:web": { "executor": "nx:run-commands", "options": { - "command": "wasm-pack build --out-dir ../dist/hello-world/ -- --features wasm", + "commands": [ + "wasm-pack build --out-dir ../dist/hello-world/ -- --features wasm", + "node scripts/copy-assets.mjs" + ], "cwd": "hello-world", - "env": "hello-world/.env" + "envFile": "hello-world/.env", + "parallel": false } }, "run": { diff --git a/hello-world/scripts/copy-assets.mjs b/hello-world/scripts/copy-assets.mjs new file mode 100644 index 0000000..fc7ecf2 --- /dev/null +++ b/hello-world/scripts/copy-assets.mjs @@ -0,0 +1,13 @@ +import { promises as fs } from "node:fs"; +import path from "node:path"; +import url from "node:url"; + +async function main() { + const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); + const assetsSrc = path.resolve(__dirname, "../assets"); + const assetsDest = path.resolve(__dirname, "../../dist/hello-world/public/assets"); + + await fs.cp(assetsSrc, assetsDest, { recursive: true }); +} + +main();