Files
element-call-Github/vite.config.ts
fkwpandClaude Opus 5 6a6605c233 Let background effects run under the dev server
- nodePolyfills rewrote MediaPipe's WASM loader, which mentions `process`
- That loader is a classic script whose job is to set self.ModuleFactory;
  as an ES module it sets nothing, so the segmenter threw
  "ModuleFactory not set" and no effect could run under `pnpm dev`
- Its transform now skips that one directory
- Affects blur on main too, not only this feature: neither could be
  exercised locally before
- Production was never affected, since the loader is emitted as an asset
  there; build still green and the assets still emitted

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 23:01:54 +02:00

220 lines
6.7 KiB
TypeScript

/*
Copyright 2021-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import {
loadEnv,
type PluginOption,
searchForWorkspaceRoot,
type ConfigEnv,
type UserConfig,
} from "vite";
import svgrPlugin from "vite-plugin-svgr";
import { createHtmlPlugin } from "vite-plugin-html";
import { codecovVitePlugin } from "@codecov/vite-plugin";
import { sentryVitePlugin } from "@sentry/vite-plugin";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import wasm from "vite-plugin-wasm";
import babel from "@rolldown/plugin-babel";
import react, { reactCompilerPreset } from "@vitejs/plugin-react";
import { realpathSync } from "fs";
import * as fs from "node:fs";
/**
* Keeps a plugin's transform off MediaPipe's WASM loader.
*
* nodePolyfills rewrites any module mentioning `process`, and that loader does.
* It is a classic script whose whole job is to set `self.ModuleFactory`;
* rewritten as an ES module it sets nothing on `self`, and background effects
* then fail at runtime with "ModuleFactory not set". A production build is
* unaffected, because there the loader is emitted as a static asset and never
* passes through a transform, which is why this only bites in dev.
*/
function exceptMediaPipeWasm(plugins: PluginOption): PluginOption {
const isLoader = (id: string): boolean =>
id.includes("tasks-vision") && id.includes("wasm");
return (Array.isArray(plugins) ? plugins : [plugins]).map((plugin) => {
if (
!plugin ||
typeof plugin !== "object" ||
!("transform" in plugin) ||
typeof plugin.transform !== "function"
)
return plugin;
const transform = plugin.transform;
return {
...plugin,
transform(this: unknown, code: string, id: string, options: unknown) {
if (isLoader(id)) return null;
return (
transform as (
this: unknown,
c: string,
i: string,
o: unknown,
) => unknown
).call(this, code, id, options);
},
} as PluginOption;
});
}
export const vitePluginsConfig = ({
mode,
html = true,
}: Pick<ConfigEnv, "mode"> & {
/**
* Whether to inject Element Call's entry point into the HTML page. Builds
* that produce a library, or serve a page of their own, must not have this:
* it would pull the standalone app in alongside whatever they are building.
*/
html?: boolean;
}): UserConfig => {
const env = loadEnv(mode, process.cwd());
const plugins: PluginOption[] = [
babel({
presets: [reactCompilerPreset()],
}),
react(),
wasm(),
exceptMediaPipeWasm(
nodePolyfills({
// Enables the 'events' module, which is required by the matrix-js-sdk
include: ["events"],
}),
),
svgrPlugin({
svgrOptions: {
// This enables ref forwarding on SVGR components, which is needed, for
// example, to make tooltips on icons work
ref: true,
},
}),
codecovVitePlugin({
enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined,
bundleName: "element-call",
uploadToken: process.env.CODECOV_TOKEN,
}),
];
if (
process.env.SENTRY_ORG &&
process.env.SENTRY_PROJECT &&
process.env.SENTRY_AUTH_TOKEN &&
process.env.SENTRY_URL
) {
plugins.push(
sentryVitePlugin({
release: {
name: process.env.VITE_APP_VERSION,
},
}),
);
}
if (html && !process.env.STORYBOOK && !process.env.VITEST) {
plugins.push(
createHtmlPlugin({
entry: "src/main.tsx",
inject: {
data: {
brand: env.VITE_PRODUCT_NAME || "Element Call",
packageType: process.env.VITE_PACKAGE,
},
},
}),
);
}
return { plugins };
};
// https://vitejs.dev/config/
// Modified type helper from defineConfig to allow for packageType (see defineConfig from vite)
export default ({
mode,
packageType,
}: ConfigEnv & { packageType?: "full" | "embedded" }): UserConfig => {
// Environment variables with the VITE_ prefix are accessible at runtime.
// So, we set this to allow for build/package specific behavior.
// In future we might be able to do what is needed via code splitting at
// build time.
process.env.VITE_PACKAGE = packageType ?? "full";
// The crypto WASM module is imported dynamically. Since it's common
// for developers to use a linked copy of matrix-js-sdk or Rust
// crypto (which could reside anywhere on their file system), Vite
// needs to be told to recognize it as a legitimate file access.
const allow = [searchForWorkspaceRoot(process.cwd())];
for (const path of [
"node_modules/matrix-js-sdk/node_modules/@matrix-org/matrix-sdk-crypto-wasm",
"node_modules/@matrix-org/matrix-sdk-crypto-wasm",
]) {
try {
allow.push(realpathSync(path));
} catch {}
}
console.log("Allowed vite paths:", allow);
return {
...vitePluginsConfig({ mode }),
server: {
host: true,
port: 3000,
fs: { allow },
https: {
key: fs.readFileSync("./backend/dev_tls_m.localhost.key"),
cert: fs.readFileSync("./backend/dev_tls_m.localhost.crt"),
},
},
worker: {
format: "es",
},
build: {
minify: mode === "production" ? true : false,
sourcemap: true,
rollupOptions: {
output: {
assetFileNames: ({ originalFileNames }): string => {
if (originalFileNames) {
for (const name of originalFileNames) {
// Custom asset name for locales to include the locale code in the filename
const match = name.match(/locales\/([^/]+)\/(.+)\.json$/);
if (match) {
const [, locale, filename] = match;
return `assets/${locale}-${filename}-[hash].json`;
}
}
}
// Default naming fallback
return "assets/[name]-[hash][extname]";
},
},
},
},
resolve: {
alias: {
// matrix-widget-api has its transpiled lib/index.js as its entry point,
// which Vite for some reason refuses to work with, so we point it to
// src/index.ts instead
"matrix-widget-api": "matrix-widget-api/src/index.ts",
},
dedupe: [
"react",
"react-dom",
"matrix-js-sdk",
"react-use-measure",
// These packages modify the document based on some module-level global
// state, and don't play nicely with duplicate copies of themselves
// https://github.com/radix-ui/primitives/issues/1241#issuecomment-1847837850
"@radix-ui/react-focus-guards",
"@radix-ui/react-dismissable-layer",
],
},
};
};