Add PUBLIC_DIR env var to merge external public files at build time
Files in the external directory take precedence over the built-in public/ directory. Works in both dev (middleware) and build (post-copy) modes. Also fix missing magicMouse property in TestApp.
This commit is contained in:
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import type { Plugin } from "vite";
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
import { DittoConfigSchema } from "./src/lib/schemas";
|
||||
@@ -27,7 +28,70 @@ function loadDittoConfig(): object | undefined {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all files from `src` into `dest`, overwriting existing files.
|
||||
* Recursively handles subdirectories.
|
||||
*/
|
||||
function copyDirSync(src: string, dest: string): void {
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
||||
const srcPath = path.join(src, entry.name);
|
||||
const destPath = path.join(dest, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
copyDirSync(srcPath, destPath);
|
||||
} else {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vite plugin that merges an external public directory on top of the default one.
|
||||
* Set the PUBLIC_DIR env var to a directory path. Files in that directory take
|
||||
* precedence over files in the built-in `public/` directory.
|
||||
*
|
||||
* - In build mode, files are copied into the output after the default public dir.
|
||||
* - In dev mode, the external directory is served with higher priority.
|
||||
*/
|
||||
function mergePublicDir(externalDir: string): Plugin {
|
||||
const resolved = path.resolve(externalDir);
|
||||
|
||||
return {
|
||||
name: "ditto:merge-public-dir",
|
||||
|
||||
configureServer(server) {
|
||||
// Serve files from the external public dir before the default public dir.
|
||||
server.middlewares.use((req, res, next) => {
|
||||
if (!req.url) return next();
|
||||
|
||||
const urlPath = decodeURIComponent(new URL(req.url, "http://localhost").pathname);
|
||||
const filePath = path.join(resolved, urlPath);
|
||||
|
||||
try {
|
||||
const stat = fs.statSync(filePath);
|
||||
if (stat.isFile()) {
|
||||
// Let Vite's static middleware handle it by pointing to the file.
|
||||
const stream = fs.createReadStream(filePath);
|
||||
stream.pipe(res);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// File not found in external dir — fall through to default public dir
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
},
|
||||
|
||||
writeBundle(options) {
|
||||
const outDir = options.dir ?? path.resolve("dist");
|
||||
copyDirSync(resolved, outDir);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const dittoConfig = loadDittoConfig();
|
||||
const publicDir = process.env.PUBLIC_DIR;
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(() => ({
|
||||
@@ -37,6 +101,7 @@ export default defineConfig(() => ({
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
...(publicDir ? [mergePublicDir(publicDir)] : []),
|
||||
],
|
||||
define: {
|
||||
__DITTO_CONFIG__: JSON.stringify(dittoConfig ?? null),
|
||||
|
||||
Reference in New Issue
Block a user