feat: 添加脚本编辑器配置和类型定义支持
This commit is contained in:
@@ -37,6 +37,25 @@ const filesToBundle = [
|
||||
}
|
||||
];
|
||||
|
||||
// Type definition files for IDE intellisense
|
||||
// 用于 IDE 智能感知的类型定义文件
|
||||
const typesDir = path.join(bundleDir, 'types');
|
||||
if (!fs.existsSync(typesDir)) {
|
||||
fs.mkdirSync(typesDir, { recursive: true });
|
||||
console.log(`Created types directory: ${typesDir}`);
|
||||
}
|
||||
|
||||
const typeFilesToBundle = [
|
||||
{
|
||||
src: path.join(rootPath, 'packages/core/dist/index.d.ts'),
|
||||
dst: path.join(typesDir, 'ecs-framework.d.ts')
|
||||
},
|
||||
{
|
||||
src: path.join(rootPath, 'packages/engine-core/dist/index.d.ts'),
|
||||
dst: path.join(typesDir, 'engine-core.d.ts')
|
||||
}
|
||||
];
|
||||
|
||||
// Copy files
|
||||
let success = true;
|
||||
for (const { src, dst } of filesToBundle) {
|
||||
@@ -59,6 +78,24 @@ for (const { src, dst } of filesToBundle) {
|
||||
}
|
||||
}
|
||||
|
||||
// Copy type definition files (optional - don't fail if not found)
|
||||
// 复制类型定义文件(可选 - 找不到不报错)
|
||||
for (const { src, dst } of typeFilesToBundle) {
|
||||
try {
|
||||
if (!fs.existsSync(src)) {
|
||||
console.warn(`Type definition not found: ${src}`);
|
||||
console.log(' Build packages first: pnpm --filter @esengine/core build');
|
||||
continue;
|
||||
}
|
||||
|
||||
fs.copyFileSync(src, dst);
|
||||
const stats = fs.statSync(dst);
|
||||
console.log(`✓ Bundled type definition ${path.basename(dst)} (${(stats.size / 1024).toFixed(2)} KB)`);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to bundle type definition ${path.basename(src)}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Update tauri.conf.json to include runtime directory
|
||||
if (success) {
|
||||
const tauriConfigPath = path.join(editorPath, 'src-tauri', 'tauri.conf.json');
|
||||
|
||||
@@ -142,6 +142,55 @@ pub fn get_temp_dir() -> Result<String, String> {
|
||||
.ok_or_else(|| "Failed to get temp directory".to_string())
|
||||
}
|
||||
|
||||
/// Open project folder with specified editor
|
||||
/// 使用指定编辑器打开项目文件夹
|
||||
///
|
||||
/// @param project_path - Project folder path | 项目文件夹路径
|
||||
/// @param editor_command - Editor command (e.g., "code", "cursor") | 编辑器命令
|
||||
/// @param file_path - Optional file to open (will be opened in the editor) | 可选的要打开的文件
|
||||
#[tauri::command]
|
||||
pub fn open_with_editor(
|
||||
project_path: String,
|
||||
editor_command: String,
|
||||
file_path: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
use std::path::Path;
|
||||
|
||||
// Normalize paths
|
||||
let normalized_project = project_path.replace('/', "\\");
|
||||
let normalized_file = file_path.map(|f| f.replace('/', "\\"));
|
||||
|
||||
// Verify project path exists
|
||||
let project = Path::new(&normalized_project);
|
||||
if !project.exists() {
|
||||
return Err(format!("Project path does not exist: {}", normalized_project));
|
||||
}
|
||||
|
||||
println!(
|
||||
"[open_with_editor] editor: {}, project: {}, file: {:?}",
|
||||
editor_command, normalized_project, normalized_file
|
||||
);
|
||||
|
||||
let mut cmd = Command::new(&editor_command);
|
||||
|
||||
// Add project folder as first argument
|
||||
cmd.arg(&normalized_project);
|
||||
|
||||
// If a specific file is provided, add it as a second argument
|
||||
// Most editors support: editor <folder> <file>
|
||||
if let Some(ref file) = normalized_file {
|
||||
let file_path = Path::new(file);
|
||||
if file_path.exists() {
|
||||
cmd.arg(file);
|
||||
}
|
||||
}
|
||||
|
||||
cmd.spawn()
|
||||
.map_err(|e| format!("Failed to open with editor '{}': {}", editor_command, e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get application resource directory
|
||||
#[tauri::command]
|
||||
pub fn get_app_resource_dir(app: AppHandle) -> Result<String, String> {
|
||||
@@ -163,6 +212,97 @@ pub fn get_current_dir() -> Result<String, String> {
|
||||
.map_err(|e| format!("Failed to get current directory: {}", e))
|
||||
}
|
||||
|
||||
/// Copy type definitions to project for IDE intellisense
|
||||
/// 复制类型定义文件到项目以支持 IDE 智能感知
|
||||
#[tauri::command]
|
||||
pub fn copy_type_definitions(app: AppHandle, project_path: String) -> Result<(), String> {
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
let project = Path::new(&project_path);
|
||||
if !project.exists() {
|
||||
return Err(format!("Project path does not exist: {}", project_path));
|
||||
}
|
||||
|
||||
// Create types directory in project
|
||||
// 在项目中创建 types 目录
|
||||
let types_dir = project.join("types");
|
||||
if !types_dir.exists() {
|
||||
fs::create_dir_all(&types_dir)
|
||||
.map_err(|e| format!("Failed to create types directory: {}", e))?;
|
||||
}
|
||||
|
||||
// Get resource directory (where bundled files are)
|
||||
// 获取资源目录(打包文件所在位置)
|
||||
let resource_dir = app.path()
|
||||
.resource_dir()
|
||||
.map_err(|e| format!("Failed to get resource directory: {}", e))?;
|
||||
|
||||
// Type definition files to copy
|
||||
// 要复制的类型定义文件
|
||||
// Format: (resource_path, workspace_path, dest_name)
|
||||
// 格式:(资源路径,工作区路径,目标文件名)
|
||||
// Note: resource_path is relative to Tauri resource dir (runtime/ is mapped to .)
|
||||
// 注意:resource_path 相对于 Tauri 资源目录(runtime/ 映射到 .)
|
||||
let type_files = [
|
||||
("types/ecs-framework.d.ts", "packages/core/dist/index.d.ts", "ecs-framework.d.ts"),
|
||||
("types/engine-core.d.ts", "packages/engine-core/dist/index.d.ts", "engine-core.d.ts"),
|
||||
];
|
||||
|
||||
// Try to find workspace root (for development mode)
|
||||
// 尝试查找工作区根目录(用于开发模式)
|
||||
let workspace_root = std::env::current_dir()
|
||||
.ok()
|
||||
.and_then(|cwd| {
|
||||
// Look for pnpm-workspace.yaml or package.json in parent directories
|
||||
// 在父目录中查找 pnpm-workspace.yaml 或 package.json
|
||||
let mut dir = cwd.as_path();
|
||||
loop {
|
||||
if dir.join("pnpm-workspace.yaml").exists() {
|
||||
return Some(dir.to_path_buf());
|
||||
}
|
||||
match dir.parent() {
|
||||
Some(parent) => dir = parent,
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut copied_count = 0;
|
||||
for (resource_relative, workspace_relative, dest_name) in type_files {
|
||||
let dest_path = types_dir.join(dest_name);
|
||||
|
||||
// Try resource directory first (production mode)
|
||||
// 首先尝试资源目录(生产模式)
|
||||
let src_path = resource_dir.join(resource_relative);
|
||||
if src_path.exists() {
|
||||
fs::copy(&src_path, &dest_path)
|
||||
.map_err(|e| format!("Failed to copy {}: {}", resource_relative, e))?;
|
||||
println!("[copy_type_definitions] Copied {} to {}", src_path.display(), dest_path.display());
|
||||
copied_count += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try workspace directory (development mode)
|
||||
// 尝试工作区目录(开发模式)
|
||||
if let Some(ref ws_root) = workspace_root {
|
||||
let ws_src_path = ws_root.join(workspace_relative);
|
||||
if ws_src_path.exists() {
|
||||
fs::copy(&ws_src_path, &dest_path)
|
||||
.map_err(|e| format!("Failed to copy {}: {}", workspace_relative, e))?;
|
||||
println!("[copy_type_definitions] Copied {} to {} (dev mode)", ws_src_path.display(), dest_path.display());
|
||||
copied_count += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
println!("[copy_type_definitions] {} not found, skipping", dest_name);
|
||||
}
|
||||
|
||||
println!("[copy_type_definitions] Copied {} type definition files to {}", copied_count, types_dir.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start a local HTTP server for runtime preview
|
||||
#[tauri::command]
|
||||
pub fn start_local_server(root_path: String, port: u16) -> Result<String, String> {
|
||||
|
||||
@@ -81,6 +81,8 @@ fn main() {
|
||||
commands::open_file_with_default_app,
|
||||
commands::show_in_folder,
|
||||
commands::get_temp_dir,
|
||||
commands::open_with_editor,
|
||||
commands::copy_type_definitions,
|
||||
commands::get_app_resource_dir,
|
||||
commands::get_current_dir,
|
||||
commands::start_local_server,
|
||||
|
||||
@@ -381,6 +381,14 @@ function App() {
|
||||
// 设置 Tauri project:// 协议的基础路径(用于加载插件等项目文件)
|
||||
await TauriAPI.setProjectBasePath(projectPath);
|
||||
|
||||
// 复制类型定义到项目,用于 IDE 智能感知
|
||||
// Copy type definitions to project for IDE intellisense
|
||||
try {
|
||||
await TauriAPI.copyTypeDefinitions(projectPath);
|
||||
} catch (e) {
|
||||
console.warn('[App] Failed to copy type definitions:', e);
|
||||
}
|
||||
|
||||
const settings = SettingsService.getInstance();
|
||||
settings.addRecentProject(projectPath);
|
||||
|
||||
|
||||
@@ -168,6 +168,26 @@ export class TauriAPI {
|
||||
await invoke('show_in_folder', { filePath: path });
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定编辑器打开项目
|
||||
* Open project with specified editor
|
||||
*
|
||||
* @param projectPath 项目文件夹路径 | Project folder path
|
||||
* @param editorCommand 编辑器命令(如 "code", "cursor")| Editor command
|
||||
* @param filePath 可选的要打开的文件路径 | Optional file path to open
|
||||
*/
|
||||
static async openWithEditor(
|
||||
projectPath: string,
|
||||
editorCommand: string,
|
||||
filePath?: string
|
||||
): Promise<void> {
|
||||
await invoke('open_with_editor', {
|
||||
projectPath,
|
||||
editorCommand,
|
||||
filePath: filePath || null
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开行为树文件选择对话框
|
||||
* @returns 用户选择的文件路径,取消则返回 null
|
||||
@@ -311,6 +331,16 @@ export class TauriAPI {
|
||||
return await invoke<string>('generate_qrcode', { text });
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制类型定义文件到项目
|
||||
* Copy type definition files to project for IDE intellisense
|
||||
*
|
||||
* @param projectPath 项目路径 | Project path
|
||||
*/
|
||||
static async copyTypeDefinitions(projectPath: string): Promise<void> {
|
||||
return await invoke<void>('copy_type_definitions', { projectPath });
|
||||
}
|
||||
|
||||
/**
|
||||
* 将本地文件路径转换为 Tauri 可访问的 asset URL
|
||||
* @param filePath 本地文件路径
|
||||
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
import { Core } from '@esengine/ecs-framework';
|
||||
import { MessageHub, FileActionRegistry, type FileCreationTemplate } from '@esengine/editor-core';
|
||||
import { TauriAPI, DirectoryEntry } from '../api/tauri';
|
||||
import { SettingsService } from '../services/SettingsService';
|
||||
import { ContextMenu, ContextMenuItem } from './ContextMenu';
|
||||
import { PromptDialog } from './PromptDialog';
|
||||
import '../styles/ContentBrowser.css';
|
||||
@@ -210,8 +211,124 @@ export function ContentBrowser({
|
||||
'Shader': { en: 'Shader', zh: '着色器' },
|
||||
'Tilemap': { en: 'Tilemap', zh: '瓦片地图' },
|
||||
'Tileset': { en: 'Tileset', zh: '瓦片集' },
|
||||
'Component': { en: 'Component', zh: '组件' },
|
||||
'System': { en: 'System', zh: '系统' },
|
||||
'TypeScript': { en: 'TypeScript', zh: 'TypeScript' },
|
||||
};
|
||||
|
||||
// 注册内置的 TypeScript 文件创建模板
|
||||
// Register built-in TypeScript file creation templates
|
||||
useEffect(() => {
|
||||
if (!fileActionRegistry) return;
|
||||
|
||||
const builtinTemplates: FileCreationTemplate[] = [
|
||||
{
|
||||
id: 'ts-component',
|
||||
label: 'Component',
|
||||
extension: '.ts',
|
||||
icon: 'FileCode',
|
||||
category: 'Script',
|
||||
getContent: (fileName: string) => {
|
||||
const className = fileName.replace(/\.ts$/, '');
|
||||
return `import { Component, ECSComponent, Property, Serialize, Serializable } from '@esengine/ecs-framework';
|
||||
|
||||
/**
|
||||
* ${className}
|
||||
*/
|
||||
@ECSComponent('${className}')
|
||||
@Serializable({ version: 1, typeId: '${className}' })
|
||||
export class ${className} extends Component {
|
||||
// 在这里添加组件属性
|
||||
// Add component properties here
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Example Property' })
|
||||
public exampleProperty: number = 0;
|
||||
|
||||
onInitialize(): void {
|
||||
// 组件初始化时调用
|
||||
// Called when component is initialized
|
||||
}
|
||||
|
||||
onDestroy(): void {
|
||||
// 组件销毁时调用
|
||||
// Called when component is destroyed
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'ts-system',
|
||||
label: 'System',
|
||||
extension: '.ts',
|
||||
icon: 'FileCode',
|
||||
category: 'Script',
|
||||
getContent: (fileName: string) => {
|
||||
const className = fileName.replace(/\.ts$/, '');
|
||||
return `import { EntitySystem, Matcher, type Entity } from '@esengine/ecs-framework';
|
||||
|
||||
/**
|
||||
* ${className}
|
||||
*/
|
||||
export class ${className} extends EntitySystem {
|
||||
// 定义系统处理的组件类型
|
||||
// Define component types this system processes
|
||||
protected getMatcher(): Matcher {
|
||||
// 返回匹配器,指定需要哪些组件
|
||||
// Return matcher specifying required components
|
||||
// return Matcher.all(SomeComponent);
|
||||
return Matcher.empty();
|
||||
}
|
||||
|
||||
protected updateEntity(entity: Entity, deltaTime: number): void {
|
||||
// 处理每个实体
|
||||
// Process each entity
|
||||
}
|
||||
|
||||
// 可选:系统初始化
|
||||
// Optional: System initialization
|
||||
// onInitialize(): void {
|
||||
// super.onInitialize();
|
||||
// }
|
||||
}
|
||||
`;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'ts-script',
|
||||
label: 'TypeScript',
|
||||
extension: '.ts',
|
||||
icon: 'FileCode',
|
||||
category: 'Script',
|
||||
getContent: (fileName: string) => {
|
||||
const name = fileName.replace(/\.ts$/, '');
|
||||
return `/**
|
||||
* ${name}
|
||||
*/
|
||||
|
||||
export function ${name.charAt(0).toLowerCase() + name.slice(1)}(): void {
|
||||
// 在这里编写代码
|
||||
// Write your code here
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// 注册模板
|
||||
for (const template of builtinTemplates) {
|
||||
fileActionRegistry.registerCreationTemplate(template);
|
||||
}
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
for (const template of builtinTemplates) {
|
||||
fileActionRegistry.unregisterCreationTemplate(template);
|
||||
}
|
||||
};
|
||||
}, [fileActionRegistry]);
|
||||
|
||||
const getTemplateLabel = (label: string): string => {
|
||||
const mapping = templateLabels[label];
|
||||
if (mapping) {
|
||||
@@ -439,6 +556,24 @@ export function ContentBrowser({
|
||||
return;
|
||||
}
|
||||
|
||||
// 脚本文件使用配置的编辑器打开
|
||||
// Open script files with configured editor
|
||||
if (ext === 'ts' || ext === 'tsx' || ext === 'js' || ext === 'jsx') {
|
||||
const settings = SettingsService.getInstance();
|
||||
const editorCommand = settings.getScriptEditorCommand();
|
||||
|
||||
if (editorCommand && projectPath) {
|
||||
try {
|
||||
await TauriAPI.openWithEditor(projectPath, editorCommand, asset.path);
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error('Failed to open with editor:', error);
|
||||
// 如果失败,回退到系统默认应用
|
||||
// Fall back to system default app if failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fileActionRegistry) {
|
||||
const handled = await fileActionRegistry.handleDoubleClick(asset.path);
|
||||
if (handled) return;
|
||||
@@ -450,7 +585,7 @@ export function ContentBrowser({
|
||||
console.error('Failed to open file:', error);
|
||||
}
|
||||
}
|
||||
}, [loadAssets, onOpenScene, fileActionRegistry]);
|
||||
}, [loadAssets, onOpenScene, fileActionRegistry, projectPath]);
|
||||
|
||||
// Handle context menu
|
||||
const handleContextMenu = useCallback((e: React.MouseEvent, asset?: AssetItem) => {
|
||||
@@ -1127,10 +1262,16 @@ export function ContentBrowser({
|
||||
)}
|
||||
|
||||
{/* Create File Dialog */}
|
||||
{createFileDialog && (
|
||||
{createFileDialog && (() => {
|
||||
// 规范化扩展名(确保有点号前缀)
|
||||
// Normalize extension (ensure dot prefix)
|
||||
const ext = createFileDialog.template.extension.startsWith('.')
|
||||
? createFileDialog.template.extension
|
||||
: `.${createFileDialog.template.extension}`;
|
||||
return (
|
||||
<PromptDialog
|
||||
title={`New ${createFileDialog.template.label}`}
|
||||
message={`Enter file name (.${createFileDialog.template.extension} will be added):`}
|
||||
title={locale === 'zh' ? `新建 ${getTemplateLabel(createFileDialog.template.label)}` : `New ${createFileDialog.template.label}`}
|
||||
message={locale === 'zh' ? `输入文件名(将添加 ${ext}):` : `Enter file name (${ext} will be added):`}
|
||||
placeholder="filename"
|
||||
confirmText={locale === 'zh' ? '创建' : 'Create'}
|
||||
cancelText={locale === 'zh' ? '取消' : 'Cancel'}
|
||||
@@ -1139,8 +1280,8 @@ export function ContentBrowser({
|
||||
setCreateFileDialog(null);
|
||||
|
||||
let fileName = value;
|
||||
if (!fileName.endsWith(`.${template.extension}`)) {
|
||||
fileName = `${fileName}.${template.extension}`;
|
||||
if (!fileName.endsWith(ext)) {
|
||||
fileName = `${fileName}${ext}`;
|
||||
}
|
||||
const filePath = `${parentPath}/${fileName}`;
|
||||
|
||||
@@ -1156,7 +1297,8 @@ export function ContentBrowser({
|
||||
}}
|
||||
onCancel={() => setCreateFileDialog(null)}
|
||||
/>
|
||||
)}
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,32 @@ class EditorAppearanceEditorModule implements IEditorModuleLoader {
|
||||
step: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'scriptEditor',
|
||||
title: '脚本编辑器',
|
||||
description: '配置用于打开脚本文件的外部编辑器',
|
||||
settings: [
|
||||
{
|
||||
key: 'editor.scriptEditor',
|
||||
label: '脚本编辑器',
|
||||
type: 'select',
|
||||
defaultValue: 'system',
|
||||
description: '双击脚本文件时使用的编辑器',
|
||||
options: SettingsService.SCRIPT_EDITORS.map(editor => ({
|
||||
value: editor.id,
|
||||
label: editor.name
|
||||
}))
|
||||
},
|
||||
{
|
||||
key: 'editor.customScriptEditorCommand',
|
||||
label: '自定义编辑器命令',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
description: '当选择"自定义"时,填写编辑器的命令行命令(如 notepad++)',
|
||||
placeholder: '例如:notepad++'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
@@ -87,4 +87,64 @@ export class SettingsService {
|
||||
public clearRecentProjects(): void {
|
||||
this.set('recentProjects', []);
|
||||
}
|
||||
|
||||
// ==================== Script Editor Settings ====================
|
||||
|
||||
/**
|
||||
* 支持的脚本编辑器类型
|
||||
* Supported script editor types
|
||||
*/
|
||||
public static readonly SCRIPT_EDITORS = [
|
||||
{ id: 'system', name: 'System Default', nameZh: '系统默认', command: '' },
|
||||
{ id: 'vscode', name: 'Visual Studio Code', nameZh: 'Visual Studio Code', command: 'code' },
|
||||
{ id: 'cursor', name: 'Cursor', nameZh: 'Cursor', command: 'cursor' },
|
||||
{ id: 'webstorm', name: 'WebStorm', nameZh: 'WebStorm', command: 'webstorm' },
|
||||
{ id: 'sublime', name: 'Sublime Text', nameZh: 'Sublime Text', command: 'subl' },
|
||||
{ id: 'custom', name: 'Custom', nameZh: '自定义', command: '' }
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取脚本编辑器设置
|
||||
* Get script editor setting
|
||||
*/
|
||||
public getScriptEditor(): string {
|
||||
return this.get<string>('editor.scriptEditor', 'system');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置脚本编辑器
|
||||
* Set script editor
|
||||
*/
|
||||
public setScriptEditor(editorId: string): void {
|
||||
this.set('editor.scriptEditor', editorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义脚本编辑器命令
|
||||
* Get custom script editor command
|
||||
*/
|
||||
public getCustomScriptEditorCommand(): string {
|
||||
return this.get<string>('editor.customScriptEditorCommand', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置自定义脚本编辑器命令
|
||||
* Set custom script editor command
|
||||
*/
|
||||
public setCustomScriptEditorCommand(command: string): void {
|
||||
this.set('editor.customScriptEditorCommand', command);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前脚本编辑器的命令
|
||||
* Get current script editor command
|
||||
*/
|
||||
public getScriptEditorCommand(): string {
|
||||
const editorId = this.getScriptEditor();
|
||||
if (editorId === 'custom') {
|
||||
return this.getCustomScriptEditorCommand();
|
||||
}
|
||||
const editor = SettingsService.SCRIPT_EDITORS.find(e => e.id === editorId);
|
||||
return editor?.command || '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,40 @@ export class ProjectService implements IService {
|
||||
const assetsPath = `${projectPath}${sep}assets`;
|
||||
await this.fileAPI.createDirectory(assetsPath);
|
||||
|
||||
// Create types folder for type definitions
|
||||
// 创建类型定义文件夹
|
||||
const typesPath = `${projectPath}${sep}types`;
|
||||
await this.fileAPI.createDirectory(typesPath);
|
||||
|
||||
// Create tsconfig.json for TypeScript support
|
||||
// 创建 tsconfig.json 用于 TypeScript 支持
|
||||
const tsConfig = {
|
||||
compilerOptions: {
|
||||
target: 'ES2020',
|
||||
module: 'ESNext',
|
||||
moduleResolution: 'bundler',
|
||||
lib: ['ES2020', 'DOM'],
|
||||
strict: true,
|
||||
esModuleInterop: true,
|
||||
skipLibCheck: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
experimentalDecorators: true,
|
||||
emitDecoratorMetadata: true,
|
||||
noEmit: true,
|
||||
// Reference local type definitions
|
||||
// 引用本地类型定义文件
|
||||
typeRoots: ['./types'],
|
||||
paths: {
|
||||
'@esengine/ecs-framework': ['./types/ecs-framework.d.ts'],
|
||||
'@esengine/engine-core': ['./types/engine-core.d.ts']
|
||||
}
|
||||
},
|
||||
include: ['scripts/**/*.ts'],
|
||||
exclude: ['.esengine']
|
||||
};
|
||||
const tsConfigPath = `${projectPath}${sep}tsconfig.json`;
|
||||
await this.fileAPI.writeFileContent(tsConfigPath, JSON.stringify(tsConfig, null, 2));
|
||||
|
||||
await this.messageHub.publish('project:created', {
|
||||
path: projectPath
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user