Feature/editor optimization (#251)
* refactor: 编辑器/运行时架构拆分与构建系统升级 * feat(core): 层级系统重构与UI变换矩阵修复 * refactor: 移除 ecs-components 聚合包并修复跨包组件查找问题 * fix(physics): 修复跨包组件类引用问题 * feat: 统一运行时架构与浏览器运行支持 * feat(asset): 实现浏览器运行时资产加载系统 * fix: 修复文档、CodeQL安全问题和CI类型检查错误 * fix: 修复文档、CodeQL安全问题和CI类型检查错误 * fix: 修复文档、CodeQL安全问题、CI类型检查和测试错误 * test: 补齐核心模块测试用例,修复CI构建配置 * fix: 修复测试用例中的类型错误和断言问题 * fix: 修复 turbo build:npm 任务的依赖顺序问题 * fix: 修复 CI 构建错误并优化构建性能
This commit is contained in:
90
packages/build-config/src/index.ts
Normal file
90
packages/build-config/src/index.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @esengine/build-config
|
||||
*
|
||||
* 统一构建配置包,提供标准化的 Vite 配置预设和共享插件
|
||||
* Unified build configuration with standardized Vite presets and shared plugins
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 1. 纯运行时包 (core, math, components)
|
||||
* import { runtimeOnlyPreset } from '@esengine/build-config/presets';
|
||||
* export default runtimeOnlyPreset({ root: __dirname });
|
||||
*
|
||||
* // 2. 插件包 (ui, tilemap, behavior-tree)
|
||||
* import { pluginPreset } from '@esengine/build-config/presets';
|
||||
* export default pluginPreset({
|
||||
* root: __dirname,
|
||||
* hasCSS: true
|
||||
* });
|
||||
*
|
||||
* // 3. 纯编辑器包 (editor-core, node-editor)
|
||||
* import { editorOnlyPreset } from '@esengine/build-config/presets';
|
||||
* export default editorOnlyPreset({
|
||||
* root: __dirname,
|
||||
* hasReact: true
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ## 包类型说明
|
||||
*
|
||||
* | 类型 | 说明 | 示例 |
|
||||
* |------|------|------|
|
||||
* | RuntimeOnly | 纯运行时库,不含编辑器代码 | core, math, components |
|
||||
* | Plugin | 插件包,同时有 runtime 和 editor 入口 | ui, tilemap, behavior-tree |
|
||||
* | EditorOnly | 纯编辑器包,仅用于编辑器 | editor-core, node-editor |
|
||||
*
|
||||
* ## 目录结构约定
|
||||
*
|
||||
* ### RuntimeOnly 包
|
||||
* ```
|
||||
* packages/my-lib/
|
||||
* ├── src/
|
||||
* │ └── index.ts # 主入口
|
||||
* ├── vite.config.ts
|
||||
* └── package.json
|
||||
* ```
|
||||
*
|
||||
* ### Plugin 包
|
||||
* ```
|
||||
* packages/my-plugin/
|
||||
* ├── src/
|
||||
* │ ├── index.ts # 主入口(编辑器环境)
|
||||
* │ ├── runtime.ts # 运行时入口(不含 React)
|
||||
* │ └── editor/
|
||||
* │ └── index.ts # 编辑器模块
|
||||
* ├── plugin.json # 插件描述文件
|
||||
* ├── vite.config.ts
|
||||
* └── package.json
|
||||
* ```
|
||||
*
|
||||
* ### EditorOnly 包
|
||||
* ```
|
||||
* packages/my-editor-tool/
|
||||
* ├── src/
|
||||
* │ └── index.ts # 主入口
|
||||
* ├── vite.config.ts
|
||||
* └── package.json
|
||||
* ```
|
||||
*/
|
||||
|
||||
// Types
|
||||
export { EPackageType, STANDARD_EXTERNALS, EDITOR_ONLY_EXTERNALS } from './types';
|
||||
export type { PackageBuildConfig } from './types';
|
||||
|
||||
// Presets
|
||||
export {
|
||||
runtimeOnlyPreset,
|
||||
pluginPreset,
|
||||
standaloneRuntimeConfig,
|
||||
editorOnlyPreset
|
||||
} from './presets';
|
||||
export type {
|
||||
RuntimeOnlyOptions,
|
||||
PluginPackageOptions,
|
||||
StandaloneRuntimeOptions,
|
||||
EditorOnlyOptions
|
||||
} from './presets';
|
||||
|
||||
// Plugins
|
||||
export { cssInjectPlugin, blockEditorPlugin } from './plugins';
|
||||
export type { BlockEditorOptions } from './plugins';
|
||||
100
packages/build-config/src/plugins/block-editor.ts
Normal file
100
packages/build-config/src/plugins/block-editor.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Block Editor Plugin
|
||||
* 阻止编辑器代码泄漏插件
|
||||
*
|
||||
* 在运行时构建中检测并阻止编辑器代码被打包
|
||||
* Detects and blocks editor code from being bundled in runtime builds
|
||||
*/
|
||||
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
export interface BlockEditorOptions {
|
||||
/** 是否为运行时构建 */
|
||||
bIsRuntimeBuild: boolean;
|
||||
/** 要阻止的模块模式 */
|
||||
blockedPatterns?: (string | RegExp)[];
|
||||
/** 是否只警告而不报错 */
|
||||
bWarnOnly?: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_BLOCKED_PATTERNS: (string | RegExp)[] = [
|
||||
// React 相关
|
||||
/^react$/,
|
||||
/^react-dom$/,
|
||||
/^react\/jsx-runtime$/,
|
||||
/^lucide-react$/,
|
||||
|
||||
// 编辑器包
|
||||
/@esengine\/editor-core/,
|
||||
/@esengine\/node-editor/,
|
||||
|
||||
// 编辑器子路径
|
||||
/\/editor$/,
|
||||
/\/editor\//,
|
||||
];
|
||||
|
||||
/**
|
||||
* 创建阻止编辑器代码泄漏的插件
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { blockEditorPlugin } from '@esengine/build-config/plugins';
|
||||
*
|
||||
* // 在运行时构建中使用
|
||||
* export default defineConfig({
|
||||
* plugins: [
|
||||
* blockEditorPlugin({ bIsRuntimeBuild: true })
|
||||
* ]
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function blockEditorPlugin(options: BlockEditorOptions): Plugin {
|
||||
const {
|
||||
bIsRuntimeBuild,
|
||||
blockedPatterns = DEFAULT_BLOCKED_PATTERNS,
|
||||
bWarnOnly = false
|
||||
} = options;
|
||||
|
||||
if (!bIsRuntimeBuild) {
|
||||
// 非运行时构建不需要此插件
|
||||
return { name: 'esengine:block-editor-noop' };
|
||||
}
|
||||
|
||||
const isBlocked = (source: string): boolean => {
|
||||
return blockedPatterns.some(pattern => {
|
||||
if (typeof pattern === 'string') {
|
||||
return source === pattern || source.startsWith(pattern + '/');
|
||||
}
|
||||
return pattern.test(source);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
name: 'esengine:block-editor',
|
||||
enforce: 'pre',
|
||||
|
||||
resolveId(source: string, importer: string | undefined) {
|
||||
if (isBlocked(source)) {
|
||||
const message = `[block-editor] Editor dependency detected in runtime build:\n` +
|
||||
` Source: ${source}\n` +
|
||||
` Importer: ${importer || 'entry'}\n` +
|
||||
`\n` +
|
||||
` This usually means:\n` +
|
||||
` 1. A runtime module is importing from a non-/runtime path\n` +
|
||||
` 2. An editor-only dependency leaked into the dependency chain\n` +
|
||||
`\n` +
|
||||
` Fix: Change the import to use /runtime subpath, e.g.:\n` +
|
||||
` import { X } from '@esengine/ui/runtime' // ✓\n` +
|
||||
` import { X } from '@esengine/ui' // ✗`;
|
||||
|
||||
if (bWarnOnly) {
|
||||
console.warn('\x1b[33m' + message + '\x1b[0m');
|
||||
return { id: source, external: true };
|
||||
} else {
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
71
packages/build-config/src/plugins/css-inject.ts
Normal file
71
packages/build-config/src/plugins/css-inject.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* CSS Inject Plugin
|
||||
* CSS 注入插件
|
||||
*
|
||||
* 将 CSS 内联到 JS 中,避免单独的 CSS 文件
|
||||
* Inlines CSS into JS to avoid separate CSS files
|
||||
*/
|
||||
|
||||
import type { Plugin } from 'vite';
|
||||
import type { OutputBundle, NormalizedOutputOptions, OutputAsset, OutputChunk } from 'rollup';
|
||||
|
||||
/**
|
||||
* 创建 CSS 注入插件
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { cssInjectPlugin } from '@esengine/build-config/plugins';
|
||||
*
|
||||
* export default defineConfig({
|
||||
* plugins: [cssInjectPlugin()]
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function cssInjectPlugin(): Plugin {
|
||||
return {
|
||||
name: 'esengine:css-inject',
|
||||
apply: 'build',
|
||||
|
||||
generateBundle(_options: NormalizedOutputOptions, bundle: OutputBundle) {
|
||||
// 收集所有 CSS 内容
|
||||
const cssChunks: string[] = [];
|
||||
const cssFileNames: string[] = [];
|
||||
|
||||
for (const [fileName, chunk] of Object.entries(bundle)) {
|
||||
if (fileName.endsWith('.css') && chunk.type === 'asset') {
|
||||
cssChunks.push(chunk.source as string);
|
||||
cssFileNames.push(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
if (cssChunks.length === 0) return;
|
||||
|
||||
// 合并所有 CSS
|
||||
const combinedCSS = cssChunks.join('\n');
|
||||
|
||||
// 创建注入代码
|
||||
const injectCode = `
|
||||
(function() {
|
||||
if (typeof document === 'undefined') return;
|
||||
var style = document.createElement('style');
|
||||
style.setAttribute('data-esengine', 'true');
|
||||
style.textContent = ${JSON.stringify(combinedCSS)};
|
||||
document.head.appendChild(style);
|
||||
})();
|
||||
`;
|
||||
|
||||
// 找到主入口 JS 文件并注入
|
||||
for (const chunk of Object.values(bundle)) {
|
||||
if (chunk.type === 'chunk' && chunk.isEntry) {
|
||||
chunk.code = injectCode + chunk.code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 删除独立的 CSS 文件
|
||||
for (const fileName of cssFileNames) {
|
||||
delete bundle[fileName];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
7
packages/build-config/src/plugins/index.ts
Normal file
7
packages/build-config/src/plugins/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Shared Vite Plugins
|
||||
* 共享 Vite 插件
|
||||
*/
|
||||
|
||||
export { cssInjectPlugin } from './css-inject';
|
||||
export { blockEditorPlugin, type BlockEditorOptions } from './block-editor';
|
||||
109
packages/build-config/src/presets/editor-only.ts
Normal file
109
packages/build-config/src/presets/editor-only.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Editor-Only Package Preset
|
||||
* 纯编辑器包预设
|
||||
*
|
||||
* 用于仅在编辑器环境使用的包
|
||||
* For packages only used in the editor environment
|
||||
*
|
||||
* Examples: editor-core, node-editor
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig, type UserConfig } from 'vite';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { STANDARD_EXTERNALS } from '../types';
|
||||
import { cssInjectPlugin } from '../plugins/css-inject';
|
||||
|
||||
export interface EditorOnlyOptions {
|
||||
/** 包根目录 (通常是 __dirname) */
|
||||
root: string;
|
||||
|
||||
/** 入口文件 (默认: src/index.ts) */
|
||||
entry?: string;
|
||||
|
||||
/** 是否包含 React 组件 (默认: true) */
|
||||
hasReact?: boolean;
|
||||
|
||||
/** 是否包含 CSS (默认: false) */
|
||||
hasCSS?: boolean;
|
||||
|
||||
/** 额外的外部依赖 */
|
||||
external?: (string | RegExp)[];
|
||||
|
||||
/** 额外的 Vite 配置 */
|
||||
viteConfig?: Partial<UserConfig>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建纯编辑器包的 Vite 配置
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // vite.config.ts
|
||||
* import { editorOnlyPreset } from '@esengine/build-config/presets';
|
||||
*
|
||||
* export default editorOnlyPreset({
|
||||
* root: __dirname,
|
||||
* hasReact: true,
|
||||
* hasCSS: true
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function editorOnlyPreset(options: EditorOnlyOptions): UserConfig {
|
||||
const {
|
||||
root,
|
||||
entry = 'src/index.ts',
|
||||
hasReact = true,
|
||||
hasCSS = false,
|
||||
external = [],
|
||||
viteConfig = {}
|
||||
} = options;
|
||||
|
||||
const plugins: any[] = [];
|
||||
|
||||
// React 支持
|
||||
if (hasReact) {
|
||||
plugins.push(react());
|
||||
}
|
||||
|
||||
// DTS 生成
|
||||
plugins.push(
|
||||
dts({
|
||||
include: ['src'],
|
||||
outDir: 'dist',
|
||||
rollupTypes: false
|
||||
})
|
||||
);
|
||||
|
||||
// CSS 注入
|
||||
if (hasCSS) {
|
||||
plugins.push(cssInjectPlugin());
|
||||
}
|
||||
|
||||
return defineConfig({
|
||||
plugins,
|
||||
esbuild: hasReact ? { jsx: 'automatic' } : undefined,
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(root, entry),
|
||||
formats: ['es'],
|
||||
fileName: () => 'index.js'
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
...STANDARD_EXTERNALS,
|
||||
...external
|
||||
],
|
||||
output: {
|
||||
exports: 'named',
|
||||
preserveModules: false
|
||||
}
|
||||
},
|
||||
target: 'es2020',
|
||||
minify: false,
|
||||
sourcemap: true
|
||||
},
|
||||
...viteConfig
|
||||
});
|
||||
}
|
||||
10
packages/build-config/src/presets/index.ts
Normal file
10
packages/build-config/src/presets/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Build Presets
|
||||
* 构建预设
|
||||
*
|
||||
* 提供不同类型包的标准化 Vite 配置
|
||||
*/
|
||||
|
||||
export { runtimeOnlyPreset, type RuntimeOnlyOptions } from './runtime-only';
|
||||
export { pluginPreset, standaloneRuntimeConfig, type PluginPackageOptions, type StandaloneRuntimeOptions } from './plugin';
|
||||
export { editorOnlyPreset, type EditorOnlyOptions } from './editor-only';
|
||||
157
packages/build-config/src/presets/plugin-tsup.ts
Normal file
157
packages/build-config/src/presets/plugin-tsup.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Plugin Package Preset (tsup)
|
||||
* 插件包预设 - 基于 tsup/esbuild
|
||||
*
|
||||
* 用于同时包含运行时和编辑器模块的插件包
|
||||
* For plugin packages with both runtime and editor modules
|
||||
*
|
||||
* 生成三个入口点:
|
||||
* - index.js - 完整导出(编辑器环境)
|
||||
* - runtime.js - 纯运行时(游戏运行时环境,不含 React)
|
||||
* - editor/index.js - 编辑器模块
|
||||
*
|
||||
* Examples: ui, tilemap, behavior-tree, physics-rapier2d
|
||||
*/
|
||||
|
||||
import type { Options } from 'tsup';
|
||||
import { STANDARD_EXTERNALS } from '../types';
|
||||
|
||||
export interface PluginPackageOptions {
|
||||
/** 入口点配置 */
|
||||
entries?: {
|
||||
/** 主入口 (默认: src/index.ts) */
|
||||
main?: string;
|
||||
/** 运行时入口 (默认: src/runtime.ts) */
|
||||
runtime?: string;
|
||||
/** 编辑器入口 (默认: src/editor/index.ts) */
|
||||
editor?: string;
|
||||
};
|
||||
|
||||
/** 额外的外部依赖 */
|
||||
external?: (string | RegExp)[];
|
||||
|
||||
/** 额外的 tsup 配置 */
|
||||
tsupConfig?: Partial<Options>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建插件包的 tsup 配置
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // tsup.config.ts
|
||||
* import { defineConfig } from 'tsup';
|
||||
* import { pluginPreset } from '@esengine/build-config/presets';
|
||||
*
|
||||
* export default defineConfig(pluginPreset());
|
||||
* ```
|
||||
*/
|
||||
export function pluginPreset(options: PluginPackageOptions = {}): Options {
|
||||
const {
|
||||
entries = {},
|
||||
external = [],
|
||||
tsupConfig = {}
|
||||
} = options;
|
||||
|
||||
const mainEntry = entries.main ?? 'src/index.ts';
|
||||
const runtimeEntry = entries.runtime ?? 'src/runtime.ts';
|
||||
const editorEntry = entries.editor ?? 'src/editor/index.ts';
|
||||
|
||||
// 合并外部依赖
|
||||
const allExternal = [
|
||||
...STANDARD_EXTERNALS,
|
||||
...external
|
||||
];
|
||||
|
||||
return {
|
||||
entry: {
|
||||
index: mainEntry,
|
||||
runtime: runtimeEntry,
|
||||
'editor/index': editorEntry
|
||||
},
|
||||
format: ['esm'],
|
||||
dts: true,
|
||||
splitting: false, // 禁用代码分割
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
external: allExternal,
|
||||
esbuildOptions(options) {
|
||||
options.jsx = 'automatic';
|
||||
},
|
||||
...tsupConfig
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建纯运行时包的 tsup 配置
|
||||
*/
|
||||
export interface RuntimeOnlyOptions {
|
||||
/** 入口文件 (默认: src/index.ts) */
|
||||
entry?: string;
|
||||
/** 额外的外部依赖 */
|
||||
external?: (string | RegExp)[];
|
||||
/** 额外的 tsup 配置 */
|
||||
tsupConfig?: Partial<Options>;
|
||||
}
|
||||
|
||||
export function runtimeOnlyPreset(options: RuntimeOnlyOptions = {}): Options {
|
||||
const {
|
||||
entry = 'src/index.ts',
|
||||
external = [],
|
||||
tsupConfig = {}
|
||||
} = options;
|
||||
|
||||
return {
|
||||
entry: [entry],
|
||||
format: ['esm'],
|
||||
dts: true,
|
||||
splitting: false,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
external: [
|
||||
...STANDARD_EXTERNALS,
|
||||
...external
|
||||
],
|
||||
...tsupConfig
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建纯编辑器包的 tsup 配置
|
||||
*/
|
||||
export interface EditorOnlyOptions {
|
||||
/** 入口文件 (默认: src/index.ts) */
|
||||
entry?: string;
|
||||
/** 额外的外部依赖 */
|
||||
external?: (string | RegExp)[];
|
||||
/** 额外的 tsup 配置 */
|
||||
tsupConfig?: Partial<Options>;
|
||||
}
|
||||
|
||||
export function editorOnlyPreset(options: EditorOnlyOptions = {}): Options {
|
||||
const {
|
||||
entry = 'src/index.ts',
|
||||
external = [],
|
||||
tsupConfig = {}
|
||||
} = options;
|
||||
|
||||
return {
|
||||
entry: [entry],
|
||||
format: ['esm'],
|
||||
dts: true,
|
||||
splitting: false,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
// 将 CSS 内联到 JS 中,运行时自动注入到 DOM
|
||||
// Inline CSS into JS, auto-inject to DOM at runtime
|
||||
injectStyle: true,
|
||||
external: [
|
||||
...STANDARD_EXTERNALS,
|
||||
...external
|
||||
],
|
||||
esbuildOptions(options) {
|
||||
options.jsx = 'automatic';
|
||||
},
|
||||
...tsupConfig
|
||||
};
|
||||
}
|
||||
176
packages/build-config/src/presets/plugin.ts
Normal file
176
packages/build-config/src/presets/plugin.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Plugin Package Preset
|
||||
* 插件包预设
|
||||
*
|
||||
* 用于同时包含运行时和编辑器模块的插件包
|
||||
* For plugin packages with both runtime and editor modules
|
||||
*
|
||||
* 生成三个入口点:
|
||||
* - index.js - 完整导出(编辑器环境)
|
||||
* - runtime.js - 纯运行时(游戏运行时环境,不含 React)
|
||||
* - editor/index.js - 编辑器模块
|
||||
*
|
||||
* Examples: ui, tilemap, behavior-tree, physics-rapier2d
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig, type UserConfig } from 'vite';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import { STANDARD_EXTERNALS, EDITOR_ONLY_EXTERNALS } from '../types';
|
||||
import { cssInjectPlugin } from '../plugins/css-inject';
|
||||
|
||||
export interface PluginPackageOptions {
|
||||
/** 包根目录 (通常是 __dirname) */
|
||||
root: string;
|
||||
|
||||
/** 入口点配置 */
|
||||
entries?: {
|
||||
/** 主入口 (默认: src/index.ts) */
|
||||
main?: string;
|
||||
/** 运行时入口 (默认: src/runtime.ts) */
|
||||
runtime?: string;
|
||||
/** 编辑器入口 (默认: src/editor/index.ts) */
|
||||
editor?: string;
|
||||
};
|
||||
|
||||
/** 是否包含 CSS (默认: false) */
|
||||
hasCSS?: boolean;
|
||||
|
||||
/** 是否生成 plugin.json 导出 (默认: true) */
|
||||
hasPluginJson?: boolean;
|
||||
|
||||
/** 额外的外部依赖 */
|
||||
external?: (string | RegExp)[];
|
||||
|
||||
/** 额外的 Vite 配置 */
|
||||
viteConfig?: Partial<UserConfig>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建插件包的 Vite 配置
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // vite.config.ts
|
||||
* import { pluginPreset } from '@esengine/build-config/presets';
|
||||
*
|
||||
* export default pluginPreset({
|
||||
* root: __dirname,
|
||||
* hasCSS: true
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function pluginPreset(options: PluginPackageOptions): UserConfig {
|
||||
const {
|
||||
root,
|
||||
entries = {},
|
||||
hasCSS = false,
|
||||
external = [],
|
||||
viteConfig = {}
|
||||
} = options;
|
||||
|
||||
const mainEntry = entries.main ?? 'src/index.ts';
|
||||
const runtimeEntry = entries.runtime ?? 'src/runtime.ts';
|
||||
const editorEntry = entries.editor ?? 'src/editor/index.ts';
|
||||
|
||||
// 构建入口点映射
|
||||
const entryPoints: Record<string, string> = {
|
||||
index: resolve(root, mainEntry),
|
||||
runtime: resolve(root, runtimeEntry),
|
||||
'editor/index': resolve(root, editorEntry)
|
||||
};
|
||||
|
||||
const plugins: any[] = [
|
||||
dts({
|
||||
include: ['src'],
|
||||
outDir: 'dist',
|
||||
rollupTypes: false
|
||||
})
|
||||
];
|
||||
|
||||
// CSS 注入插件
|
||||
if (hasCSS) {
|
||||
plugins.push(cssInjectPlugin());
|
||||
}
|
||||
|
||||
return defineConfig({
|
||||
plugins,
|
||||
esbuild: {
|
||||
jsx: 'automatic',
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry: entryPoints,
|
||||
formats: ['es'],
|
||||
fileName: (_format: string, entryName: string) => `${entryName}.js`
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
...STANDARD_EXTERNALS,
|
||||
...external
|
||||
],
|
||||
output: {
|
||||
exports: 'named',
|
||||
preserveModules: false,
|
||||
// 禁用自动代码分割,所有共享代码内联到各入口
|
||||
manualChunks: () => undefined
|
||||
}
|
||||
},
|
||||
target: 'es2020',
|
||||
minify: false,
|
||||
sourcemap: true
|
||||
},
|
||||
...viteConfig
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建独立运行时构建配置
|
||||
* 用于 platform-web 等需要生成独立 IIFE 运行时的场景
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // rollup.runtime.config.js
|
||||
* import { standaloneRuntimeConfig } from '@esengine/build-config/presets';
|
||||
*
|
||||
* export default standaloneRuntimeConfig({
|
||||
* root: __dirname,
|
||||
* entry: 'src/runtime.ts',
|
||||
* globalName: 'ECSRuntime'
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export interface StandaloneRuntimeOptions {
|
||||
/** 包根目录 */
|
||||
root: string;
|
||||
/** 入口文件 */
|
||||
entry: string;
|
||||
/** 全局变量名 (IIFE 格式) */
|
||||
globalName: string;
|
||||
/** 额外的外部依赖 */
|
||||
external?: (string | RegExp)[];
|
||||
}
|
||||
|
||||
export function standaloneRuntimeConfig(options: StandaloneRuntimeOptions) {
|
||||
const { root, entry, globalName, external = [] } = options;
|
||||
|
||||
// 返回 Rollup 配置(而非 Vite,因为需要 IIFE 格式)
|
||||
return {
|
||||
input: resolve(root, entry),
|
||||
output: {
|
||||
file: 'dist/runtime.browser.js',
|
||||
format: 'iife' as const,
|
||||
name: globalName,
|
||||
sourcemap: true,
|
||||
exports: 'default' as const
|
||||
},
|
||||
external: [
|
||||
...STANDARD_EXTERNALS,
|
||||
...EDITOR_ONLY_EXTERNALS,
|
||||
...external
|
||||
],
|
||||
plugins: [
|
||||
// 需要在使用时传入 rollup 插件
|
||||
]
|
||||
};
|
||||
}
|
||||
78
packages/build-config/src/presets/runtime-only.ts
Normal file
78
packages/build-config/src/presets/runtime-only.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Runtime-Only Package Preset
|
||||
* 纯运行时包预设
|
||||
*
|
||||
* 用于不包含任何编辑器代码的基础库
|
||||
* For basic libraries without any editor code
|
||||
*
|
||||
* Examples: core, math, components, asset-system
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig, type UserConfig } from 'vite';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import { STANDARD_EXTERNALS } from '../types';
|
||||
|
||||
export interface RuntimeOnlyOptions {
|
||||
/** 包根目录 (通常是 __dirname) */
|
||||
root: string;
|
||||
/** 入口文件 (默认: src/index.ts) */
|
||||
entry?: string;
|
||||
/** 额外的外部依赖 */
|
||||
external?: (string | RegExp)[];
|
||||
/** 额外的 Vite 配置 */
|
||||
viteConfig?: Partial<UserConfig>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建纯运行时包的 Vite 配置
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // vite.config.ts
|
||||
* import { runtimeOnlyPreset } from '@esengine/build-config/presets';
|
||||
*
|
||||
* export default runtimeOnlyPreset({
|
||||
* root: __dirname
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function runtimeOnlyPreset(options: RuntimeOnlyOptions): UserConfig {
|
||||
const {
|
||||
root,
|
||||
entry = 'src/index.ts',
|
||||
external = [],
|
||||
viteConfig = {}
|
||||
} = options;
|
||||
|
||||
return defineConfig({
|
||||
plugins: [
|
||||
dts({
|
||||
include: ['src'],
|
||||
outDir: 'dist',
|
||||
rollupTypes: false
|
||||
})
|
||||
],
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(root, entry),
|
||||
formats: ['es'],
|
||||
fileName: () => 'index.js'
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
...STANDARD_EXTERNALS,
|
||||
...external
|
||||
],
|
||||
output: {
|
||||
exports: 'named',
|
||||
preserveModules: false
|
||||
}
|
||||
},
|
||||
target: 'es2020',
|
||||
minify: false,
|
||||
sourcemap: true
|
||||
},
|
||||
...viteConfig
|
||||
});
|
||||
}
|
||||
107
packages/build-config/src/types.ts
Normal file
107
packages/build-config/src/types.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Build Configuration Types
|
||||
* 构建配置类型定义
|
||||
*/
|
||||
|
||||
import type { UserConfig } from 'vite';
|
||||
|
||||
/**
|
||||
* 包类型
|
||||
* Package types for different build configurations
|
||||
*/
|
||||
export const enum EPackageType {
|
||||
/**
|
||||
* 纯运行时库 - 不含任何编辑器代码
|
||||
* Pure runtime library - no editor dependencies
|
||||
*
|
||||
* Examples: core, math, components, asset-system
|
||||
*/
|
||||
RuntimeOnly = 'runtime-only',
|
||||
|
||||
/**
|
||||
* 插件包 - 同时包含运行时和编辑器模块
|
||||
* Plugin package - contains both runtime and editor modules
|
||||
*
|
||||
* Examples: ui, tilemap, behavior-tree, physics-rapier2d
|
||||
*/
|
||||
Plugin = 'plugin',
|
||||
|
||||
/**
|
||||
* 纯编辑器包 - 仅用于编辑器
|
||||
* Editor-only package - only used in editor
|
||||
*
|
||||
* Examples: editor-core, node-editor
|
||||
*/
|
||||
EditorOnly = 'editor-only',
|
||||
|
||||
/**
|
||||
* 应用包 - 最终应用(不发布到 npm)
|
||||
* Application package - final app (not published)
|
||||
*
|
||||
* Examples: editor-app
|
||||
*/
|
||||
Application = 'application'
|
||||
}
|
||||
|
||||
/**
|
||||
* 包构建配置
|
||||
*/
|
||||
export interface PackageBuildConfig {
|
||||
/** 包名 */
|
||||
name: string;
|
||||
|
||||
/** 包类型 */
|
||||
type: EPackageType;
|
||||
|
||||
/** 入口点配置 */
|
||||
entries?: {
|
||||
/** 主入口 (默认: src/index.ts) */
|
||||
main?: string;
|
||||
/** 运行时入口 (仅 Plugin 类型) */
|
||||
runtime?: string;
|
||||
/** 编辑器入口 (Plugin 和 EditorOnly 类型) */
|
||||
editor?: string;
|
||||
};
|
||||
|
||||
/** 额外的外部依赖 */
|
||||
external?: (string | RegExp)[];
|
||||
|
||||
/** 是否包含 CSS */
|
||||
hasCSS?: boolean;
|
||||
|
||||
/** 是否生成 plugin.json 导出 */
|
||||
hasPluginJson?: boolean;
|
||||
|
||||
/** 额外的 Vite 配置 */
|
||||
viteConfig?: Partial<UserConfig>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准外部依赖列表
|
||||
* Standard external dependencies that should never be bundled
|
||||
*/
|
||||
export const STANDARD_EXTERNALS = [
|
||||
// React 生态
|
||||
'react',
|
||||
'react-dom',
|
||||
'react/jsx-runtime',
|
||||
'lucide-react',
|
||||
|
||||
// 状态管理
|
||||
'zustand',
|
||||
'immer',
|
||||
|
||||
// 所有 @esengine 包
|
||||
/^@esengine\//,
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* 编辑器专用依赖(运行时构建必须排除)
|
||||
* Editor-only dependencies that must be excluded from runtime builds
|
||||
*/
|
||||
export const EDITOR_ONLY_EXTERNALS = [
|
||||
'@esengine/editor-core',
|
||||
'@esengine/node-editor',
|
||||
/\/editor$/,
|
||||
/\/editor\//,
|
||||
] as const;
|
||||
Reference in New Issue
Block a user