refactor: 代码规范化与依赖清理 (#317)

* refactor(deps): 统一编辑器包依赖配置 & 优化分层架构

- 将 ecs-engine-bindgen 提升为 Layer 1 核心包
- 统一 9 个编辑器包的依赖声明模式
- 清理废弃的包目录 (ui, ui-editor, network-*)

* refactor(tokens): 修复 PrefabService 令牌冲突 & 补充 module.json

- 将 editor-core 的 PrefabServiceToken 改名为 EditorPrefabServiceToken
  避免与 asset-system 的 PrefabServiceToken 冲突 (Symbol.for 冲突)
- 为 mesh-3d 添加 module.json
- 为 world-streaming 添加 module.json

* refactor(editor-core): 整理导出结构 & 添加 blueprint tokens.ts

- 按功能分组整理 editor-core 的 65 行导出
- 添加清晰的分组注释 (中英双语)
- 为 blueprint 添加占位符 tokens.ts

* chore(editor): 为 14 个编辑器插件包添加 module.json

统一编辑器包的模块配置,包含:
- isEditorPlugin 标识
- runtimeModule 关联
- exports 导出清单 (inspectors, panels, gizmos)

* refactor(core): 改进类型安全 - 减少 as any 使用

- 添加 GlobalTypes.ts 定义小游戏平台和 Chrome API 类型
- SoAStorage 使用 IComponentTypeMetadata 替代 as any
- PlatformDetector 使用类型安全的平台检测
- 添加 ISoAStorageStats/ISoAFieldStats 接口

* feat(editor): 添加 EditorServicesContext 解决 prop drilling

- 新增 contexts/EditorServicesContext.tsx 提供统一服务访问
- App.tsx 包裹 EditorServicesProvider
- 提供 useEditorServices/useMessageHub 等便捷 hooks
- SceneHierarchy 添加迁移注释,后续可移除 props

* docs(editor): 澄清 inspector 目录架构关系

- inspector/ 标记为内部实现,添加 @deprecated 警告
- inspectors/ 标记为公共 API 入口点
- 添加架构说明文档

* refactor(editor): 添加全局类型声明消除 window as any

- 创建 editor-app/src/global.d.ts 声明 Window 接口扩展
- 创建 editor-core/src/global.d.ts 声明 Window 接口扩展
- 更新 App.tsx 使用类型安全的 window 属性访问
- 更新 PluginLoader.ts 使用 window.__ESENGINE_PLUGINS__
- 更新 PluginSDKRegistry.ts 使用 window.__ESENGINE_SDK__
- 更新 UserCodeService.ts 使用类型安全的全局变量访问

* refactor(editor): 提取项目和场景操作到独立 hooks

- 创建 useProjectActions hook 封装项目操作
- 创建 useSceneActions hook 封装场景操作
- 为渐进式重构 App.tsx 做准备

* refactor(editor): 清理冗余代码和未使用文件

删除的目录和文件:
- application/state/ - 重复的状态管理(与 stores/ 重复)
- 8 个孤立 CSS 文件(对应组件不存在)
- AssetBrowser.tsx - 仅为 ContentBrowser 的向后兼容包装
- AssetPicker.tsx - 未被使用
- AssetPickerDialog.tsx (顶级) - 已被 dialogs/ 版本取代
- EntityInspector.tsx (顶级) - 已被 inspectors/views/ 版本取代

修复:
- 移除 App.tsx 中未使用的导入
- 更新 application/index.ts 移除已删除模块
- 修复 useProjectActions.ts 的 MutableRefObject 类型

* refactor(editor): 统一 inspectors 模块导出结构

- 在 inspectors/index.ts 重新导出 PropertyInspector
- 创建 inspectors/fields/index.ts barrel export
- 导出 views、fields、common 子模块
- 更新 EntityInspector 使用统一入口导入

* refactor(editor): 删除废弃的 Profiler 组件

删除未使用的组件(共 1059 行):
- ProfilerPanel.tsx (229 行)
- ProfilerWindow.tsx (589 行)
- ProfilerDockPanel.tsx (241 行)
- ProfilerPanel.css
- ProfilerDockPanel.css

保留:AdvancedProfiler + AdvancedProfilerWindow(正在使用)

* refactor(runtime-core): 统一依赖处理与插件状态管理

- 新增 DependencyUtils 统一拓扑排序和依赖验证
- 新增 PluginState 定义插件生命周期状态机
- 合并 UnifiedPluginLoader 到 PluginLoader
- 清理 index.ts 移除不必要的 Token re-exports
- 新增 RuntimeMode/UserCodeRealm/ImportMapGenerator

* refactor(editor-core): 使用统一的 ImportMapGenerator

- WebBuildPipeline 使用 runtime-core 的 generateImportMap
- UserCodeService 添加 ImportMap 相关接口

* feat(compiler): 增强 esbuild 查找策略

- 支持本地 node_modules、pnpm exec、npx、全局多种来源
- EngineService 使用 RuntimeMode

* refactor(runtime-core): 简化 GameRuntime 代码

- 合并 _disableGameLogicSystems/_enableGameLogicSystems 为 _setGameLogicSystemsEnabled
- 精简本地 Token 定义的注释

* refactor(editor-core): 引入 BaseRegistry 基类消除代码重复

- 新增 BaseRegistry 和 PrioritizedRegistry 基类
- 重构 CompilerRegistry, InspectorRegistry, FieldEditorRegistry
- 统一注册表的日志记录和错误处理

* refactor(editor-core): 扩展 BaseRegistry 重构

- ComponentInspectorRegistry 继承 PrioritizedRegistry
- EditorComponentRegistry 继承 BaseRegistry
- EntityCreationRegistry 继承 BaseRegistry
- PropertyRendererRegistry 继承 PrioritizedRegistry
- 导出 BaseRegistry 基类供外部使用
- 统一双语注释格式

* refactor(editor-core): 代码优雅性优化

CommandManager:
- 提取 tryMergeWithLast() 和 pushToUndoStack() 消除重复代码
- 统一双语注释格式

FileActionRegistry:
- 提取 normalizeExtension() 消除扩展名规范化重复
- 统一私有属性命名风格(_前缀)
- 使用 createRegistryToken 统一 Token 创建

BaseRegistry:
- 添加 IOrdered 接口
- 添加 sortByOrder() 排序辅助方法

EntityCreationRegistry:
- 使用 sortByOrder() 简化排序逻辑

* refactor(editor-core): 统一日志系统 & 代码规范优化

- GizmoRegistry: 使用 createLogger 替代 console.warn
- VirtualNodeRegistry: 使用 createLogger 替代 console.warn
- WindowRegistry: 使用 logger、添加 _ 前缀、导出 IWindowRegistry token
- EditorViewportService: 使用 createLogger 替代 console.warn
- ComponentActionRegistry: 使用 logger、添加 _ 前缀、返回值改进
- SettingsRegistry: 使用 logger、提取 ensureCategory/ensureSection 方法
- 添加 WindowRegistry 到主导出

* refactor(editor-core): ModuleRegistry 使用 logger 替代 console

* refactor(editor-core): SerializerRegistry/UIRegistry 添加 token 和 _ 前缀

* refactor(editor-core): UIRegistry 代码优雅性 & Token 命名统一

- UIRegistry: 提取 _sortByOrder 消除 6 处重复排序逻辑
- UIRegistry: 添加分节注释和双语文档
- FieldEditorRegistry: Token 重命名为 FieldEditorRegistryToken
- PropertyRendererRegistry: Token 重命名为 PropertyRendererRegistryToken

* refactor(core): 统一日志系统 - console 替换为 logger

- ComponentSerializer: 使用 logger 替代 console.warn
- ComponentRegistry: console.warn → logger.warn (已有 logger)
- SceneSerializer: 添加 logger,替换 console.warn/error
- SystemScheduler: 添加 logger,替换 console.warn
- VersionMigration: 添加 logger,替换所有 console.warn
- RuntimeModeService: console.error → logger.error
- Core.ts: _logger 改为 readonly,双语错误消息
- SceneSerializer 修复:使用 getComponentTypeName 替代 constructor.name

* fix(core): 修复 constructor.name 压缩后失效问题

- Scene.ts: 使用 system.systemName 替代 system.constructor.name
- CommandBuffer.ts: 使用 getComponentTypeName() 替代 constructor.name

* refactor(editor-core): 代码规范优化 - 私有方法命名 & 日志统一

- BuildService: console → logger
- FileActionRegistry: 添加 logger, 私有方法 _ 前缀
- SettingsRegistry: 私有方法 _ 前缀 (ensureCategory → _ensureCategory)

* refactor(core): Scene.ts 私有变量命名规范化

- logger → _logger (遵循私有变量 _ 前缀规范)

* refactor(editor-core): 服务类私有成员命名规范化

- CommandManager: 私有变量/方法添加 _ 前缀
  - undoStack/redoStack/config/isExecuting
  - tryMergeWithLast/pushToUndoStack
- LocaleService: 私有变量/方法添加 _ 前缀
  - currentLocale/translations/changeListeners
  - deepMerge/getNestedValue/loadSavedLocale/saveLocale

* refactor(core): 私有成员命名规范化 & 单例模式优化

- Component.ts: _idGenerator 私有静态变量规范化
- PlatformManager.ts: _instance, _adapter, _logger 规范化
- AutoProfiler.ts: _instance, _config 及所有私有方法规范化
- ProfilerSDK.ts: _instance, _config 及所有私有方法规范化
- ComponentPoolManager: _instance, _pools, _usageTracker 规范化
- GlobalEventBus: _instance 规范化
- 添加中英双语 JSDoc 注释

* refactor(editor-app,behavior-tree-editor): 私有成员 & 单例模式命名规范化

editor-app:
- EngineService: private static instance → _instance
- EditorEngineSync: 所有私有成员添加 _ 前缀
- RuntimeResolver: 所有私有成员和方法添加 _ 前缀
- SettingsService: 所有私有成员和方法添加 _ 前缀

behavior-tree-editor:
- GlobalBlackboardService: 所有私有成员和方法添加 _ 前缀
- NotificationService: private static instance → _instance
- NodeRegistryService: 所有私有成员和方法添加 _ 前缀
- TreeStateAdapter: private static instance → _instance

* fix(editor-runtime): 添加 editor-core 到 external 避免传递依赖问题

将 @esengine/editor-core 添加到 vite external 配置,
避免 editor-core → runtime-core → ecs-engine-bindgen 的传递依赖
被错误地打包进 editor-runtime.js,导致 CI 构建失败。

* fix(core): 修复空接口 lint 错误

将 IByteDanceMiniGameAPI、IAlipayMiniGameAPI、IBaiduMiniGameAPI 从空接口改为类型别名,修复 no-empty-object-type 规则报错
This commit is contained in:
YHH
2025-12-24 20:57:08 +08:00
committed by GitHub
parent 58f70a5783
commit dbc6793dc4
133 changed files with 6880 additions and 9141 deletions

View File

@@ -0,0 +1,472 @@
/**
* @zh 依赖管理工具
* @en Dependency Management Utilities
*
* @zh 提供统一的依赖解析、拓扑排序和验证功能
* @en Provides unified dependency resolution, topological sorting, and validation
*
* @zh 设计原则 | Design principles:
* 1. 单一实现 - 所有依赖处理逻辑集中在这里
* 2. 泛型设计 - 支持任何带有 id 和 dependencies 的对象
* 3. 算法可选 - 支持 DFS 和 Kahn 两种排序算法
*/
import { createLogger } from '@esengine/ecs-framework';
const logger = createLogger('DependencyUtils');
// ============================================================================
// 类型定义 | Type Definitions
// ============================================================================
/**
* @zh 可排序的依赖项接口
* @en Interface for sortable dependency items
*/
export interface IDependable {
/**
* @zh 唯一标识符
* @en Unique identifier
*/
id: string;
/**
* @zh 依赖项 ID 列表
* @en List of dependency IDs
*/
dependencies?: string[];
}
/**
* @zh 拓扑排序选项
* @en Topological sort options
*/
export interface TopologicalSortOptions {
/**
* @zh 排序算法
* @en Sorting algorithm
* @default 'kahn'
*/
algorithm?: 'dfs' | 'kahn';
/**
* @zh 是否检测循环依赖
* @en Whether to detect circular dependencies
* @default true
*/
detectCycles?: boolean;
/**
* @zh ID 解析函数(将短 ID 转换为完整 ID
* @en ID resolver function (convert short ID to full ID)
*/
resolveId?: (id: string) => string;
}
/**
* @zh 拓扑排序结果
* @en Topological sort result
*/
export interface TopologicalSortResult<T> {
/**
* @zh 排序后的项目列表
* @en Sorted items list
*/
sorted: T[];
/**
* @zh 是否存在循环依赖
* @en Whether circular dependencies exist
*/
hasCycles: boolean;
/**
* @zh 循环依赖中的项目 ID
* @en IDs of items in circular dependencies
*/
cycleIds?: string[];
}
/**
* @zh 依赖验证结果
* @en Dependency validation result
*/
export interface DependencyValidationResult {
/**
* @zh 是否验证通过
* @en Whether validation passed
*/
valid: boolean;
/**
* @zh 缺失依赖的映射(项目 ID -> 缺失的依赖 ID 列表)
* @en Map of missing dependencies (item ID -> missing dependency IDs)
*/
missingDependencies: Map<string, string[]>;
/**
* @zh 循环依赖的项目 ID
* @en IDs involved in circular dependencies
*/
circularDependencies?: string[];
}
// ============================================================================
// 依赖 ID 解析 | Dependency ID Resolution
// ============================================================================
/**
* @zh 解析依赖 ID短 ID 转完整包名)
* @en Resolve dependency ID (short ID to full package name)
*
* @example
* resolveDependencyId('sprite') // '@esengine/sprite'
* resolveDependencyId('@esengine/sprite') // '@esengine/sprite'
* resolveDependencyId('@dimforge/rapier2d') // '@dimforge/rapier2d'
*/
export function resolveDependencyId(depId: string, scope = '@esengine'): string {
if (depId.startsWith('@')) {
return depId;
}
return `${scope}/${depId}`;
}
/**
* @zh 从完整包名提取短 ID
* @en Extract short ID from full package name
*
* @example
* extractShortId('@esengine/sprite') // 'sprite'
* extractShortId('@esengine/ecs-framework') // 'core' (特殊映射)
*/
export function extractShortId(packageName: string): string {
if (packageName.startsWith('@esengine/')) {
const name = packageName.slice(10);
if (name === 'ecs-framework') return 'core';
if (name === 'ecs-framework-math') return 'math';
return name;
}
const scopeMatch = packageName.match(/^@[^/]+\/(.+)$/);
if (scopeMatch) {
return scopeMatch[1];
}
return packageName;
}
/**
* @zh 从短 ID 获取完整包名
* @en Get full package name from short ID
*
* @example
* getPackageName('core') // '@esengine/ecs-framework'
* getPackageName('sprite') // '@esengine/sprite'
*/
export function getPackageName(shortId: string): string {
if (shortId === 'core') return '@esengine/ecs-framework';
if (shortId === 'math') return '@esengine/ecs-framework-math';
return `@esengine/${shortId}`;
}
// ============================================================================
// 拓扑排序 | Topological Sort
// ============================================================================
/**
* @zh 使用 Kahn 算法进行拓扑排序
* @en Topological sort using Kahn's algorithm
*
* @zh Kahn 算法优势:
* - 能够检测循环依赖
* - 返回所有循环中的节点
* - 时间复杂度 O(V + E)
*/
function kahnSort<T extends IDependable>(
items: T[],
resolveId: (id: string) => string
): TopologicalSortResult<T> {
const itemMap = new Map<string, T>();
const graph = new Map<string, Set<string>>();
const inDegree = new Map<string, number>();
// 构建节点映射
for (const item of items) {
itemMap.set(item.id, item);
graph.set(item.id, new Set());
inDegree.set(item.id, 0);
}
// 构建边(依赖 -> 被依赖者)
for (const item of items) {
for (const dep of item.dependencies || []) {
const depId = resolveId(dep);
if (itemMap.has(depId)) {
graph.get(depId)!.add(item.id);
inDegree.set(item.id, (inDegree.get(item.id) || 0) + 1);
}
}
}
// 收集入度为 0 的节点
const queue: string[] = [];
for (const [id, degree] of inDegree) {
if (degree === 0) {
queue.push(id);
}
}
// BFS 处理
const sorted: T[] = [];
while (queue.length > 0) {
const current = queue.shift()!;
sorted.push(itemMap.get(current)!);
for (const neighbor of graph.get(current) || []) {
const newDegree = (inDegree.get(neighbor) || 0) - 1;
inDegree.set(neighbor, newDegree);
if (newDegree === 0) {
queue.push(neighbor);
}
}
}
// 检查循环依赖
if (sorted.length !== items.length) {
const cycleIds = items
.filter(item => !sorted.includes(item))
.map(item => item.id);
return { sorted, hasCycles: true, cycleIds };
}
return { sorted, hasCycles: false };
}
/**
* @zh 使用 DFS 进行拓扑排序
* @en Topological sort using DFS
*
* @zh DFS 算法特点:
* - 实现简单
* - 递归方式,栈溢出风险(极端情况)
*/
function dfsSort<T extends IDependable>(
items: T[],
resolveId: (id: string) => string
): TopologicalSortResult<T> {
const itemMap = new Map<string, T>();
for (const item of items) {
itemMap.set(item.id, item);
}
const sorted: T[] = [];
const visited = new Set<string>();
const visiting = new Set<string>(); // 用于检测循环
const cycleIds: string[] = [];
const visit = (item: T): boolean => {
if (visited.has(item.id)) return true;
if (visiting.has(item.id)) {
cycleIds.push(item.id);
return false; // 发现循环
}
visiting.add(item.id);
for (const dep of item.dependencies || []) {
const depId = resolveId(dep);
const depItem = itemMap.get(depId);
if (depItem && !visit(depItem)) {
cycleIds.push(item.id);
return false;
}
}
visiting.delete(item.id);
visited.add(item.id);
sorted.push(item);
return true;
};
for (const item of items) {
if (!visited.has(item.id)) {
visit(item);
}
}
return {
sorted,
hasCycles: cycleIds.length > 0,
cycleIds: cycleIds.length > 0 ? [...new Set(cycleIds)] : undefined
};
}
/**
* @zh 拓扑排序(统一入口)
* @en Topological sort (unified entry)
*
* @zh 按依赖关系对项目进行排序,确保被依赖的项目在前
* @en Sort items by dependencies, ensuring dependencies come first
*
* @param items - @zh 待排序的项目列表 @en Items to sort
* @param options - @zh 排序选项 @en Sort options
* @returns @zh 排序结果 @en Sort result
*
* @example
* ```typescript
* const plugins = [
* { id: '@esengine/sprite', dependencies: ['engine-core'] },
* { id: '@esengine/engine-core', dependencies: [] },
* { id: '@esengine/tilemap', dependencies: ['sprite'] }
* ];
*
* const result = topologicalSort(plugins);
* // result.sorted: [engine-core, sprite, tilemap]
* ```
*/
export function topologicalSort<T extends IDependable>(
items: T[],
options: TopologicalSortOptions = {}
): TopologicalSortResult<T> {
const {
algorithm = 'kahn',
detectCycles = true,
resolveId = resolveDependencyId
} = options;
if (items.length === 0) {
return { sorted: [], hasCycles: false };
}
const result = algorithm === 'kahn'
? kahnSort(items, resolveId)
: dfsSort(items, resolveId);
if (result.hasCycles && detectCycles) {
logger.warn(`Circular dependency detected among: ${result.cycleIds?.join(', ')}`);
}
return result;
}
// ============================================================================
// 依赖验证 | Dependency Validation
// ============================================================================
/**
* @zh 验证依赖完整性
* @en Validate dependency completeness
*
* @zh 检查所有启用的项目的依赖是否都已启用
* @en Check if all dependencies of enabled items are also enabled
*
* @param items - @zh 所有项目 @en All items
* @param enabledIds - @zh 已启用的项目 ID 集合 @en Set of enabled item IDs
* @param options - @zh 选项 @en Options
* @returns @zh 验证结果 @en Validation result
*/
export function validateDependencies<T extends IDependable>(
items: T[],
enabledIds: Set<string>,
options: { resolveId?: (id: string) => string } = {}
): DependencyValidationResult {
const { resolveId = resolveDependencyId } = options;
const missingDependencies = new Map<string, string[]>();
for (const item of items) {
if (!enabledIds.has(item.id)) continue;
const missing: string[] = [];
for (const dep of item.dependencies || []) {
const depId = resolveId(dep);
if (!enabledIds.has(depId)) {
missing.push(depId);
}
}
if (missing.length > 0) {
missingDependencies.set(item.id, missing);
}
}
// 检查循环依赖
const enabledItems = items.filter(item => enabledIds.has(item.id));
const sortResult = topologicalSort(enabledItems, { resolveId });
return {
valid: missingDependencies.size === 0 && !sortResult.hasCycles,
missingDependencies,
circularDependencies: sortResult.cycleIds
};
}
/**
* @zh 获取项目的所有依赖(包括传递依赖)
* @en Get all dependencies of an item (including transitive)
*
* @param itemId - @zh 项目 ID @en Item ID
* @param items - @zh 所有项目 @en All items
* @param options - @zh 选项 @en Options
* @returns @zh 所有依赖 ID 的集合 @en Set of all dependency IDs
*/
export function getAllDependencies<T extends IDependable>(
itemId: string,
items: T[],
options: { resolveId?: (id: string) => string } = {}
): Set<string> {
const { resolveId = resolveDependencyId } = options;
const itemMap = new Map<string, T>();
for (const item of items) {
itemMap.set(item.id, item);
}
const allDeps = new Set<string>();
const visited = new Set<string>();
const collect = (id: string) => {
if (visited.has(id)) return;
visited.add(id);
const item = itemMap.get(id);
if (!item) return;
for (const dep of item.dependencies || []) {
const depId = resolveId(dep);
allDeps.add(depId);
collect(depId);
}
};
collect(itemId);
return allDeps;
}
/**
* @zh 获取依赖于指定项目的所有项目(反向依赖)
* @en Get all items that depend on the specified item (reverse dependencies)
*
* @param itemId - @zh 项目 ID @en Item ID
* @param items - @zh 所有项目 @en All items
* @param options - @zh 选项 @en Options
* @returns @zh 所有依赖此项目的 ID 集合 @en Set of IDs that depend on this item
*/
export function getReverseDependencies<T extends IDependable>(
itemId: string,
items: T[],
options: { resolveId?: (id: string) => string } = {}
): Set<string> {
const { resolveId = resolveDependencyId } = options;
const reverseDeps = new Set<string>();
for (const item of items) {
for (const dep of item.dependencies || []) {
const depId = resolveId(dep);
if (depId === itemId) {
reverseDeps.add(item.id);
break;
}
}
}
return reverseDeps;
}

View File

@@ -0,0 +1,22 @@
/**
* @zh 运行时核心工具模块
* @en Runtime Core Utilities
*/
export {
// 类型
type IDependable,
type TopologicalSortOptions,
type TopologicalSortResult,
type DependencyValidationResult,
// 依赖 ID 解析
resolveDependencyId,
extractShortId,
getPackageName,
// 拓扑排序
topologicalSort,
// 依赖验证
validateDependencies,
getAllDependencies,
getReverseDependencies
} from './DependencyUtils';