mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
feat(desktop): bundled renderer, modules and instance accounts (#3254)
This commit is contained in:
1 parent
c748c8af4e
commit
7c5fa2180a
1275 files changed
+215074
-81246
No files matched your search
+321
-26
@@ -4,11 +4,13 @@ import {existsSync, mkdirSync, readdirSync, writeFileSync} from 'node:fs';
|
||||
import path, {dirname} from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import {
|
||||
Compilation,
|
||||
CopyRspackPlugin,
|
||||
DefinePlugin,
|
||||
HtmlRspackPlugin,
|
||||
LightningCssMinimizerRspackPlugin,
|
||||
SwcJsMinimizerRspackPlugin,
|
||||
sources,
|
||||
} from '@rspack/core';
|
||||
import {createPoFileRule, getLinguiSwcPluginConfig} from './scripts/build/rspack/lingui.mjs';
|
||||
import {staticFilesPlugin} from './scripts/build/rspack/static-files.mjs';
|
||||
@@ -23,6 +25,35 @@ const PKGS_DIR = path.join(ROOT_DIR, 'pkgs');
|
||||
const PUBLIC_DIR = path.join(ROOT_DIR, 'assets');
|
||||
const BROWSER_ASSERT_STRICT_MODULE = path.join(SRC_DIR, 'features', 'platform', 'utils', 'BrowserAssertStrict.ts');
|
||||
const DEFAULT_DEV_SERVER_PORT = 3000;
|
||||
const BOOT_PRELOAD_CHUNK_GROUPS = ['app-bootstrap', 'boot-app'];
|
||||
const BOOT_PRELOAD_FONT_FILES = [
|
||||
'FluxerSans/FluxerSans-Regular.woff2',
|
||||
'FluxerSans/FluxerSans-SemiBold.woff2',
|
||||
'FluxerSans/FluxerSans-Bold.woff2',
|
||||
];
|
||||
const BOOT_LOW_PRIORITY_FONT_FILES = ['FluxerSans/FluxerSans-Medium.woff2'];
|
||||
const AUTH_ENTRY_DOCUMENT = 'auth-index.html';
|
||||
const ENTRY_STYLESHEET_LINK_PATTERN = /<link href="([^"]+\.css)" rel="stylesheet">/gu;
|
||||
const CSS_URL_PATTERN = /url\(\s*['"]?([^'")]+)/gu;
|
||||
const DESKTOP_MODULE_ASSET_QUERY = /[?&]m=([a-z][a-z0-9_]{0,63})(?:&|$)/u;
|
||||
const RESERVED_DESKTOP_MODULE_NAMES = new Set(['assets', 'fluxer_renderer']);
|
||||
|
||||
class UnusableDesktopModuleQueryError extends Error {
|
||||
constructor(resource) {
|
||||
super(
|
||||
`${resource} carries an ?m= desktop module query that is not a usable module name. It must match [a-z][a-z0-9_]{0,63} and cannot be assets or fluxer_renderer.`,
|
||||
);
|
||||
this.name = 'UnusableDesktopModuleQueryError';
|
||||
}
|
||||
}
|
||||
|
||||
function desktopModuleAssetName(resource) {
|
||||
const match = DESKTOP_MODULE_ASSET_QUERY.exec(resource);
|
||||
if (match == null || RESERVED_DESKTOP_MODULE_NAMES.has(match[1])) {
|
||||
throw new UnusableDesktopModuleQueryError(resource);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function resolveMode() {
|
||||
const modeIndex = process.argv.indexOf('--mode');
|
||||
@@ -52,6 +83,164 @@ function isMainRuntimeChunk(chunk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function mirrorCacheGroups(groups) {
|
||||
const mirrored = {};
|
||||
for (const [key, group] of Object.entries(groups)) {
|
||||
if (group.chunks != null) {
|
||||
mirrored[key] = group;
|
||||
continue;
|
||||
}
|
||||
mirrored[key] = {...group, chunks: (chunk) => isMainRuntimeChunk(chunk) && chunk.canBeInitial()};
|
||||
mirrored[`${key}Async`] = {
|
||||
...group,
|
||||
name: `${group.name}-async`,
|
||||
chunks: (chunk) => isMainRuntimeChunk(chunk) && !chunk.canBeInitial(),
|
||||
};
|
||||
}
|
||||
return mirrored;
|
||||
}
|
||||
|
||||
function bootPreloadPlugin() {
|
||||
return {
|
||||
apply(compiler) {
|
||||
compiler.hooks.thisCompilation.tap('BootPreloadPlugin', (compilation) => {
|
||||
compilation.hooks.processAssets.tap(
|
||||
{name: 'BootPreloadPlugin', stage: Compilation.PROCESS_ASSETS_STAGE_REPORT},
|
||||
() => {
|
||||
const asset = compilation.getAsset('index.html');
|
||||
if (asset == null) {
|
||||
return;
|
||||
}
|
||||
const html = String(asset.source.source());
|
||||
if (!html.includes('</body>')) {
|
||||
return;
|
||||
}
|
||||
const configuredPublicPath = compilation.outputOptions.publicPath;
|
||||
const base =
|
||||
typeof configuredPublicPath === 'string' && configuredPublicPath !== 'auto' ? configuredPublicPath : '/';
|
||||
const files = new Set();
|
||||
for (const name of BOOT_PRELOAD_CHUNK_GROUPS) {
|
||||
for (const group of compilation.chunkGroups) {
|
||||
if (group.name !== name) {
|
||||
continue;
|
||||
}
|
||||
for (const file of group.getFiles()) {
|
||||
if (!html.includes(file)) {
|
||||
files.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const ordered = [...files];
|
||||
const links = [
|
||||
...ordered
|
||||
.filter((file) => file.endsWith('.css'))
|
||||
.map((file) => `<link rel="preload" as="style" fetchpriority="low" href="${base}${file}">`),
|
||||
...ordered
|
||||
.filter((file) => file.endsWith('.js'))
|
||||
.map((file) => `<link rel="preload" as="script" fetchpriority="low" href="${base}${file}">`),
|
||||
].join('');
|
||||
if (links === '') {
|
||||
return;
|
||||
}
|
||||
compilation.updateAsset('index.html', new sources.RawSource(html.replace('</body>', `${links}</body>`)));
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class UninlinableEntryStylesheetError extends Error {
|
||||
constructor(reason) {
|
||||
super(`Cannot inline the entry stylesheet into ${AUTH_ENTRY_DOCUMENT}: ${reason}`);
|
||||
this.name = 'UninlinableEntryStylesheetError';
|
||||
}
|
||||
}
|
||||
|
||||
function inlineEntryStylesheet(css, href) {
|
||||
const content = css.replace(/\/\*# sourceMappingURL=[^*]*\*\/\s*$/u, '');
|
||||
if (content.includes('</style')) {
|
||||
throw new UninlinableEntryStylesheetError(`${href} contains a closing style tag`);
|
||||
}
|
||||
for (const [, url] of content.matchAll(CSS_URL_PATTERN)) {
|
||||
if (!/^(?:\/|https?:|data:|#)/u.test(url)) {
|
||||
throw new UninlinableEntryStylesheetError(`${href} references ${url} relative to its own location`);
|
||||
}
|
||||
}
|
||||
return `<style>${content}</style><link rel="prefetch" as="style" href="${href}">`;
|
||||
}
|
||||
|
||||
function authEntryDocumentPlugin() {
|
||||
return {
|
||||
apply(compiler) {
|
||||
compiler.hooks.thisCompilation.tap('AuthEntryDocumentPlugin', (compilation) => {
|
||||
compilation.hooks.processAssets.tap(
|
||||
{name: 'AuthEntryDocumentPlugin', stage: Compilation.PROCESS_ASSETS_STAGE_REPORT},
|
||||
() => {
|
||||
const asset = compilation.getAsset('index.html');
|
||||
if (asset == null) {
|
||||
return;
|
||||
}
|
||||
const html = String(asset.source.source());
|
||||
const links = [...html.matchAll(ENTRY_STYLESHEET_LINK_PATTERN)];
|
||||
if (links.length === 0) {
|
||||
throw new UninlinableEntryStylesheetError('index.html links no stylesheet');
|
||||
}
|
||||
let document = html;
|
||||
for (const [link, href] of links) {
|
||||
const name = compilation
|
||||
.getAssets()
|
||||
.find((candidate) => href.endsWith(`/${candidate.name}`) || href === candidate.name)?.name;
|
||||
if (name == null) {
|
||||
throw new UninlinableEntryStylesheetError(`${href} is not an emitted asset`);
|
||||
}
|
||||
const css = String(compilation.getAsset(name).source.source());
|
||||
document = document.replace(link, () => inlineEntryStylesheet(css, href));
|
||||
}
|
||||
compilation.emitAsset(AUTH_ENTRY_DOCUMENT, new sources.RawSource(document));
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function fontPreloadPlugin() {
|
||||
return {
|
||||
apply(compiler) {
|
||||
compiler.hooks.thisCompilation.tap('FontPreloadPlugin', (compilation) => {
|
||||
HtmlRspackPlugin.getCompilationHooks(compilation).alterAssetTagGroups.tap('FontPreloadPlugin', (data) => {
|
||||
const fonts = [];
|
||||
const lowPriority = new Set(BOOT_LOW_PRIORITY_FONT_FILES);
|
||||
for (const sourceName of [...BOOT_PRELOAD_FONT_FILES, ...BOOT_LOW_PRIORITY_FONT_FILES]) {
|
||||
const asset = compilation
|
||||
.getAssets()
|
||||
.find((candidate) => String(candidate.info.sourceFilename ?? '').endsWith(sourceName));
|
||||
if (asset == null) {
|
||||
throw new Error(`Cannot preload ${sourceName}: no emitted asset came from it`);
|
||||
}
|
||||
fonts.push({
|
||||
tagName: 'link',
|
||||
voidTag: true,
|
||||
attributes: {
|
||||
rel: 'preload',
|
||||
as: 'font',
|
||||
type: 'font/woff2',
|
||||
crossorigin: '',
|
||||
href: `${data.publicPath}${asset.name}`,
|
||||
...(lowPriority.has(sourceName) ? {fetchpriority: 'low'} : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
data.headTags.unshift(...fonts);
|
||||
return data;
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function nodeAssertStrictSchemePlugin() {
|
||||
return {
|
||||
apply(compiler) {
|
||||
@@ -69,6 +258,7 @@ function nodeAssertStrictSchemePlugin() {
|
||||
const mode = resolveMode();
|
||||
const isProduction = mode === 'production';
|
||||
const isDevelopment = !isProduction;
|
||||
const isDesktopRenderer = envString('FLUXER_DESKTOP_RENDERER') === 'true';
|
||||
const devJsName = 'assets/[name].js';
|
||||
const devCssName = 'assets/[name].css';
|
||||
const productionJsName = 'assets/[contenthash:16].js';
|
||||
@@ -103,19 +293,16 @@ function devServerHeaders() {
|
||||
|
||||
function resolveReleaseChannel() {
|
||||
const value = envString('PUBLIC_RELEASE_CHANNEL', envString('RELEASE_CHANNEL', 'canary')).trim().toLowerCase();
|
||||
if (value === 'stable' || value === 'canary') {
|
||||
if (value === 'stable' || value === 'canary' || value === 'development') {
|
||||
return value;
|
||||
}
|
||||
return 'canary';
|
||||
throw new Error(`PUBLIC_RELEASE_CHANNEL must be stable, canary, or development, received ${JSON.stringify(value)}`);
|
||||
}
|
||||
|
||||
function resolvePublicValues() {
|
||||
return {
|
||||
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'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -165,11 +352,10 @@ function jsFilename(pathData) {
|
||||
export default () => {
|
||||
const linguiSwcPlugin = getLinguiSwcPluginConfig();
|
||||
const publicValues = resolvePublicValues();
|
||||
const assetBaseUrl = envString('PUBLIC_ASSET_BASE_URL');
|
||||
const staticCdnEndpoint = envString(
|
||||
'PUBLIC_STATIC_CDN_ENDPOINT',
|
||||
envString('FLUXER_STATIC_CDN_ENDPOINT', isProduction ? '' : ''),
|
||||
);
|
||||
const assetBaseUrl = isDesktopRenderer ? undefined : envString('PUBLIC_ASSET_BASE_URL');
|
||||
const staticCdnEndpoint = isDesktopRenderer
|
||||
? ''
|
||||
: envString('PUBLIC_STATIC_CDN_ENDPOINT', envString('FLUXER_STATIC_CDN_ENDPOINT', isProduction ? '' : ''));
|
||||
function resolveArboriumWasmAliases() {
|
||||
const arbDir = path.join(ROOT_DIR, 'node_modules', '@arborium');
|
||||
const aliases = {};
|
||||
@@ -192,10 +378,12 @@ export default () => {
|
||||
const publicPath = isProduction ? productionPublicPath : developmentPublicPath;
|
||||
return {
|
||||
mode,
|
||||
entry: {
|
||||
main: path.join(SRC_DIR, 'index.tsx'),
|
||||
sw: path.join(SRC_DIR, 'features', 'platform', 'service_worker', 'Worker.ts'),
|
||||
},
|
||||
entry: isDesktopRenderer
|
||||
? {main: path.join(SRC_DIR, 'index.tsx')}
|
||||
: {
|
||||
main: path.join(SRC_DIR, 'index.tsx'),
|
||||
sw: path.join(SRC_DIR, 'features', 'platform', 'service_worker', 'Worker.ts'),
|
||||
},
|
||||
output: {
|
||||
path: DIST_DIR,
|
||||
publicPath,
|
||||
@@ -337,6 +525,15 @@ export default () => {
|
||||
type: 'css/module',
|
||||
parser: {namedExports: false, dashedIdents: false, grid: false, container: false},
|
||||
},
|
||||
{
|
||||
test: /[\\/]@phosphor-icons[\\/]react[\\/]dist[\\/]defs[\\/][^\\/]+\.es\.js$/,
|
||||
use: [{loader: path.join(ROOT_DIR, 'scripts/build/rspack/phosphor-unused-weights-loader.cjs')}],
|
||||
},
|
||||
{
|
||||
test: /[\\/]node_modules[\\/]katex[\\/]dist[\\/]katex(\.min)?\.css$/,
|
||||
use: [{loader: path.join(ROOT_DIR, 'scripts/build/rspack/katex-legacy-fonts-loader.cjs')}],
|
||||
type: 'css',
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /\.module\.css$/,
|
||||
@@ -411,6 +608,18 @@ export default () => {
|
||||
filename: isProduction ? 'assets/[contenthash:16][ext]' : 'assets/[name].[hash][ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
resourceQuery: DESKTOP_MODULE_ASSET_QUERY,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: (pathData) => {
|
||||
const moduleName = desktopModuleAssetName(pathData.filename ?? '');
|
||||
return isProduction
|
||||
? `assets/${moduleName}/[contenthash:16][ext]`
|
||||
: `assets/${moduleName}/[name].[hash][ext]`;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
generator: {
|
||||
'css/module': {
|
||||
@@ -427,6 +636,9 @@ export default () => {
|
||||
},
|
||||
plugins: [
|
||||
nodeAssertStrictSchemePlugin(),
|
||||
fontPreloadPlugin(),
|
||||
...(isProduction ? [bootPreloadPlugin()] : []),
|
||||
...(isProduction && !isDesktopRenderer ? [authEntryDocumentPlugin()] : []),
|
||||
new HtmlRspackPlugin({
|
||||
template: path.join(ROOT_DIR, 'index.html'),
|
||||
filename: 'index.html',
|
||||
@@ -434,6 +646,7 @@ export default () => {
|
||||
inject: 'body',
|
||||
scriptLoading: 'module',
|
||||
excludeChunks: ['sw'],
|
||||
...(isDesktopRenderer ? {publicPath: '/'} : {}),
|
||||
}),
|
||||
new CopyRspackPlugin({
|
||||
patterns: [
|
||||
@@ -442,6 +655,14 @@ export default () => {
|
||||
to: DIST_DIR,
|
||||
noErrorOnMissing: true,
|
||||
},
|
||||
...(isDesktopRenderer
|
||||
? [
|
||||
{
|
||||
from: path.join(MONOREPO_ROOT, 'fluxer_static', 'web'),
|
||||
to: path.join(DIST_DIR, 'web'),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
}),
|
||||
staticFilesPlugin({
|
||||
@@ -457,13 +678,7 @@ export default () => {
|
||||
'import.meta.env.PROD': JSON.stringify(isProduction),
|
||||
'import.meta.env.MODE': JSON.stringify(mode),
|
||||
'import.meta.env.PUBLIC_BUILD_VERSION': getPublicEnvVar(publicValues, 'PUBLIC_BUILD_VERSION'),
|
||||
'import.meta.env.PUBLIC_API_VERSION': getPublicEnvVar(publicValues, 'PUBLIC_API_VERSION'),
|
||||
'import.meta.env.PUBLIC_RELEASE_CHANNEL': getPublicEnvVar(publicValues, 'PUBLIC_RELEASE_CHANNEL'),
|
||||
'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',
|
||||
),
|
||||
}),
|
||||
{
|
||||
apply(compiler) {
|
||||
@@ -482,7 +697,8 @@ export default () => {
|
||||
? {
|
||||
chunks: (chunk) => isMainRuntimeChunk(chunk),
|
||||
maxInitialRequests: 15,
|
||||
cacheGroups: {
|
||||
maxAsyncSize: 2_000_000,
|
||||
cacheGroups: mirrorCacheGroups({
|
||||
icons: {
|
||||
test: /[\\/]node_modules[\\/]@phosphor-icons[\\/]/,
|
||||
name: 'icons',
|
||||
@@ -497,8 +713,24 @@ export default () => {
|
||||
enforce: true,
|
||||
chunks: (chunk) => !chunk.canBeInitial() && !isWorkerPath({chunk}),
|
||||
},
|
||||
tts: {
|
||||
test: /[\\/]node_modules[\\/]mespeak[\\/]/,
|
||||
name: 'tts',
|
||||
priority: 54,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
chunks: (chunk) => !chunk.canBeInitial() && !isWorkerPath({chunk}),
|
||||
},
|
||||
editor: {
|
||||
test: /[\\/]node_modules[\\/](@codemirror|@lezer|codemirror|style-mod|w3c-keyname|crelt|@marijn)[\\/]/,
|
||||
name: 'editor',
|
||||
priority: 53,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
chunks: (chunk) => !chunk.canBeInitial() && !isWorkerPath({chunk}),
|
||||
},
|
||||
livekit: {
|
||||
test: /[\\/]node_modules[\\/](livekit-client|@livekit)[\\/]/,
|
||||
test: /[\\/](node_modules|pkgs)[\\/](livekit-client|@livekit)[\\/]/,
|
||||
name: 'livekit',
|
||||
priority: 50,
|
||||
reuseExistingChunk: true,
|
||||
@@ -511,7 +743,7 @@ export default () => {
|
||||
maxSize: 100_000,
|
||||
},
|
||||
animation: {
|
||||
test: /[\\/]node_modules[\\/](framer-motion|motion)[\\/]/,
|
||||
test: /[\\/]node_modules[\\/](framer-motion|motion|motion-dom|motion-utils)[\\/]/,
|
||||
name: 'animation',
|
||||
priority: 45,
|
||||
reuseExistingChunk: true,
|
||||
@@ -529,8 +761,15 @@ export default () => {
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
colorPicker: {
|
||||
test: /[\\/]node_modules[\\/](@react-aria[\\/]color|@react-stately[\\/]color)[\\/]/,
|
||||
name: 'color-picker',
|
||||
priority: 42,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
reactAria: {
|
||||
test: /[\\/]node_modules[\\/]react-aria-components[\\/]/,
|
||||
test: /[\\/]node_modules[\\/](react-aria-components|@react-aria|@react-stately|@internationalized)[\\/]/,
|
||||
name: 'react-aria',
|
||||
priority: 41,
|
||||
reuseExistingChunk: true,
|
||||
@@ -578,7 +817,7 @@ export default () => {
|
||||
reuseExistingChunk: true,
|
||||
},
|
||||
utils: {
|
||||
test: /[\\/]node_modules[\\/](lodash|clsx|qrcode|thumbhash|bowser|match-sorter)[\\/]/,
|
||||
test: /[\\/]node_modules[\\/](lodash|clsx|thumbhash|match-sorter)[\\/]/,
|
||||
name: 'utils',
|
||||
priority: 28,
|
||||
reuseExistingChunk: true,
|
||||
@@ -595,6 +834,62 @@ export default () => {
|
||||
priority: 25,
|
||||
reuseExistingChunk: true,
|
||||
},
|
||||
platform: {
|
||||
test: /[\\/]node_modules[\\/]bowser[\\/]/,
|
||||
name: 'platform',
|
||||
priority: 27,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
phone: {
|
||||
test: /[\\/]node_modules[\\/]libphonenumber-js[\\/]/,
|
||||
name: 'phone',
|
||||
priority: 26,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
qrcode: {
|
||||
test: /[\\/]node_modules[\\/](qrcode|dijkstrajs)[\\/]/,
|
||||
name: 'qrcode',
|
||||
priority: 24,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
lexical: {
|
||||
test: /[\\/]node_modules[\\/](lexical|@lexical)[\\/]/,
|
||||
name: 'lexical',
|
||||
priority: 23,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
calendar: {
|
||||
test: /[\\/]node_modules[\\/](react-day-picker|date-fns|@date-fns)[\\/]/,
|
||||
name: 'calendar',
|
||||
priority: 22,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
webrtc: {
|
||||
test: /[\\/]node_modules[\\/](webrtc-adapter|sdp|sdp-transform)[\\/]/,
|
||||
name: 'webrtc',
|
||||
priority: 21,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
noiseFilter: {
|
||||
test: /[\\/]node_modules[\\/]deepfilternet3-noise-filter[\\/]/,
|
||||
name: 'noise-filter',
|
||||
priority: 20,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
virtualizer: {
|
||||
test: /[\\/]node_modules[\\/]@tanstack[\\/]/,
|
||||
name: 'virtualizer',
|
||||
priority: 19,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true,
|
||||
},
|
||||
vendor: {
|
||||
test: (module) => {
|
||||
if (!module.resource) return false;
|
||||
@@ -607,7 +902,7 @@ export default () => {
|
||||
priority: 10,
|
||||
reuseExistingChunk: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
: false,
|
||||
runtimeChunk: false,
|
||||
|
||||
Reference in new issue
Block a user