2026-06-16 06:41:37 +02:00
|
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
2026-01-01 20:42:59 +00:00
|
|
|
|
2026-06-16 06:41:37 +02:00
|
|
|
import {existsSync, mkdirSync, readdirSync, writeFileSync} from 'node:fs';
|
2026-01-01 20:42:59 +00:00
|
|
|
import path, {dirname} from 'node:path';
|
|
|
|
|
import {fileURLToPath} from 'node:url';
|
2026-08-24 12:26:55 +02:00
|
|
|
import {
|
|
|
|
|
CopyRspackPlugin,
|
|
|
|
|
DefinePlugin,
|
|
|
|
|
HtmlRspackPlugin,
|
|
|
|
|
LightningCssMinimizerRspackPlugin,
|
|
|
|
|
SwcJsMinimizerRspackPlugin,
|
|
|
|
|
} from '@rspack/core';
|
2026-01-01 20:42:59 +00:00
|
|
|
import {createPoFileRule, getLinguiSwcPluginConfig} from './scripts/build/rspack/lingui.mjs';
|
|
|
|
|
import {staticFilesPlugin} from './scripts/build/rspack/static-files.mjs';
|
|
|
|
|
|
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
|
|
|
const __dirname = dirname(__filename);
|
|
|
|
|
const ROOT_DIR = path.resolve(__dirname, '.');
|
2026-02-17 12:22:36 +00:00
|
|
|
const MONOREPO_ROOT = path.resolve(__dirname, '..');
|
2026-01-01 20:42:59 +00:00
|
|
|
const SRC_DIR = path.join(ROOT_DIR, 'src');
|
|
|
|
|
const DIST_DIR = path.join(ROOT_DIR, 'dist');
|
|
|
|
|
const PKGS_DIR = path.join(ROOT_DIR, 'pkgs');
|
|
|
|
|
const PUBLIC_DIR = path.join(ROOT_DIR, 'assets');
|
2026-06-16 06:41:37 +02:00
|
|
|
const BROWSER_ASSERT_STRICT_MODULE = path.join(SRC_DIR, 'features', 'platform', 'utils', 'BrowserAssertStrict.ts');
|
|
|
|
|
const DEFAULT_DEV_SERVER_PORT = 3000;
|
2026-01-01 20:42:59 +00:00
|
|
|
|
2026-02-17 12:22:36 +00:00
|
|
|
function resolveMode() {
|
|
|
|
|
const modeIndex = process.argv.indexOf('--mode');
|
|
|
|
|
if (modeIndex >= 0) {
|
|
|
|
|
const modeValue = process.argv[modeIndex + 1];
|
|
|
|
|
if (modeValue) {
|
|
|
|
|
return modeValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 'production';
|
|
|
|
|
}
|
2026-01-01 20:42:59 +00:00
|
|
|
|
2026-06-16 06:41:37 +02:00
|
|
|
function isMainRuntimeChunk(chunk) {
|
|
|
|
|
const runtime = chunk.runtime;
|
|
|
|
|
if (runtime == null) {
|
|
|
|
|
return chunk.name === 'main';
|
|
|
|
|
}
|
|
|
|
|
if (typeof runtime === 'string') {
|
|
|
|
|
return runtime === 'main';
|
|
|
|
|
}
|
|
|
|
|
if (typeof runtime[Symbol.iterator] === 'function') {
|
|
|
|
|
for (const name of runtime) {
|
|
|
|
|
if (name !== 'main') return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function nodeAssertStrictSchemePlugin() {
|
|
|
|
|
return {
|
|
|
|
|
apply(compiler) {
|
|
|
|
|
compiler.hooks.normalModuleFactory.tap('NodeAssertStrictSchemePlugin', (factory) => {
|
|
|
|
|
factory.hooks.beforeResolve.tap('NodeAssertStrictSchemePlugin', (resolveData) => {
|
|
|
|
|
if (resolveData.request === 'node:assert/strict') {
|
|
|
|
|
resolveData.request = BROWSER_ASSERT_STRICT_MODULE;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-17 12:22:36 +00:00
|
|
|
const mode = resolveMode();
|
|
|
|
|
const isProduction = mode === 'production';
|
|
|
|
|
const isDevelopment = !isProduction;
|
2026-01-01 20:42:59 +00:00
|
|
|
const devJsName = 'assets/[name].js';
|
|
|
|
|
const devCssName = 'assets/[name].css';
|
2026-06-16 06:41:37 +02:00
|
|
|
const productionJsName = 'assets/[contenthash:16].js';
|
|
|
|
|
const productionWorkerJsName = 'assets/[contenthash:16].worker.js';
|
|
|
|
|
const devCorsHeaders = {
|
|
|
|
|
'Access-Control-Allow-Origin': '*',
|
|
|
|
|
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
|
|
|
|
'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
|
|
|
|
|
};
|
|
|
|
|
const devNoStoreHeaders = {
|
|
|
|
|
'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0',
|
|
|
|
|
Pragma: 'no-cache',
|
|
|
|
|
Expires: '0',
|
|
|
|
|
'CDN-Cache-Control': 'no-store',
|
|
|
|
|
'Cloudflare-CDN-Cache-Control': 'no-store',
|
|
|
|
|
};
|
|
|
|
|
function envString(name, fallback = undefined) {
|
|
|
|
|
const value = process.env[name];
|
|
|
|
|
if (value === undefined || value === null || value === '') {
|
2026-02-17 12:22:36 +00:00
|
|
|
return fallback;
|
|
|
|
|
}
|
2026-06-16 06:41:37 +02:00
|
|
|
return value;
|
2026-02-17 12:22:36 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-16 06:41:37 +02:00
|
|
|
function withTrailingSlash(value) {
|
|
|
|
|
return value.endsWith('/') ? value : `${value}/`;
|
2026-02-17 12:22:36 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-16 06:41:37 +02:00
|
|
|
function devServerHeaders() {
|
|
|
|
|
return {...devCorsHeaders, ...devNoStoreHeaders};
|
2026-02-17 12:22:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resolveReleaseChannel() {
|
2026-06-16 06:41:37 +02:00
|
|
|
const value = envString('PUBLIC_RELEASE_CHANNEL', envString('RELEASE_CHANNEL', 'canary')).trim().toLowerCase();
|
|
|
|
|
if (value === 'stable' || value === 'canary') {
|
|
|
|
|
return value;
|
2026-02-17 12:22:36 +00:00
|
|
|
}
|
2026-06-16 06:41:37 +02:00
|
|
|
return 'canary';
|
2026-02-17 12:22:36 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-16 06:41:37 +02:00
|
|
|
function resolvePublicValues() {
|
2026-02-17 12:22:36 +00:00
|
|
|
return {
|
2026-06-16 06:41:37 +02:00
|
|
|
PUBLIC_API_VERSION: envString('PUBLIC_API_VERSION', '1'),
|
|
|
|
|
PUBLIC_BUILD_VERSION: envString('PUBLIC_BUILD_VERSION', envString('BUILD_VERSION', 'dev')),
|
|
|
|
|
PUBLIC_RELEASE_CHANNEL: resolveReleaseChannel(),
|
|
|
|
|
PUBLIC_BOOTSTRAP_API_ENDPOINT: envString('PUBLIC_BOOTSTRAP_API_ENDPOINT', '/api'),
|
|
|
|
|
PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT: envString('PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT'),
|
2026-02-17 12:22:36 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getPublicEnvVar(values, name) {
|
|
|
|
|
const value = values[name];
|
|
|
|
|
return value === undefined ? 'undefined' : JSON.stringify(value);
|
2026-01-01 20:42:59 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-16 06:41:37 +02:00
|
|
|
function getChunkRuntimeNames(runtime) {
|
|
|
|
|
if (runtime == null) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
if (typeof runtime === 'string') {
|
|
|
|
|
return [runtime];
|
|
|
|
|
}
|
|
|
|
|
if (typeof runtime[Symbol.iterator] === 'function') {
|
|
|
|
|
return [...runtime].filter((name) => typeof name === 'string');
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getChunkPathNames(pathData) {
|
|
|
|
|
const names = [];
|
|
|
|
|
const chunkName = pathData.chunk?.name;
|
|
|
|
|
if (typeof chunkName === 'string') {
|
|
|
|
|
names.push(chunkName);
|
|
|
|
|
}
|
|
|
|
|
names.push(...getChunkRuntimeNames(pathData.runtime));
|
|
|
|
|
names.push(...getChunkRuntimeNames(pathData.chunk?.runtime));
|
|
|
|
|
return names;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isWorkerPath(pathData) {
|
|
|
|
|
return getChunkPathNames(pathData).some((name) => name.endsWith('.worker'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function jsFilename(pathData) {
|
|
|
|
|
if (pathData.chunk?.name === 'sw') {
|
|
|
|
|
return 'sw.js';
|
|
|
|
|
}
|
|
|
|
|
if (!isProduction) {
|
|
|
|
|
return devJsName;
|
|
|
|
|
}
|
|
|
|
|
return isWorkerPath(pathData) ? productionWorkerJsName : productionJsName;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 20:42:59 +00:00
|
|
|
export default () => {
|
|
|
|
|
const linguiSwcPlugin = getLinguiSwcPluginConfig();
|
2026-06-16 06:41:37 +02:00
|
|
|
const publicValues = resolvePublicValues();
|
|
|
|
|
const assetBaseUrl = envString('PUBLIC_ASSET_BASE_URL');
|
|
|
|
|
const staticCdnEndpoint = envString(
|
|
|
|
|
'PUBLIC_STATIC_CDN_ENDPOINT',
|
|
|
|
|
envString('FLUXER_STATIC_CDN_ENDPOINT', isProduction ? '' : ''),
|
|
|
|
|
);
|
|
|
|
|
function resolveArboriumWasmAliases() {
|
|
|
|
|
const arbDir = path.join(ROOT_DIR, 'node_modules', '@arborium');
|
|
|
|
|
const aliases = {};
|
|
|
|
|
try {
|
|
|
|
|
for (const pkg of readdirSync(arbDir)) {
|
|
|
|
|
const grammarWasm = path.join(arbDir, pkg, 'grammar_bg.wasm');
|
|
|
|
|
if (!existsSync(grammarWasm)) continue;
|
|
|
|
|
const internalWasm = `arborium_${pkg.replace(/-/g, '_')}_plugin_bg.wasm`;
|
|
|
|
|
aliases[internalWasm] = grammarWasm;
|
|
|
|
|
aliases[`@arborium/${pkg}/${internalWasm}`] = grammarWasm;
|
|
|
|
|
}
|
|
|
|
|
} catch {}
|
|
|
|
|
return aliases;
|
|
|
|
|
}
|
|
|
|
|
const normalizedStaticCdnEndpoint = staticCdnEndpoint?.replace(/\/+$/, '') ?? '';
|
|
|
|
|
const workerWasmPublicPath =
|
|
|
|
|
isProduction && normalizedStaticCdnEndpoint ? `${normalizedStaticCdnEndpoint}/` : undefined;
|
|
|
|
|
const productionPublicPath = assetBaseUrl !== undefined ? withTrailingSlash(assetBaseUrl) : '/';
|
|
|
|
|
const developmentPublicPath = normalizedStaticCdnEndpoint ? `${normalizedStaticCdnEndpoint}/` : '/';
|
|
|
|
|
const publicPath = isProduction ? productionPublicPath : developmentPublicPath;
|
2026-01-01 20:42:59 +00:00
|
|
|
return {
|
|
|
|
|
mode,
|
|
|
|
|
entry: {
|
|
|
|
|
main: path.join(SRC_DIR, 'index.tsx'),
|
2026-06-16 06:41:37 +02:00
|
|
|
sw: path.join(SRC_DIR, 'features', 'platform', 'service_worker', 'Worker.ts'),
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
|
|
|
|
output: {
|
|
|
|
|
path: DIST_DIR,
|
2026-06-16 06:41:37 +02:00
|
|
|
publicPath,
|
2026-01-01 20:42:59 +00:00
|
|
|
workerPublicPath: '/',
|
2026-06-16 06:41:37 +02:00
|
|
|
workerChunkLoading: false,
|
|
|
|
|
filename: jsFilename,
|
|
|
|
|
chunkFilename: jsFilename,
|
2026-01-01 20:42:59 +00:00
|
|
|
cssFilename: isProduction ? 'assets/[contenthash:16].css' : devCssName,
|
|
|
|
|
cssChunkFilename: isProduction ? 'assets/[contenthash:16].css' : devCssName,
|
|
|
|
|
assetModuleFilename: isProduction ? 'assets/[contenthash:16][ext]' : 'assets/[name].[hash][ext]',
|
|
|
|
|
webAssemblyModuleFilename: isProduction ? 'assets/[contenthash:16].wasm' : 'assets/[name].[hash].wasm',
|
|
|
|
|
clean: true,
|
|
|
|
|
},
|
|
|
|
|
devtool: 'source-map',
|
|
|
|
|
target: ['web', 'browserslist'],
|
2026-06-16 06:41:37 +02:00
|
|
|
lazyCompilation: false,
|
2026-01-01 20:42:59 +00:00
|
|
|
resolve: {
|
|
|
|
|
alias: {
|
2026-06-16 06:41:37 +02:00
|
|
|
...resolveArboriumWasmAliases(),
|
|
|
|
|
'@arborium/arborium/arborium_host_bg.wasm': path.resolve(
|
|
|
|
|
ROOT_DIR,
|
|
|
|
|
'node_modules/@arborium/arborium/dist/arborium_host_bg.wasm',
|
|
|
|
|
),
|
2026-02-17 12:22:36 +00:00
|
|
|
'@app': SRC_DIR,
|
2026-06-16 06:41:37 +02:00
|
|
|
'@fluxer/voice_engine_v2/bridge': path.join(
|
|
|
|
|
MONOREPO_ROOT,
|
|
|
|
|
'packages',
|
|
|
|
|
'voice_engine_v2',
|
|
|
|
|
'src',
|
|
|
|
|
'bridge',
|
|
|
|
|
'index.ts',
|
|
|
|
|
),
|
|
|
|
|
'@fluxer/voice_engine_v2/runtime': path.join(
|
|
|
|
|
MONOREPO_ROOT,
|
|
|
|
|
'packages',
|
|
|
|
|
'voice_engine_v2',
|
|
|
|
|
'src',
|
|
|
|
|
'runtime',
|
|
|
|
|
'index.ts',
|
|
|
|
|
),
|
|
|
|
|
'@fluxer/voice_engine_v2/testing': path.join(
|
|
|
|
|
MONOREPO_ROOT,
|
|
|
|
|
'packages',
|
|
|
|
|
'voice_engine_v2',
|
|
|
|
|
'src',
|
|
|
|
|
'testing',
|
|
|
|
|
'index.ts',
|
|
|
|
|
),
|
|
|
|
|
'@fluxer': path.join(MONOREPO_ROOT, 'packages'),
|
2026-01-01 20:42:59 +00:00
|
|
|
'@pkgs': PKGS_DIR,
|
2026-06-16 06:41:37 +02:00
|
|
|
'assert/strict': BROWSER_ASSERT_STRICT_MODULE,
|
|
|
|
|
'livekit-client$': path.join(PKGS_DIR, 'livekit-client/src/index.ts'),
|
|
|
|
|
'livekit-client/e2ee-worker': path.join(PKGS_DIR, 'livekit-client/src/e2ee/worker/e2ee.worker.ts'),
|
|
|
|
|
'node:assert/strict': BROWSER_ASSERT_STRICT_MODULE,
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
|
|
|
|
extensions: [
|
|
|
|
|
'.web.tsx',
|
|
|
|
|
'.web.ts',
|
|
|
|
|
'.web.jsx',
|
|
|
|
|
'.web.js',
|
|
|
|
|
'.tsx',
|
|
|
|
|
'.ts',
|
|
|
|
|
'.jsx',
|
|
|
|
|
'.js',
|
|
|
|
|
'.json',
|
|
|
|
|
'.mjs',
|
|
|
|
|
'.cjs',
|
|
|
|
|
'.po',
|
|
|
|
|
],
|
2026-06-16 06:41:37 +02:00
|
|
|
extensionAlias: {
|
|
|
|
|
'.js': ['.js', '.tsx', '.ts'],
|
|
|
|
|
'.mjs': ['.mjs', '.mts'],
|
|
|
|
|
'.cjs': ['.cjs', '.cts'],
|
|
|
|
|
},
|
|
|
|
|
conditionNames: ['import', 'module', 'webpack', 'browser', 'default'],
|
|
|
|
|
mainFields: ['browser', 'module', 'main'],
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
|
|
|
|
module: {
|
|
|
|
|
rules: [
|
2026-06-16 06:41:37 +02:00
|
|
|
{
|
|
|
|
|
test: /[\\/]@arborium[\\/]arborium[\\/]dist[\\/]arborium\.js$/,
|
|
|
|
|
use: [{loader: path.join(ROOT_DIR, 'scripts/build/rspack/local-arborium-loader.cjs')}],
|
|
|
|
|
parser: {
|
|
|
|
|
wrappedContextRegExp: /^\b\B$/u,
|
|
|
|
|
exprContextCritical: false,
|
|
|
|
|
wrappedContextCritical: false,
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-01-01 20:42:59 +00:00
|
|
|
{
|
|
|
|
|
test: /\.(tsx|ts|jsx|js)$/,
|
|
|
|
|
exclude: /node_modules/,
|
|
|
|
|
type: 'javascript/auto',
|
|
|
|
|
parser: {
|
2026-06-16 06:41:37 +02:00
|
|
|
dynamicImport: true,
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
|
|
|
|
use: {
|
|
|
|
|
loader: 'builtin:swc-loader',
|
|
|
|
|
options: {
|
|
|
|
|
jsc: {
|
|
|
|
|
parser: {
|
|
|
|
|
syntax: 'typescript',
|
|
|
|
|
tsx: true,
|
|
|
|
|
decorators: true,
|
|
|
|
|
},
|
|
|
|
|
transform: {
|
|
|
|
|
legacyDecorator: true,
|
|
|
|
|
decoratorMetadata: true,
|
|
|
|
|
react: {
|
|
|
|
|
runtime: 'automatic',
|
|
|
|
|
development: isDevelopment,
|
2026-02-17 12:22:36 +00:00
|
|
|
refresh: false,
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
experimental: {
|
|
|
|
|
plugins: [linguiSwcPlugin],
|
|
|
|
|
},
|
|
|
|
|
target: 'es2015',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
createPoFileRule(),
|
|
|
|
|
{
|
|
|
|
|
test: /\.module\.css$/,
|
|
|
|
|
use: [{loader: 'postcss-loader'}],
|
|
|
|
|
type: 'css/module',
|
|
|
|
|
parser: {namedExports: false},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
test: /\.css$/,
|
|
|
|
|
exclude: /\.module\.css$/,
|
|
|
|
|
use: [{loader: 'postcss-loader'}],
|
|
|
|
|
type: 'css',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
test: /\.svg$/,
|
|
|
|
|
issuer: /\.[jt]sx?$/,
|
|
|
|
|
resourceQuery: /react/,
|
|
|
|
|
type: 'javascript/auto',
|
|
|
|
|
use: [
|
|
|
|
|
{
|
|
|
|
|
loader: 'builtin:swc-loader',
|
|
|
|
|
options: {
|
|
|
|
|
jsc: {
|
|
|
|
|
parser: {syntax: 'typescript', tsx: true},
|
|
|
|
|
transform: {react: {runtime: 'automatic', development: isDevelopment}},
|
|
|
|
|
target: 'es2015',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
loader: '@svgr/webpack',
|
|
|
|
|
options: {
|
|
|
|
|
babel: false,
|
|
|
|
|
typescript: true,
|
|
|
|
|
jsxRuntime: 'automatic',
|
|
|
|
|
svgoConfig: {
|
|
|
|
|
plugins: [
|
|
|
|
|
{
|
|
|
|
|
name: 'preset-default',
|
|
|
|
|
params: {overrides: {removeViewBox: false}},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
test: /\.svg$/,
|
|
|
|
|
resourceQuery: {not: [/react/]},
|
|
|
|
|
type: 'asset/resource',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
test: /\.wasm$/,
|
|
|
|
|
type: 'asset/resource',
|
2026-06-16 06:41:37 +02:00
|
|
|
generator: {
|
|
|
|
|
filename: isProduction ? 'assets/[contenthash:16][ext]' : 'assets/[name].[hash][ext]',
|
|
|
|
|
...(workerWasmPublicPath ? {publicPath: workerWasmPublicPath} : {}),
|
|
|
|
|
},
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
2026-09-02 17:31:04 +02:00
|
|
|
{
|
|
|
|
|
test: /\.onnx$/,
|
|
|
|
|
type: 'asset/resource',
|
|
|
|
|
generator: {
|
|
|
|
|
filename: isProduction ? 'assets/[contenthash:16][ext]' : 'assets/[name].[hash][ext]',
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-01-01 20:42:59 +00:00
|
|
|
{
|
|
|
|
|
test: /\.(png|jpg|jpeg|gif|webp|ico|woff|woff2|ttf|eot|mp3|wav|ogg|mp4|webm)$/,
|
|
|
|
|
type: 'asset/resource',
|
|
|
|
|
generator: {
|
|
|
|
|
filename: isProduction ? 'assets/[contenthash:16][ext]' : 'assets/[name].[hash][ext]',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
2026-02-17 12:22:36 +00:00
|
|
|
generator: {
|
|
|
|
|
'css/module': {
|
|
|
|
|
localIdentName: '[name]__[local]___[hash:base64:6]',
|
|
|
|
|
exportsConvention: 'camel-case-only',
|
|
|
|
|
exportsOnly: false,
|
|
|
|
|
},
|
|
|
|
|
'css/auto': {
|
|
|
|
|
localIdentName: '[name]__[local]___[hash:base64:6]',
|
|
|
|
|
exportsConvention: 'camel-case-only',
|
|
|
|
|
exportsOnly: false,
|
|
|
|
|
},
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
plugins: [
|
2026-06-16 06:41:37 +02:00
|
|
|
nodeAssertStrictSchemePlugin(),
|
2026-01-01 20:42:59 +00:00
|
|
|
new HtmlRspackPlugin({
|
|
|
|
|
template: path.join(ROOT_DIR, 'index.html'),
|
|
|
|
|
filename: 'index.html',
|
2026-06-16 06:41:37 +02:00
|
|
|
hash: isDevelopment,
|
2026-01-01 20:42:59 +00:00
|
|
|
inject: 'body',
|
|
|
|
|
scriptLoading: 'module',
|
2026-02-17 12:22:36 +00:00
|
|
|
excludeChunks: ['sw'],
|
2026-01-01 20:42:59 +00:00
|
|
|
}),
|
|
|
|
|
new CopyRspackPlugin({
|
|
|
|
|
patterns: [
|
|
|
|
|
{
|
|
|
|
|
from: PUBLIC_DIR,
|
|
|
|
|
to: DIST_DIR,
|
|
|
|
|
noErrorOnMissing: true,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}),
|
2026-08-12 13:21:50 +02:00
|
|
|
staticFilesPlugin({
|
|
|
|
|
staticCdnEndpoint: normalizedStaticCdnEndpoint,
|
|
|
|
|
fontsDir: path.join(MONOREPO_ROOT, 'packages', 'fonts'),
|
|
|
|
|
}),
|
2026-01-01 20:42:59 +00:00
|
|
|
new DefinePlugin({
|
2026-06-16 06:41:37 +02:00
|
|
|
__FLUXER_PRECACHE_MANIFEST__: JSON.stringify([]),
|
|
|
|
|
__FLUXER_SW_VERSION__: JSON.stringify(publicValues.PUBLIC_BUILD_VERSION || 'dev'),
|
2026-01-01 20:42:59 +00:00
|
|
|
'process.env.NODE_ENV': JSON.stringify(mode),
|
|
|
|
|
'import.meta.env.DEV': JSON.stringify(isDevelopment),
|
|
|
|
|
'import.meta.env.PROD': JSON.stringify(isProduction),
|
|
|
|
|
'import.meta.env.MODE': JSON.stringify(mode),
|
2026-06-16 06:41:37 +02:00
|
|
|
'import.meta.env.PUBLIC_BUILD_VERSION': getPublicEnvVar(publicValues, 'PUBLIC_BUILD_VERSION'),
|
2026-02-17 12:22:36 +00:00
|
|
|
'import.meta.env.PUBLIC_API_VERSION': getPublicEnvVar(publicValues, 'PUBLIC_API_VERSION'),
|
2026-06-16 06:41:37 +02:00
|
|
|
'import.meta.env.PUBLIC_RELEASE_CHANNEL': getPublicEnvVar(publicValues, 'PUBLIC_RELEASE_CHANNEL'),
|
2026-02-17 12:22:36 +00:00
|
|
|
'import.meta.env.PUBLIC_BOOTSTRAP_API_ENDPOINT': getPublicEnvVar(publicValues, 'PUBLIC_BOOTSTRAP_API_ENDPOINT'),
|
|
|
|
|
'import.meta.env.PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT': getPublicEnvVar(
|
|
|
|
|
publicValues,
|
|
|
|
|
'PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT',
|
|
|
|
|
),
|
2026-01-01 20:42:59 +00:00
|
|
|
}),
|
2026-06-16 06:41:37 +02:00
|
|
|
{
|
|
|
|
|
apply(compiler) {
|
|
|
|
|
compiler.hooks.afterEmit.tap('VersionJsonPlugin', () => {
|
|
|
|
|
const versionData = {
|
|
|
|
|
version: publicValues.PUBLIC_BUILD_VERSION,
|
|
|
|
|
};
|
|
|
|
|
mkdirSync(DIST_DIR, {recursive: true});
|
|
|
|
|
writeFileSync(path.join(DIST_DIR, 'version.json'), JSON.stringify(versionData));
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-02-17 12:22:36 +00:00
|
|
|
],
|
2026-01-01 20:42:59 +00:00
|
|
|
optimization: {
|
|
|
|
|
splitChunks: isProduction
|
|
|
|
|
? {
|
2026-06-16 06:41:37 +02:00
|
|
|
chunks: (chunk) => isMainRuntimeChunk(chunk),
|
|
|
|
|
maxInitialRequests: 15,
|
2026-01-01 20:42:59 +00:00
|
|
|
cacheGroups: {
|
|
|
|
|
icons: {
|
|
|
|
|
test: /[\\/]node_modules[\\/]@phosphor-icons[\\/]/,
|
|
|
|
|
name: 'icons',
|
|
|
|
|
priority: 60,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
highlight: {
|
2026-06-16 06:41:37 +02:00
|
|
|
test: /[\\/]node_modules[\\/](@arborium[\\/]arborium[\\/]|\.pnpm[\\/]@arborium\+arborium@)/,
|
2026-01-01 20:42:59 +00:00
|
|
|
name: 'highlight',
|
|
|
|
|
priority: 55,
|
|
|
|
|
reuseExistingChunk: true,
|
2026-06-16 06:41:37 +02:00
|
|
|
enforce: true,
|
2026-08-13 19:52:00 +02:00
|
|
|
chunks: (chunk) => !chunk.canBeInitial() && !isWorkerPath({chunk}),
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
|
|
|
|
livekit: {
|
|
|
|
|
test: /[\\/]node_modules[\\/](livekit-client|@livekit)[\\/]/,
|
|
|
|
|
name: 'livekit',
|
|
|
|
|
priority: 50,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
katex: {
|
|
|
|
|
test: /[\\/]node_modules[\\/]katex[\\/]/,
|
|
|
|
|
name: 'katex',
|
|
|
|
|
priority: 48,
|
|
|
|
|
reuseExistingChunk: true,
|
2026-06-16 06:41:37 +02:00
|
|
|
maxSize: 100_000,
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
|
|
|
|
animation: {
|
|
|
|
|
test: /[\\/]node_modules[\\/](framer-motion|motion)[\\/]/,
|
|
|
|
|
name: 'animation',
|
|
|
|
|
priority: 45,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
mobx: {
|
|
|
|
|
test: /[\\/]node_modules[\\/](mobx|mobx-react-lite|mobx-persist-store)[\\/]/,
|
|
|
|
|
name: 'mobx',
|
|
|
|
|
priority: 43,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
reactAria: {
|
|
|
|
|
test: /[\\/]node_modules[\\/]react-aria-components[\\/]/,
|
|
|
|
|
name: 'react-aria',
|
2026-06-16 06:41:37 +02:00
|
|
|
priority: 41,
|
2026-01-01 20:42:59 +00:00
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
validation: {
|
|
|
|
|
test: /[\\/]node_modules[\\/](valibot)[\\/]/,
|
|
|
|
|
name: 'validation',
|
2026-06-16 06:41:37 +02:00
|
|
|
priority: 39,
|
2026-01-01 20:42:59 +00:00
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
datetime: {
|
|
|
|
|
test: /[\\/]node_modules[\\/]luxon[\\/]/,
|
|
|
|
|
name: 'datetime',
|
2026-06-16 06:41:37 +02:00
|
|
|
priority: 38,
|
2026-01-01 20:42:59 +00:00
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
observable: {
|
|
|
|
|
test: /[\\/]node_modules[\\/]rxjs[\\/]/,
|
|
|
|
|
name: 'observable',
|
2026-06-16 06:41:37 +02:00
|
|
|
priority: 37,
|
2026-01-01 20:42:59 +00:00
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
unicode: {
|
|
|
|
|
test: /[\\/]node_modules[\\/](idna-uts46-hx|emoji-regex)[\\/]/,
|
|
|
|
|
name: 'unicode',
|
2026-06-16 06:41:37 +02:00
|
|
|
priority: 36,
|
2026-01-01 20:42:59 +00:00
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
dnd: {
|
|
|
|
|
test: /[\\/]node_modules[\\/](@dnd-kit|react-dnd)[\\/]/,
|
|
|
|
|
name: 'dnd',
|
|
|
|
|
priority: 33,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
radix: {
|
|
|
|
|
test: /[\\/]node_modules[\\/]@radix-ui[\\/]/,
|
|
|
|
|
name: 'radix',
|
|
|
|
|
priority: 31,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
ui: {
|
2026-06-16 06:41:37 +02:00
|
|
|
test: /[\\/]node_modules[\\/](react-select|react-hook-form|@floating-ui)[\\/]/,
|
2026-01-01 20:42:59 +00:00
|
|
|
name: 'ui',
|
|
|
|
|
priority: 30,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
utils: {
|
2026-01-05 14:32:55 +01:00
|
|
|
test: /[\\/]node_modules[\\/](lodash|clsx|qrcode|thumbhash|bowser|match-sorter)[\\/]/,
|
2026-01-01 20:42:59 +00:00
|
|
|
name: 'utils',
|
|
|
|
|
priority: 28,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
networking: {
|
2026-02-17 12:22:36 +00:00
|
|
|
test: /[\\/]node_modules[\\/](ws)[\\/]/,
|
2026-01-01 20:42:59 +00:00
|
|
|
name: 'networking',
|
|
|
|
|
priority: 26,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
react: {
|
|
|
|
|
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
|
|
|
|
|
name: 'react',
|
|
|
|
|
priority: 25,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
vendor: {
|
2026-06-16 06:41:37 +02:00
|
|
|
test: (module) => {
|
|
|
|
|
if (!module.resource) return false;
|
|
|
|
|
if (!/[\\/]node_modules[\\/]/.test(module.resource)) return false;
|
|
|
|
|
if (/[\\/](@arborium|@phosphor-icons|katex)[\\/]/.test(module.resource)) return false;
|
|
|
|
|
if (/[\\/]\.pnpm[\\/]@arborium\+/.test(module.resource)) return false;
|
|
|
|
|
return true;
|
|
|
|
|
},
|
2026-01-01 20:42:59 +00:00
|
|
|
name: 'vendor',
|
|
|
|
|
priority: 10,
|
|
|
|
|
reuseExistingChunk: true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
: false,
|
|
|
|
|
runtimeChunk: false,
|
2026-06-16 06:41:37 +02:00
|
|
|
moduleIds: isProduction ? 'deterministic' : 'named',
|
|
|
|
|
chunkIds: isProduction ? 'deterministic' : 'named',
|
2026-01-01 20:42:59 +00:00
|
|
|
minimize: isProduction,
|
|
|
|
|
minimizer: [
|
|
|
|
|
new SwcJsMinimizerRspackPlugin({
|
|
|
|
|
compress: true,
|
|
|
|
|
mangle: true,
|
|
|
|
|
format: {comments: false},
|
|
|
|
|
}),
|
2026-08-24 12:26:55 +02:00
|
|
|
new LightningCssMinimizerRspackPlugin(),
|
2026-01-01 20:42:59 +00:00
|
|
|
],
|
|
|
|
|
},
|
2026-02-17 12:22:36 +00:00
|
|
|
devServer: {
|
2026-06-16 06:41:37 +02:00
|
|
|
port: Number(process.env.FLUXER_APP_DEV_PORT) || DEFAULT_DEV_SERVER_PORT,
|
2026-02-17 12:22:36 +00:00
|
|
|
hot: false,
|
|
|
|
|
liveReload: false,
|
|
|
|
|
client: false,
|
|
|
|
|
webSocketServer: false,
|
|
|
|
|
historyApiFallback: true,
|
|
|
|
|
allowedHosts: 'all',
|
2026-06-16 06:41:37 +02:00
|
|
|
headers: isDevelopment ? devServerHeaders : devCorsHeaders,
|
2026-02-17 12:22:36 +00:00
|
|
|
static: {
|
|
|
|
|
directory: DIST_DIR,
|
|
|
|
|
watch: false,
|
2026-01-01 20:42:59 +00:00
|
|
|
},
|
|
|
|
|
},
|
2026-02-17 12:22:36 +00:00
|
|
|
experiments: {css: true},
|
2026-01-01 20:42:59 +00:00
|
|
|
};
|
|
|
|
|
};
|