663 lines
20 KiB
TypeScript
663 lines
20 KiB
TypeScript
import { rmSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
import tailwindcss from '@tailwindcss/vite';
|
|
import react from '@vitejs/plugin-react';
|
|
import postcss from 'postcss';
|
|
import { defineConfig, loadEnv, type Plugin } from 'vite';
|
|
|
|
const RETIRED_TEMPLATE_CSS_PATTERN =
|
|
/(?:baby-object|bark-battle|big-fish|child-motion|creation-(?:agent|work)|creative-agent|custom-world|fusion-pixel|jump-hop|match3d|pixel|platform-recommend|platform-work-detail|public-work|puzzle|rpg|square-hole|story-top-tabs|unified-creation|visual-novel|wooden-fish)/iu;
|
|
|
|
const RETIRED_COMPONENT_MODULE_DIRECTORIES = new Set([
|
|
'asset-studio',
|
|
'bark-battle-creation',
|
|
'big-fish-creation',
|
|
'big-fish-result',
|
|
'big-fish-runtime',
|
|
'child-motion-demo',
|
|
'creation-agent',
|
|
'creative-agent',
|
|
'custom-world-agent',
|
|
'custom-world-home',
|
|
'edutainment-creation',
|
|
'edutainment-result',
|
|
'edutainment-runtime',
|
|
'game-canvas',
|
|
'jump-hop-result',
|
|
'jump-hop-runtime',
|
|
'match3d-result',
|
|
'match3d-runtime',
|
|
'puzzle-clear-creation',
|
|
'puzzle-clear-result',
|
|
'puzzle-clear-runtime',
|
|
'puzzle-gallery',
|
|
'puzzle-result',
|
|
'puzzle-runtime',
|
|
'rpg-creation-asset-studio',
|
|
'rpg-creation-editor',
|
|
'rpg-creation-result',
|
|
'rpg-entry',
|
|
'rpg-runtime-panels',
|
|
'rpg-runtime-shell',
|
|
'square-hole-creation',
|
|
'square-hole-result',
|
|
'square-hole-runtime',
|
|
'unified-creation',
|
|
'visual-novel-creation',
|
|
'visual-novel-result',
|
|
'visual-novel-runtime',
|
|
'wooden-fish-result',
|
|
'wooden-fish-runtime',
|
|
]);
|
|
|
|
const ACTIVE_TOP_LEVEL_SOURCE_MODULES = new Map<string, ReadonlySet<string>>([
|
|
[
|
|
'components',
|
|
new Set([
|
|
'ResolvedAssetAudio.tsx',
|
|
'ResolvedAssetImage.tsx',
|
|
'ResolvedAssetVideo.tsx',
|
|
]),
|
|
],
|
|
[
|
|
'hooks',
|
|
new Set([
|
|
'useGameSettings.ts',
|
|
'useHostNavigationCanGoBack.ts',
|
|
'useResolvedAssetReadUrl.ts',
|
|
]),
|
|
],
|
|
['persistence', new Set(['gameSettingsStorage.ts', 'storage.ts'])],
|
|
['routing', new Set(['activeAppPageRoutes.ts', 'activeAppRoutes.tsx'])],
|
|
[
|
|
'services',
|
|
new Set([
|
|
'activeAppTitle.ts',
|
|
'apiClient.ts',
|
|
'assetReadUrlService.ts',
|
|
'authService.ts',
|
|
'clipboard.ts',
|
|
'frontendRuntimeConfigService.ts',
|
|
'sseStream.ts',
|
|
]),
|
|
],
|
|
]);
|
|
|
|
const RETIRED_SERVICE_MODULE_DIRECTORIES = new Set([
|
|
'bark-battle-creation',
|
|
'bark-battle-runtime',
|
|
'big-fish-creation',
|
|
'big-fish-gallery',
|
|
'big-fish-runtime',
|
|
'big-fish-works',
|
|
'child-motion-demo',
|
|
'creation-agent',
|
|
'creation-audio',
|
|
'creative-agent',
|
|
'edutainment-baby-drawing',
|
|
'edutainment-baby-object',
|
|
'input-devices',
|
|
'jump-hop',
|
|
'match3d-creation',
|
|
'match3d-runtime',
|
|
'match3d-works',
|
|
'puzzle-agent',
|
|
'puzzle-clear',
|
|
'puzzle-gallery',
|
|
'puzzle-onboarding',
|
|
'puzzle-runtime',
|
|
'puzzle-works',
|
|
'rpg-creation',
|
|
'rpg-entry',
|
|
'rpg-runtime',
|
|
'square-hole-creation',
|
|
'square-hole-runtime',
|
|
'square-hole-works',
|
|
'storyEngine',
|
|
'visual-novel-creation',
|
|
'visual-novel-runtime',
|
|
'visual-novel-works',
|
|
'wooden-fish',
|
|
]);
|
|
|
|
const RETIRED_HOOK_MODULE_DIRECTORIES = new Set([
|
|
'combat',
|
|
'rpg-runtime-story',
|
|
'rpg-session',
|
|
]);
|
|
|
|
const RETIRED_SOURCE_MODULE_DIRECTORIES = new Set(['data', 'games', 'prompts']);
|
|
|
|
const RETIRED_PLATFORM_ENTRY_MODULE_PATTERN =
|
|
/\/(?:Platform(?:Draft|EntryCreation|EntryFlowShellImpl|EntryHome|EntryWorld|Error|MobileHome|Profile(?:Generation|Played|Qr|Task)|Task|Work)|barkBattle|platform(?:Creation|Dialog|Draft|Edutainment|EntryCreation|External|Generation|Host|MiniGame|Played|Public|Puzzle|Recommend|Rpg|Selection)|puzzleDraft|usePlatform(?:Creation|Entry))[^/]*\.(?:ts|tsx)$/u;
|
|
|
|
const RETIRED_STANDALONE_MODULE_PATHS = [
|
|
'/src/App.test.tsx',
|
|
'/src/App.tsx',
|
|
'/src/ChildMotionDemoApp.tsx',
|
|
'/src/RpgRuntimeApp.tsx',
|
|
'/src/hooks/useCombatFlow.ts',
|
|
'/src/hooks/useStoryOptions.ts',
|
|
'/src/main.tsx',
|
|
'/src/routing/appPageRoutes.ts',
|
|
'/src/routing/appRoutes.tsx',
|
|
'/src/services/ai.ts',
|
|
'/src/services/appTitle.ts',
|
|
'/src/services/creationEntryConfigService.ts',
|
|
'/src/services/creationUrlState.ts',
|
|
'/src/services/runtimeGuestAuth.ts',
|
|
'/src/services/runtimeRequest.ts',
|
|
'/src/services/publicWorkCode.ts',
|
|
'/src/services/runtimeAudioFeedback.ts',
|
|
'/src/services/useMocapInput.ts',
|
|
'/src/services/wechatMiniProgramSubscribe.ts',
|
|
'/src/types.ts',
|
|
'/src/uiAssets.ts',
|
|
] as const;
|
|
|
|
const RETIRED_STANDALONE_MODULE_PATTERNS = [
|
|
/\/src\/[^/]*PlaygroundApp\.tsx$/u,
|
|
/\/src\/components\/common\/(?:PublishShare|publishShare)[^/]*\.(?:ts|tsx)$/u,
|
|
/\/src\/components\/platform-entry\/PlatformEntryFlowShellImpl\//u,
|
|
/\/src\/components\/platform-entry\/platform(?:EntryTypes|Match3DRuntimeProfile)[^/]*\.(?:ts|tsx)$/u,
|
|
/\/src\/services\/customWorld[^/]*\.(?:ts|tsx)$/u,
|
|
] as const;
|
|
|
|
const ACTIVE_TAILWIND_SOURCES = `@import 'tailwindcss' source(none);
|
|
@source "./active-main.tsx";
|
|
@source "./ActiveApp.tsx";
|
|
@source "./AuthenticatedApp.tsx";
|
|
@source "./components/auth";
|
|
@source "./components/common";
|
|
@source "./components/creation-home";
|
|
@source "./components/image-editor";
|
|
@source "./components/project";
|
|
@source "./components/platform-entry/PlatformEntryFlowShell.tsx";
|
|
@source "./components/platform-entry/PlatformEntryActiveFlowShell.tsx";
|
|
@source "./components/platform-entry/PlatformActiveProfileView.tsx";
|
|
@source "./components/platform-entry/PlatformProfilePrimitives.tsx";
|
|
@source "./components/platform-entry/PlatformProfileApiKeysModal.tsx";
|
|
@source "./components/platform-entry/PlatformProfileModalShell.tsx";
|
|
@source "./components/platform-entry/PlatformProfileRechargeModal.tsx";
|
|
@source "./components/platform-entry/PlatformProfileReferralModal.tsx";
|
|
@source "./components/platform-entry/PlatformProfileRewardCodeRedeemModal.tsx";
|
|
@source "./components/platform-entry/PlatformProfileWalletLedgerModal.tsx";
|
|
@source "./components/platform-entry/PlatformRechargePaymentStatusDialogs.tsx";
|
|
@source "./editor";
|
|
@source "../packages/shared/src/components";`;
|
|
|
|
const RETIRED_PUBLIC_ASSET_PATHS = [
|
|
'/audio',
|
|
'/branding/jump-hop-taonier-character.png',
|
|
'/anthro-cat-illustrations',
|
|
'/bark-battle-assets',
|
|
'/character',
|
|
'/child-motion-demo',
|
|
'/creation-type-references',
|
|
'/edutainment-baby-object',
|
|
'/fusion-pixel.ttf',
|
|
'/generated-animations',
|
|
'/generated-bark-battle-assets',
|
|
'/generated-big-fish-assets',
|
|
'/generated-character-drafts',
|
|
'/generated-characters',
|
|
'/generated-custom-world-covers',
|
|
'/generated-custom-world-scenes',
|
|
'/generated-puzzle-assets',
|
|
'/generated-qwen-sprites',
|
|
'/Icons',
|
|
'/chat.png',
|
|
'/creation-type-references/jump-hop.webp',
|
|
'/creation-type-references/match3d.webp',
|
|
'/match3d-background-references',
|
|
'/match3d-style-references',
|
|
'/Pixel Monsters Vol1',
|
|
'/puzzle-creation-templates',
|
|
'/scene_bg',
|
|
'/UI',
|
|
'/ui-previews',
|
|
'/visual-novel-style-references',
|
|
'/wooden-fish',
|
|
] as const;
|
|
|
|
const RETIRED_API_PATHS = [
|
|
'/api/creation',
|
|
'/api/creation-entry',
|
|
'/api/public-works',
|
|
] as const;
|
|
|
|
function normalizeViteModuleId(id: string) {
|
|
return id.split('?', 1)[0]?.replaceAll('\\', '/') ?? id;
|
|
}
|
|
|
|
const ROOT_SOURCE_DIRECTORY = normalizeViteModuleId(
|
|
path.resolve(__dirname, 'src'),
|
|
);
|
|
|
|
function readTopLevelSourceDirectory(
|
|
normalizedId: string,
|
|
sourceKind: 'components' | 'hooks' | 'services',
|
|
) {
|
|
const marker = `/src/${sourceKind}/`;
|
|
const markerIndex = normalizedId.lastIndexOf(marker);
|
|
if (markerIndex < 0) {
|
|
return null;
|
|
}
|
|
return (
|
|
normalizedId.slice(markerIndex + marker.length).split('/', 1)[0] ?? null
|
|
);
|
|
}
|
|
|
|
function isRetiredTopLevelSourceModule(normalizedId: string) {
|
|
const marker = '/src/';
|
|
const markerIndex = normalizedId.lastIndexOf(marker);
|
|
if (markerIndex < 0) {
|
|
return false;
|
|
}
|
|
const relativePath = normalizedId.slice(markerIndex + marker.length);
|
|
const segments = relativePath.split('/');
|
|
if (segments.length !== 2) {
|
|
return false;
|
|
}
|
|
const [directory, fileName] = segments;
|
|
const activeModules = directory
|
|
? ACTIVE_TOP_LEVEL_SOURCE_MODULES.get(directory)
|
|
: undefined;
|
|
return Boolean(activeModules && fileName && !activeModules.has(fileName));
|
|
}
|
|
|
|
function readSourceDirectory(normalizedId: string) {
|
|
const marker = '/src/';
|
|
const markerIndex = normalizedId.lastIndexOf(marker);
|
|
if (markerIndex < 0) {
|
|
return null;
|
|
}
|
|
return (
|
|
normalizedId.slice(markerIndex + marker.length).split('/', 1)[0] ?? null
|
|
);
|
|
}
|
|
|
|
export function isRetiredFrontendModuleId(id: string) {
|
|
const normalizedId = normalizeViteModuleId(id);
|
|
if (!normalizedId.startsWith(`${ROOT_SOURCE_DIRECTORY}/`)) {
|
|
return false;
|
|
}
|
|
const sourceDirectory = readSourceDirectory(normalizedId);
|
|
if (
|
|
isRetiredTopLevelSourceModule(normalizedId) ||
|
|
(sourceDirectory &&
|
|
RETIRED_SOURCE_MODULE_DIRECTORIES.has(sourceDirectory)) ||
|
|
normalizedId.includes('/src/types/') ||
|
|
RETIRED_STANDALONE_MODULE_PATHS.some((path) =>
|
|
normalizedId.endsWith(path),
|
|
) ||
|
|
RETIRED_STANDALONE_MODULE_PATTERNS.some((pattern) =>
|
|
pattern.test(normalizedId),
|
|
)
|
|
) {
|
|
return true;
|
|
}
|
|
const componentDirectory = readTopLevelSourceDirectory(
|
|
normalizedId,
|
|
'components',
|
|
);
|
|
if (
|
|
componentDirectory &&
|
|
RETIRED_COMPONENT_MODULE_DIRECTORIES.has(componentDirectory)
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
const serviceDirectory = readTopLevelSourceDirectory(
|
|
normalizedId,
|
|
'services',
|
|
);
|
|
if (
|
|
serviceDirectory &&
|
|
RETIRED_SERVICE_MODULE_DIRECTORIES.has(serviceDirectory)
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
const hookDirectory = readTopLevelSourceDirectory(normalizedId, 'hooks');
|
|
if (hookDirectory && RETIRED_HOOK_MODULE_DIRECTORIES.has(hookDirectory)) {
|
|
return true;
|
|
}
|
|
|
|
return RETIRED_PLATFORM_ENTRY_MODULE_PATTERN.test(normalizedId);
|
|
}
|
|
|
|
function retiredCreationTemplateModulesPlugin(): Plugin {
|
|
return {
|
|
name: 'retired-creation-template-modules',
|
|
enforce: 'pre',
|
|
transform(_code, id) {
|
|
if (!isRetiredFrontendModuleId(id)) {
|
|
return null;
|
|
}
|
|
throw new Error(
|
|
`退役创作模板模块不得进入现役 Vite 依赖图:${normalizeViteModuleId(id)}`,
|
|
);
|
|
},
|
|
};
|
|
}
|
|
|
|
export function isForbiddenPublicAssetPath(pathname: string) {
|
|
return (
|
|
pathname.startsWith('/generated-') ||
|
|
RETIRED_PUBLIC_ASSET_PATHS.some(
|
|
(prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`),
|
|
)
|
|
);
|
|
}
|
|
|
|
export function isRetiredApiPath(pathname: string) {
|
|
return RETIRED_API_PATHS.some(
|
|
(prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`),
|
|
);
|
|
}
|
|
|
|
function retiredCreationTemplateAssetsPlugin(): Plugin {
|
|
return {
|
|
name: 'retired-creation-template-assets',
|
|
configureServer(server) {
|
|
server.middlewares.use((request, response, next) => {
|
|
const pathname = new URL(request.url ?? '/', 'http://localhost')
|
|
.pathname;
|
|
if (
|
|
!isForbiddenPublicAssetPath(pathname) &&
|
|
!isRetiredApiPath(pathname)
|
|
) {
|
|
next();
|
|
return;
|
|
}
|
|
response.statusCode = 404;
|
|
response.end();
|
|
});
|
|
},
|
|
writeBundle(options) {
|
|
const outputDir = path.resolve(__dirname, options.dir ?? 'dist');
|
|
for (const assetPath of RETIRED_PUBLIC_ASSET_PATHS) {
|
|
rmSync(path.join(outputDir, assetPath.slice(1)), {
|
|
force: true,
|
|
recursive: true,
|
|
});
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
function retiredCreationTemplateCssPlugin(): Plugin {
|
|
const stripRetiredCss = (code: string, from: string) => {
|
|
const root = postcss.parse(code, { from });
|
|
root.walkRules((rule) => {
|
|
if (RETIRED_TEMPLATE_CSS_PATTERN.test(rule.selector)) {
|
|
rule.remove();
|
|
}
|
|
});
|
|
root.walkAtRules((atRule) => {
|
|
if (
|
|
(atRule.name === 'keyframes' || atRule.name === 'source') &&
|
|
RETIRED_TEMPLATE_CSS_PATTERN.test(atRule.params)
|
|
) {
|
|
atRule.remove();
|
|
}
|
|
});
|
|
root.walkDecls((declaration) => {
|
|
if (
|
|
RETIRED_TEMPLATE_CSS_PATTERN.test(declaration.prop) ||
|
|
RETIRED_TEMPLATE_CSS_PATTERN.test(declaration.value)
|
|
) {
|
|
declaration.remove();
|
|
}
|
|
});
|
|
return root.toString();
|
|
};
|
|
|
|
return {
|
|
name: 'retired-creation-template-css',
|
|
enforce: 'pre',
|
|
transform(code, id) {
|
|
if (
|
|
!id.endsWith('/src/index.css') &&
|
|
!id.endsWith('/packages/shared/src/theme.css')
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
const activeSourceCode = id.endsWith('/src/index.css')
|
|
? code.replace("@import 'tailwindcss';", ACTIVE_TAILWIND_SOURCES)
|
|
: code;
|
|
return { code: stripRetiredCss(activeSourceCode, id), map: null };
|
|
},
|
|
generateBundle(_options, bundle) {
|
|
Object.values(bundle).forEach((entry) => {
|
|
if (entry.type !== 'asset' || !entry.fileName.endsWith('.css')) {
|
|
return;
|
|
}
|
|
entry.source = stripRetiredCss(
|
|
typeof entry.source === 'string'
|
|
? entry.source
|
|
: new TextDecoder().decode(entry.source),
|
|
entry.fileName,
|
|
);
|
|
});
|
|
},
|
|
};
|
|
}
|
|
|
|
export default defineConfig(({ mode }) => {
|
|
const env = loadEnv(mode, __dirname, '');
|
|
const ignoredWatchGlobs = [
|
|
// Prevent a root-level nohup log from triggering a Vite reload loop.
|
|
'**/nohup.out',
|
|
'**/.git/**',
|
|
'**/.worktrees/**',
|
|
'**/dist/**',
|
|
'**/dist_check/**',
|
|
'**/dist_check_final/**',
|
|
'**/dist_check_monster_position/**',
|
|
'**/temp*build*/**',
|
|
'**/public/generated-character-drafts/**',
|
|
'**/public/generated-characters/**',
|
|
'**/public/generated-custom-world-scenes/**',
|
|
'**/public/generated-qwen-sprites/**',
|
|
'**/public/child-motion-demo/**',
|
|
'**/public/anthro-cat-illustrations/**',
|
|
'**/public/bark-battle-assets/**',
|
|
'**/public/character/**',
|
|
'**/public/creation-type-references/**',
|
|
'**/public/edutainment-baby-object/**',
|
|
'**/public/match3d-background-references/**',
|
|
'**/public/match3d-style-references/**',
|
|
'**/public/puzzle-creation-templates/**',
|
|
'**/public/visual-novel-style-references/**',
|
|
'**/public/wooden-fish/**',
|
|
'**/src/components/child-motion-demo/**',
|
|
'**/src/components/asset-studio/**',
|
|
'**/src/components/bark-battle-creation/**',
|
|
'**/src/components/big-fish-*/**',
|
|
'**/src/components/creation-agent/**',
|
|
'**/src/components/creative-agent/**',
|
|
'**/src/components/custom-world-*/**',
|
|
'**/src/components/edutainment-*/**',
|
|
'**/src/ChildMotionDemoApp.tsx',
|
|
'**/src/Match3DPlaygroundApp.tsx',
|
|
'**/src/components/jump-hop-result/**',
|
|
'**/src/components/jump-hop-runtime/**',
|
|
'**/src/components/match3d-result/**',
|
|
'**/src/components/match3d-runtime/**',
|
|
'**/src/components/puzzle-*/**',
|
|
'**/src/components/rpg-entry/**',
|
|
'**/src/components/rpg-creation-*/**',
|
|
'**/src/components/rpg-runtime-*/**',
|
|
'**/src/components/square-hole-*/**',
|
|
'**/src/components/unified-creation/**',
|
|
'**/src/components/visual-novel-*/**',
|
|
'**/src/components/wooden-fish-*/**',
|
|
'**/src/components/platform-entry/platformMatch3DRuntimeProfile*',
|
|
'**/src/components/unified-creation/workspaces/JumpHopCreationWorkspace*',
|
|
'**/src/components/unified-creation/workspaces/Match3DCreationWorkspace*',
|
|
'**/src/services/child-motion-demo/**',
|
|
'**/src/services/bark-battle-*/**',
|
|
'**/src/services/big-fish-*/**',
|
|
'**/src/services/creation-*/**',
|
|
'**/src/services/creative-agent/**',
|
|
'**/src/services/edutainment-*/**',
|
|
'**/src/services/jump-hop/**',
|
|
'**/src/services/match3d-creation/**',
|
|
'**/src/services/match3d-runtime/**',
|
|
'**/src/services/match3d-works/**',
|
|
'**/src/services/match3dGeneratedModelCache*',
|
|
'**/src/services/match3dSpritesheetParser*',
|
|
'**/src/services/puzzle-*/**',
|
|
'**/src/services/rpg-entry/**',
|
|
'**/src/services/rpg-creation/**',
|
|
'**/src/services/rpg-runtime/**',
|
|
'**/src/services/square-hole-*/**',
|
|
'**/src/services/visual-novel-*/**',
|
|
'**/src/services/wooden-fish/**',
|
|
'**/backend-rewrite-tasklist/**',
|
|
'**/docs/**',
|
|
'**/jenkins/**',
|
|
'**/media/**',
|
|
'**/scripts/**',
|
|
'**/server-rs/**',
|
|
'**/server-rs/target/**',
|
|
'**/*.test.ts',
|
|
'**/*.test.tsx',
|
|
'**/*.spec.ts',
|
|
'**/*.spec.tsx',
|
|
];
|
|
const rustServerTarget =
|
|
env.RUST_SERVER_TARGET ||
|
|
env.GENARRATIVE_API_TARGET ||
|
|
`http://127.0.0.1:${env.GENARRATIVE_API_PORT || '3100'}`;
|
|
const runtimeServerTarget =
|
|
env.GENARRATIVE_RUNTIME_SERVER_TARGET || rustServerTarget;
|
|
const adminWebTarget =
|
|
env.ADMIN_WEB_TARGET || `http://127.0.0.1:${env.ADMIN_WEB_PORT || '3102'}`;
|
|
|
|
return {
|
|
root: __dirname,
|
|
envDir: __dirname,
|
|
plugins: [
|
|
retiredCreationTemplateModulesPlugin(),
|
|
retiredCreationTemplateCssPlugin(),
|
|
react(),
|
|
tailwindcss(),
|
|
retiredCreationTemplateAssetsPlugin(),
|
|
],
|
|
define: {
|
|
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
|
},
|
|
resolve: {
|
|
alias: [
|
|
{
|
|
find: /^@cubone\/react-file-manager$/,
|
|
replacement: path.resolve(
|
|
__dirname,
|
|
'apps/ai-game-creator-shell/node_modules/@cubone/react-file-manager/dist/react-file-manager.es.js',
|
|
),
|
|
},
|
|
{ find: '@', replacement: path.resolve(__dirname, '.') },
|
|
{
|
|
find: '@genarrative/image-canvas-core',
|
|
replacement: path.resolve(
|
|
__dirname,
|
|
'packages/image-canvas-core/src/index.ts',
|
|
),
|
|
},
|
|
{
|
|
find: '@genarrative/image-canvas-react/styles.css',
|
|
replacement: path.resolve(
|
|
__dirname,
|
|
'packages/image-canvas-react/src/styles.css',
|
|
),
|
|
},
|
|
{
|
|
find: '@genarrative/image-canvas-react',
|
|
replacement: path.resolve(
|
|
__dirname,
|
|
'packages/image-canvas-react/src/index.ts',
|
|
),
|
|
},
|
|
],
|
|
dedupe: ['react', 'react-dom'],
|
|
},
|
|
optimizeDeps: {
|
|
// Only crawl the real app entry so temporary build folders do not get
|
|
// treated as extra HTML entrypoints during dependency scanning.
|
|
entries: ['index.html'],
|
|
},
|
|
build: {
|
|
chunkSizeWarningLimit: 1000,
|
|
rollupOptions: {
|
|
output: {
|
|
manualChunks(id) {
|
|
if (id.includes('/node_modules/motion')) {
|
|
return 'motion-vendor';
|
|
}
|
|
if (id.includes('/src/services/')) {
|
|
return 'app-services';
|
|
}
|
|
},
|
|
},
|
|
},
|
|
},
|
|
server: {
|
|
allowedHosts: ['outfield-outlook-reprocess.ngrok-free.dev'],
|
|
// HMR is disabled in AI Studio via DISABLE_HMR env var.
|
|
// Do not modify; file watching is disabled to prevent flickering during agent edits.
|
|
hmr: process.env.DISABLE_HMR !== 'true',
|
|
proxy: {
|
|
'/admin/': {
|
|
target: adminWebTarget,
|
|
changeOrigin: true,
|
|
secure: false,
|
|
},
|
|
'/api/auth': {
|
|
target: runtimeServerTarget,
|
|
changeOrigin: true,
|
|
secure: false,
|
|
},
|
|
'/api/profile': {
|
|
target: runtimeServerTarget,
|
|
changeOrigin: true,
|
|
secure: false,
|
|
},
|
|
'/api/runtime': {
|
|
target: runtimeServerTarget,
|
|
changeOrigin: true,
|
|
secure: false,
|
|
},
|
|
'/api/editor': {
|
|
target: runtimeServerTarget,
|
|
changeOrigin: true,
|
|
secure: false,
|
|
},
|
|
'/api/assets': {
|
|
target: runtimeServerTarget,
|
|
changeOrigin: true,
|
|
secure: false,
|
|
},
|
|
'/api/llm': {
|
|
target: runtimeServerTarget,
|
|
changeOrigin: true,
|
|
secure: false,
|
|
},
|
|
'/api/ws': {
|
|
target: runtimeServerTarget,
|
|
changeOrigin: true,
|
|
secure: false,
|
|
ws: true,
|
|
},
|
|
},
|
|
watch: {
|
|
ignored: ignoredWatchGlobs,
|
|
},
|
|
},
|
|
};
|
|
});
|