Files
chenlin deead17a9a
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
feat(training): release V0.8 自调参 Agent
2026-09-02 13:49:34 +08:00

123 lines
3.8 KiB
TypeScript

import { readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { defineConfig } from 'vitest/config';
import type { Plugin } from 'vite';
import react from '@vitejs/plugin-react';
const PYODIDE_ASSETS = [
'pyodide.asm.js',
'pyodide.asm.wasm',
'python_stdlib.zip',
'pyodide-lock.json',
] as const;
const pyodideDirectory = resolve(
dirname(fileURLToPath(import.meta.url)),
'../node_modules/pyodide',
);
/** 生产预览中长期缓存带内容哈希的资源,同时允许入口文件及时更新。 */
function previewCacheHeaders(): Plugin {
return {
name: 'preview-cache-headers',
configurePreviewServer(server) {
server.middlewares.use((request, response, next) => {
const path = request.url?.split(/[?#]/, 1)[0] ?? '';
if (path.startsWith('/assets/'))
response.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
else if (path.startsWith('/pyodide/'))
response.setHeader(
'Cache-Control',
'public, max-age=86400, stale-while-revalidate=604800',
);
else response.setHeader('Cache-Control', 'no-cache');
next();
});
},
};
}
/** 让开发服务器与生产构建都从本地 npm 包提供 Pyodide,保持平台可离线部署。 */
function localPyodideAssets(): Plugin {
let isBuild = false;
return {
name: 'local-pyodide-assets',
configResolved(config) {
isBuild = config.command === 'build';
},
configureServer(server) {
server.middlewares.use((request, response, next) => {
const name = request.url?.split(/[?#]/, 1)[0].replace(/^\/pyodide\//, '');
if (!name || !PYODIDE_ASSETS.includes(name as (typeof PYODIDE_ASSETS)[number])) {
next();
return;
}
response.setHeader(
'Content-Type',
name.endsWith('.wasm')
? 'application/wasm'
: name.endsWith('.zip')
? 'application/zip'
: name.endsWith('.json')
? 'application/json'
: 'text/javascript',
);
response.end(readFileSync(resolve(pyodideDirectory, name)));
});
},
buildStart() {
if (isBuild)
for (const name of PYODIDE_ASSETS)
this.emitFile({
type: 'asset',
fileName: `pyodide/${name}`,
source: readFileSync(resolve(pyodideDirectory, name)),
});
},
};
}
export default defineConfig({
root: 'web_platform',
base: './',
plugins: [react(), localPyodideAssets(), previewCacheHeaders()],
publicDir: 'public',
build: {
outDir: '../web-platform-dist',
emptyOutDir: true,
target: 'es2022',
rollupOptions: {
input: {
main: resolve(dirname(fileURLToPath(import.meta.url)), 'index.html'),
tuning: resolve(dirname(fileURLToPath(import.meta.url)), 'tuning.html'),
},
},
modulePreload: { polyfill: false },
},
worker: { format: 'es' },
server: { open: true, fs: { allow: ['..'] } },
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
include: ['src/**/*.test.ts', 'src/**/*.test.tsx'],
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'json-summary'],
include: [
'src/project/cachedFiles.ts',
'src/project/importer.ts',
'src/project/urdfToMjcf.ts',
'src/project/workspace.ts',
'src/simulation/geometry.ts',
'src/stores/useAppStore.ts',
'src/training/LocalTrainingClient.ts',
'src/viewer/interactionMath.ts',
'src/viewer/texturePixels.ts',
'src/viewer/visualizationMath.ts',
],
thresholds: { lines: 85, functions: 75, branches: 70, statements: 80 },
},
},
});