@wukong-kit/extension 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/main.d.ts +40 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +53 -0
- package/dist/main.js.map +1 -0
- package/dist/panels/default/index.d.ts +80 -0
- package/dist/panels/default/index.d.ts.map +1 -0
- package/dist/panels/default/index.js +770 -0
- package/dist/panels/default/index.js.map +1 -0
- package/dist/services/ModuleRegistryService.d.ts +157 -0
- package/dist/services/ModuleRegistryService.d.ts.map +1 -0
- package/dist/services/ModuleRegistryService.js +891 -0
- package/dist/services/ModuleRegistryService.js.map +1 -0
- package/package.json +112 -0
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ModuleRegistryService = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
class ModuleRegistryService {
|
|
8
|
+
constructor(projectPath = Editor.Project.path) {
|
|
9
|
+
this.projectPath = projectPath;
|
|
10
|
+
this.configPath = (0, path_1.join)(projectPath, 'assets', 'config', 'wukong.modules.json');
|
|
11
|
+
this.packageJsonPath = (0, path_1.join)(projectPath, 'package.json');
|
|
12
|
+
}
|
|
13
|
+
async listModules() {
|
|
14
|
+
const config = this.readConfig();
|
|
15
|
+
const packageJson = this.readPackageJson();
|
|
16
|
+
const dependencies = {
|
|
17
|
+
...(packageJson.dependencies ?? {}),
|
|
18
|
+
...(packageJson.devDependencies ?? {})
|
|
19
|
+
};
|
|
20
|
+
return Promise.all(config.supportedModules.map(async (module) => {
|
|
21
|
+
const installedVersion = dependencies[module.npmPackage] ?? null;
|
|
22
|
+
const installPackage = module.installPackage || module.npmPackage;
|
|
23
|
+
const enabledModule = config.enabledModules.find(item => item.id === module.id);
|
|
24
|
+
const initMode = module.initMode ?? 'none';
|
|
25
|
+
const shouldFetchLatest = Boolean(installedVersion) && !installPackage.startsWith('file:');
|
|
26
|
+
const latestVersion = shouldFetchLatest ? await this.fetchLatestVersion(module.npmPackage).catch(() => null) : null;
|
|
27
|
+
return {
|
|
28
|
+
...module,
|
|
29
|
+
installPackage,
|
|
30
|
+
initMode,
|
|
31
|
+
enabled: enabledModule?.enabled ?? Boolean(enabledModule),
|
|
32
|
+
order: this.normalizeOrder(enabledModule?.order ?? module.defaultOrder ?? 100),
|
|
33
|
+
installed: Boolean(installedVersion),
|
|
34
|
+
installedVersion,
|
|
35
|
+
latestVersion,
|
|
36
|
+
updateAvailable: Boolean(installedVersion && latestVersion && this.normalizeVersion(installedVersion) !== latestVersion)
|
|
37
|
+
};
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
async installModule(npmPackage) {
|
|
41
|
+
await this.runNpm(['install', npmPackage]);
|
|
42
|
+
const config = this.readConfig();
|
|
43
|
+
const module = config.supportedModules.find(item => npmPackage === item.npmPackage || npmPackage === item.installPackage || npmPackage.startsWith(`${item.npmPackage}@`));
|
|
44
|
+
if (module && !config.enabledModules.some(item => item.id === module.id)) {
|
|
45
|
+
config.enabledModules.push({
|
|
46
|
+
id: module.id,
|
|
47
|
+
npmPackage: module.npmPackage,
|
|
48
|
+
installedAt: new Date().toISOString(),
|
|
49
|
+
enabled: true,
|
|
50
|
+
order: this.normalizeOrder(module.defaultOrder ?? 100)
|
|
51
|
+
});
|
|
52
|
+
this.writeConfig(config);
|
|
53
|
+
}
|
|
54
|
+
if (module) {
|
|
55
|
+
await this.applyLibraryPackage(module);
|
|
56
|
+
this.writeLibraryBootstrap();
|
|
57
|
+
await this.refreshLibraryAssets();
|
|
58
|
+
}
|
|
59
|
+
return this.listModules();
|
|
60
|
+
}
|
|
61
|
+
async uninstallModule(npmPackage) {
|
|
62
|
+
const config = this.readConfig();
|
|
63
|
+
const module = config.supportedModules.find(item => npmPackage === item.npmPackage || npmPackage === item.installPackage || npmPackage.startsWith(`${item.npmPackage}@`));
|
|
64
|
+
const packageName = module?.npmPackage ?? npmPackage;
|
|
65
|
+
if (module) {
|
|
66
|
+
this.removeLibraryEditorTemplates(module);
|
|
67
|
+
}
|
|
68
|
+
await this.runNpm(['uninstall', packageName]);
|
|
69
|
+
if (module) {
|
|
70
|
+
config.enabledModules = config.enabledModules.filter(item => item.id !== module.id);
|
|
71
|
+
this.writeConfig(config);
|
|
72
|
+
this.writeLibraryBootstrap();
|
|
73
|
+
await this.refreshLibraryAssets();
|
|
74
|
+
}
|
|
75
|
+
return this.listModules();
|
|
76
|
+
}
|
|
77
|
+
async updateLibraryState(options = {}) {
|
|
78
|
+
if (!options.id) {
|
|
79
|
+
throw new Error('Library id is required.');
|
|
80
|
+
}
|
|
81
|
+
const config = this.readConfig();
|
|
82
|
+
const module = config.supportedModules.find(item => item.id === options.id);
|
|
83
|
+
if (!module) {
|
|
84
|
+
throw new Error(`Library not found: ${options.id}`);
|
|
85
|
+
}
|
|
86
|
+
let enabledModule = config.enabledModules.find(item => item.id === module.id);
|
|
87
|
+
if (!enabledModule) {
|
|
88
|
+
enabledModule = {
|
|
89
|
+
id: module.id,
|
|
90
|
+
npmPackage: module.npmPackage,
|
|
91
|
+
installedAt: new Date().toISOString(),
|
|
92
|
+
enabled: options.enabled ?? false,
|
|
93
|
+
order: this.normalizeOrder(options.order ?? module.defaultOrder ?? 100)
|
|
94
|
+
};
|
|
95
|
+
config.enabledModules.push(enabledModule);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
enabledModule.enabled = options.enabled ?? enabledModule.enabled ?? true;
|
|
99
|
+
enabledModule.order = this.normalizeOrder(options.order ?? enabledModule.order ?? module.defaultOrder ?? 100);
|
|
100
|
+
}
|
|
101
|
+
this.writeConfig(config);
|
|
102
|
+
this.writeLibraryBootstrap();
|
|
103
|
+
await this.refreshLibraryAssets();
|
|
104
|
+
return this.listModules();
|
|
105
|
+
}
|
|
106
|
+
async createModule(options = {}) {
|
|
107
|
+
const baseName = this.toPascalCase(options.name || 'NewModule');
|
|
108
|
+
const className = this.withWKPrefix(baseName);
|
|
109
|
+
const order = this.normalizeOrder(options.order);
|
|
110
|
+
const dirName = this.toFolderName(baseName);
|
|
111
|
+
const dir = (0, path_1.join)(this.projectPath, 'assets', 'scripts', 'game', 'modules', dirName);
|
|
112
|
+
if ((0, fs_1.existsSync)(dir) && (0, fs_1.readdirSync)(dir).length > 0) {
|
|
113
|
+
throw new Error(`Module already exists: ${className}`);
|
|
114
|
+
}
|
|
115
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
116
|
+
const modelPath = (0, path_1.join)(dir, `${className}Model.ts`);
|
|
117
|
+
const controllerPath = (0, path_1.join)(dir, `${className}Controller.ts`);
|
|
118
|
+
const modulePath = (0, path_1.join)(dir, `${className}Module.ts`);
|
|
119
|
+
const files = [modelPath, controllerPath, modulePath];
|
|
120
|
+
if (!(0, fs_1.existsSync)(modelPath)) {
|
|
121
|
+
(0, fs_1.writeFileSync)(modelPath, this.modelScript(baseName, className), 'utf8');
|
|
122
|
+
}
|
|
123
|
+
if (!(0, fs_1.existsSync)(controllerPath)) {
|
|
124
|
+
(0, fs_1.writeFileSync)(controllerPath, this.controllerScript(baseName, className), 'utf8');
|
|
125
|
+
}
|
|
126
|
+
if (!(0, fs_1.existsSync)(modulePath)) {
|
|
127
|
+
(0, fs_1.writeFileSync)(modulePath, this.moduleScript(baseName, className, order), 'utf8');
|
|
128
|
+
}
|
|
129
|
+
this.upsertModuleEntry(dirName, className);
|
|
130
|
+
if (options.withView) {
|
|
131
|
+
const view = await this.createView({
|
|
132
|
+
moduleName: baseName,
|
|
133
|
+
viewName: 'Main',
|
|
134
|
+
layer: 'UI',
|
|
135
|
+
cache: true
|
|
136
|
+
});
|
|
137
|
+
files.push(...view.files);
|
|
138
|
+
}
|
|
139
|
+
await Editor.Message.request('asset-db', 'refresh-asset', 'db://assets/scripts/game/modules');
|
|
140
|
+
return {
|
|
141
|
+
moduleName: className,
|
|
142
|
+
folderPath: dir,
|
|
143
|
+
files
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async createView(options = {}) {
|
|
147
|
+
const baseName = this.toPascalCase(options.moduleName || 'NewModule');
|
|
148
|
+
const viewName = this.toPascalCase(options.viewName || 'View');
|
|
149
|
+
const className = this.withWKPrefix(baseName);
|
|
150
|
+
const viewClassName = `${className}${viewName}View`;
|
|
151
|
+
const folderName = this.toFolderName(baseName);
|
|
152
|
+
const dir = (0, path_1.join)(this.projectPath, 'assets', 'scripts', 'game', 'modules', folderName);
|
|
153
|
+
const viewsDir = (0, path_1.join)(dir, 'views');
|
|
154
|
+
(0, fs_1.mkdirSync)(viewsDir, { recursive: true });
|
|
155
|
+
const viewPath = (0, path_1.join)(viewsDir, `${viewClassName}.ts`);
|
|
156
|
+
const modulePath = (0, path_1.join)(dir, `${className}Module.ts`);
|
|
157
|
+
const controllerPath = (0, path_1.join)(dir, `${className}Controller.ts`);
|
|
158
|
+
const layer = options.layer || 'UI';
|
|
159
|
+
const cache = options.cache ?? true;
|
|
160
|
+
if ((0, fs_1.existsSync)(viewPath)) {
|
|
161
|
+
throw new Error(`View already exists: ${viewClassName}`);
|
|
162
|
+
}
|
|
163
|
+
(0, fs_1.writeFileSync)(viewPath, this.viewScript(viewClassName), 'utf8');
|
|
164
|
+
await this.requestWithTimeout('asset-db', 'refresh-asset', 'db://assets/scripts/game/modules');
|
|
165
|
+
const scriptUuid = await this.readScriptUuid(viewPath, 5000);
|
|
166
|
+
const compressedUuid = this.compressUuid(scriptUuid);
|
|
167
|
+
const prefabDirFS = (0, path_1.join)(this.projectPath, 'assets', 'resources', 'prefabs', folderName);
|
|
168
|
+
(0, fs_1.mkdirSync)(prefabDirFS, { recursive: true });
|
|
169
|
+
const prefabFileName = `${baseName}${viewName}.prefab`;
|
|
170
|
+
const prefabFilePath = (0, path_1.join)(prefabDirFS, prefabFileName);
|
|
171
|
+
(0, fs_1.writeFileSync)(prefabFilePath, this.prefabContent(viewClassName, compressedUuid), 'utf8');
|
|
172
|
+
await this.requestWithTimeout('asset-db', 'refresh-asset', 'db://assets/resources/prefabs');
|
|
173
|
+
await this.waitForFile(prefabFilePath + '.meta', 5000);
|
|
174
|
+
const prefabPath = `prefabs/${folderName}/${baseName}${viewName}`;
|
|
175
|
+
if (!(0, fs_1.existsSync)(modulePath)) {
|
|
176
|
+
(0, fs_1.writeFileSync)(modulePath, this.moduleScript(baseName, className, 100), 'utf8');
|
|
177
|
+
}
|
|
178
|
+
this.upsertViewInModule(modulePath, viewClassName, viewName, prefabPath, layer, cache);
|
|
179
|
+
return {
|
|
180
|
+
moduleName: className,
|
|
181
|
+
folderPath: dir,
|
|
182
|
+
files: [viewPath, modulePath, prefabFilePath],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async deleteView(options = {}) {
|
|
186
|
+
const baseName = this.toPascalCase(options.moduleName || 'NewModule');
|
|
187
|
+
const className = this.withWKPrefix(baseName);
|
|
188
|
+
const viewClassName = options.className || `${className}${this.toPascalCase(options.viewName || 'View')}View`;
|
|
189
|
+
const dir = (0, path_1.join)(this.projectPath, 'assets', 'scripts', 'game', 'modules', this.toFolderName(baseName));
|
|
190
|
+
const viewPath = (0, path_1.join)(dir, 'views', `${viewClassName}.ts`);
|
|
191
|
+
const modulePath = (0, path_1.join)(dir, `${className}Module.ts`);
|
|
192
|
+
if ((0, fs_1.existsSync)(viewPath)) {
|
|
193
|
+
(0, fs_1.unlinkSync)(viewPath);
|
|
194
|
+
}
|
|
195
|
+
if ((0, fs_1.existsSync)(modulePath)) {
|
|
196
|
+
this.removeViewFromModule(modulePath, viewClassName);
|
|
197
|
+
}
|
|
198
|
+
await Editor.Message.request('asset-db', 'refresh-asset', 'db://assets/scripts/game/modules');
|
|
199
|
+
return this.listProjectModules();
|
|
200
|
+
}
|
|
201
|
+
async updateModuleOrder(options = {}) {
|
|
202
|
+
const baseName = this.toPascalCase(options.moduleName || 'NewModule');
|
|
203
|
+
const className = this.withWKPrefix(baseName);
|
|
204
|
+
const modulePath = (0, path_1.join)(this.projectPath, 'assets', 'scripts', 'game', 'modules', this.toFolderName(baseName), `${className}Module.ts`);
|
|
205
|
+
if (!(0, fs_1.existsSync)(modulePath)) {
|
|
206
|
+
throw new Error(`Module declaration not found: ${className}Module.ts`);
|
|
207
|
+
}
|
|
208
|
+
const order = this.normalizeOrder(options.order);
|
|
209
|
+
let content = (0, fs_1.readFileSync)(modulePath, 'utf8');
|
|
210
|
+
if (/order:\s*-?\d+/.test(content)) {
|
|
211
|
+
content = content.replace(/order:\s*-?\d+/, `order: ${order}`);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
content = content.replace(/name:\s*'[^']+',/, match => `${match}\n order: ${order},`);
|
|
215
|
+
}
|
|
216
|
+
(0, fs_1.writeFileSync)(modulePath, content, 'utf8');
|
|
217
|
+
await Editor.Message.request('asset-db', 'refresh-asset', 'db://assets/scripts/game/modules');
|
|
218
|
+
return this.listProjectModules();
|
|
219
|
+
}
|
|
220
|
+
async createManager(options = {}) {
|
|
221
|
+
const baseName = this.toPascalCase(options.name || 'NewManager').replace(/Manager$/, '');
|
|
222
|
+
const className = `${this.withWKPrefix(baseName)}Manager`;
|
|
223
|
+
const exportName = this.toManagerExportName(baseName);
|
|
224
|
+
const dir = (0, path_1.join)(this.projectPath, 'assets', 'scripts', 'core', 'managers');
|
|
225
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
226
|
+
const managerPath = (0, path_1.join)(dir, `${className}.ts`);
|
|
227
|
+
if ((0, fs_1.existsSync)(managerPath)) {
|
|
228
|
+
throw new Error(`Manager already exists: ${className}`);
|
|
229
|
+
}
|
|
230
|
+
(0, fs_1.writeFileSync)(managerPath, this.managerScript(className, exportName), 'utf8');
|
|
231
|
+
await Editor.Message.request('asset-db', 'refresh-asset', 'db://assets/scripts/core/managers');
|
|
232
|
+
return {
|
|
233
|
+
moduleName: className,
|
|
234
|
+
folderPath: dir,
|
|
235
|
+
files: [managerPath]
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
listProjectModules() {
|
|
239
|
+
const modulesDir = (0, path_1.join)(this.projectPath, 'assets', 'scripts', 'game', 'modules');
|
|
240
|
+
if (!(0, fs_1.existsSync)(modulesDir))
|
|
241
|
+
return [];
|
|
242
|
+
return (0, fs_1.readdirSync)(modulesDir)
|
|
243
|
+
.map(entry => (0, path_1.join)(modulesDir, entry))
|
|
244
|
+
.filter(item => (0, fs_1.statSync)(item).isDirectory())
|
|
245
|
+
.map(dir => this.scanProjectModule(dir))
|
|
246
|
+
.filter((module) => Boolean(module))
|
|
247
|
+
.sort((a, b) => a.order - b.order || a.name.localeCompare(b.name));
|
|
248
|
+
}
|
|
249
|
+
diagnoseProject() {
|
|
250
|
+
const issues = [];
|
|
251
|
+
const modules = this.listProjectModules();
|
|
252
|
+
const config = this.readConfig();
|
|
253
|
+
const packageJson = this.readPackageJson();
|
|
254
|
+
const dependencies = {
|
|
255
|
+
...(packageJson.dependencies ?? {}),
|
|
256
|
+
...(packageJson.devDependencies ?? {})
|
|
257
|
+
};
|
|
258
|
+
const moduleNames = new Set();
|
|
259
|
+
for (const module of modules) {
|
|
260
|
+
if (moduleNames.has(module.name)) {
|
|
261
|
+
issues.push({
|
|
262
|
+
level: 'error',
|
|
263
|
+
code: 'MODULE_DUPLICATE',
|
|
264
|
+
message: `Duplicate project module: ${module.name}`,
|
|
265
|
+
target: module.displayPath
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
moduleNames.add(module.name);
|
|
269
|
+
if (!module.controllerPath) {
|
|
270
|
+
issues.push({
|
|
271
|
+
level: 'warn',
|
|
272
|
+
code: 'MODULE_CONTROLLER_MISSING',
|
|
273
|
+
message: `${module.className} has no controller script.`,
|
|
274
|
+
target: module.displayPath
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (!module.modulePath) {
|
|
278
|
+
issues.push({
|
|
279
|
+
level: 'warn',
|
|
280
|
+
code: 'MODULE_DECLARATION_MISSING',
|
|
281
|
+
message: `${module.className} has no module declaration script.`,
|
|
282
|
+
target: module.displayPath
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
this.inspectModuleViewPrefabs(module, issues);
|
|
286
|
+
}
|
|
287
|
+
for (const module of config.supportedModules) {
|
|
288
|
+
const installedVersion = dependencies[module.npmPackage];
|
|
289
|
+
const enabled = config.enabledModules.find(item => item.id === module.id);
|
|
290
|
+
if (enabled && !installedVersion) {
|
|
291
|
+
issues.push({
|
|
292
|
+
level: 'warn',
|
|
293
|
+
code: 'LIB_ENABLED_NOT_INSTALLED',
|
|
294
|
+
message: `${module.displayName} is enabled but not installed.`,
|
|
295
|
+
target: module.npmPackage
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (issues.length === 0) {
|
|
300
|
+
issues.push({
|
|
301
|
+
level: 'info',
|
|
302
|
+
code: 'PROJECT_OK',
|
|
303
|
+
message: 'No Wukong project issues found.'
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
return issues;
|
|
307
|
+
}
|
|
308
|
+
readConfig() {
|
|
309
|
+
if (!(0, fs_1.existsSync)(this.configPath)) {
|
|
310
|
+
return this.emptyConfig();
|
|
311
|
+
}
|
|
312
|
+
const config = JSON.parse((0, fs_1.readFileSync)(this.configPath, 'utf8'));
|
|
313
|
+
return {
|
|
314
|
+
schemaVersion: config.schemaVersion ?? 1,
|
|
315
|
+
framework: config.framework ?? {
|
|
316
|
+
name: '@wukong-kit/core',
|
|
317
|
+
version: '0.0.0'
|
|
318
|
+
},
|
|
319
|
+
enabledModules: config.enabledModules ?? [],
|
|
320
|
+
supportedModules: config.supportedModules ?? []
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
inspectModuleViewPrefabs(module, issues) {
|
|
324
|
+
if (!module.modulePath || !(0, fs_1.existsSync)(module.modulePath))
|
|
325
|
+
return;
|
|
326
|
+
const content = (0, fs_1.readFileSync)(module.modulePath, 'utf8');
|
|
327
|
+
const prefabMatches = content.matchAll(/prefab:\s*['"]([^'"]+)['"]/g);
|
|
328
|
+
for (const match of prefabMatches) {
|
|
329
|
+
const prefabPath = match[1];
|
|
330
|
+
const filePath = (0, path_1.join)(this.projectPath, 'assets', 'resources', `${prefabPath}.prefab`);
|
|
331
|
+
if ((0, fs_1.existsSync)(filePath))
|
|
332
|
+
continue;
|
|
333
|
+
issues.push({
|
|
334
|
+
level: 'error',
|
|
335
|
+
code: 'VIEW_PREFAB_MISSING',
|
|
336
|
+
message: `${module.className} references missing prefab: ${prefabPath}`,
|
|
337
|
+
target: module.modulePath
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
emptyConfig() {
|
|
342
|
+
return {
|
|
343
|
+
schemaVersion: 1,
|
|
344
|
+
framework: {
|
|
345
|
+
name: '@wukong-kit/core',
|
|
346
|
+
version: '0.0.0'
|
|
347
|
+
},
|
|
348
|
+
enabledModules: [],
|
|
349
|
+
supportedModules: []
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
writeConfig(config) {
|
|
353
|
+
(0, fs_1.writeFileSync)(this.configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
354
|
+
}
|
|
355
|
+
readPackageJson() {
|
|
356
|
+
if (!(0, fs_1.existsSync)(this.packageJsonPath))
|
|
357
|
+
return {};
|
|
358
|
+
return JSON.parse((0, fs_1.readFileSync)(this.packageJsonPath, 'utf8'));
|
|
359
|
+
}
|
|
360
|
+
fetchLatestVersion(npmPackage) {
|
|
361
|
+
return new Promise((resolve, reject) => {
|
|
362
|
+
(0, child_process_1.execFile)('npm', ['view', npmPackage, 'version'], { shell: process.platform === 'win32', timeout: 8000 }, (error, stdout) => {
|
|
363
|
+
if (error) {
|
|
364
|
+
reject(error);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
resolve(stdout.trim());
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
runNpm(args) {
|
|
372
|
+
return new Promise((resolve, reject) => {
|
|
373
|
+
(0, child_process_1.execFile)('npm', args, {
|
|
374
|
+
cwd: this.projectPath,
|
|
375
|
+
shell: process.platform === 'win32'
|
|
376
|
+
}, error => {
|
|
377
|
+
if (error) {
|
|
378
|
+
reject(error);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
resolve();
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
async applyLibraryPackage(module) {
|
|
386
|
+
const packageRoot = this.resolveInstalledPackageRoot(module.npmPackage);
|
|
387
|
+
if (!packageRoot)
|
|
388
|
+
return;
|
|
389
|
+
const packageManifest = this.readLibraryPackageManifest(packageRoot);
|
|
390
|
+
const templates = packageManifest.assetTemplates ?? module.assetTemplates ?? [];
|
|
391
|
+
for (const template of templates) {
|
|
392
|
+
const source = (0, path_1.join)(packageRoot, template.from);
|
|
393
|
+
const target = (0, path_1.join)(this.projectPath, template.to);
|
|
394
|
+
if (!(0, fs_1.existsSync)(source))
|
|
395
|
+
continue;
|
|
396
|
+
(0, fs_1.mkdirSync)(target, { recursive: true });
|
|
397
|
+
this.copyDirectory(source, target);
|
|
398
|
+
}
|
|
399
|
+
const editorTemplates = packageManifest.editorTemplates ?? module.editorTemplates ?? [];
|
|
400
|
+
for (const template of editorTemplates) {
|
|
401
|
+
const source = (0, path_1.join)(packageRoot, template.from);
|
|
402
|
+
const target = (0, path_1.join)(this.projectPath, template.to);
|
|
403
|
+
if (!(0, fs_1.existsSync)(source))
|
|
404
|
+
continue;
|
|
405
|
+
(0, fs_1.mkdirSync)(target, { recursive: true });
|
|
406
|
+
this.copyDirectory(source, target);
|
|
407
|
+
this.enableProjectExtension(target);
|
|
408
|
+
await this.tryEnableKnownEditorExtension(target);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
enableProjectExtension(extensionPath) {
|
|
412
|
+
const packageJsonPath = (0, path_1.join)(extensionPath, 'package.json');
|
|
413
|
+
if (!(0, fs_1.existsSync)(packageJsonPath))
|
|
414
|
+
return;
|
|
415
|
+
const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf8'));
|
|
416
|
+
const packageName = packageJson.name || (0, path_1.basename)(extensionPath);
|
|
417
|
+
const packagesConfigPath = (0, path_1.join)(this.projectPath, 'profiles', 'v2', 'editor', 'packages.json');
|
|
418
|
+
const config = (0, fs_1.existsSync)(packagesConfigPath)
|
|
419
|
+
? JSON.parse((0, fs_1.readFileSync)(packagesConfigPath, 'utf8'))
|
|
420
|
+
: { 'disable-packages': {} };
|
|
421
|
+
const disabled = config['disable-packages'] ?? {};
|
|
422
|
+
delete disabled[packageName];
|
|
423
|
+
delete disabled[(0, path_1.basename)(extensionPath)];
|
|
424
|
+
delete disabled[`project://${extensionPath.replace(/\\/g, '/')}`];
|
|
425
|
+
config['disable-packages'] = disabled;
|
|
426
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(packagesConfigPath), { recursive: true });
|
|
427
|
+
(0, fs_1.writeFileSync)(packagesConfigPath, JSON.stringify(config, null, 2), 'utf8');
|
|
428
|
+
}
|
|
429
|
+
async tryEnableKnownEditorExtension(extensionPath) {
|
|
430
|
+
const editorPackage = Editor.Package;
|
|
431
|
+
if (!editorPackage?.getPackages)
|
|
432
|
+
return;
|
|
433
|
+
const normalizedTarget = (0, path_1.resolve)(extensionPath);
|
|
434
|
+
let knownPackage = this.findKnownEditorPackage(normalizedTarget);
|
|
435
|
+
if (!knownPackage) {
|
|
436
|
+
await this.importEditorExtension(extensionPath);
|
|
437
|
+
await this.sleep(800);
|
|
438
|
+
knownPackage = this.findKnownEditorPackage(normalizedTarget);
|
|
439
|
+
}
|
|
440
|
+
if (!knownPackage)
|
|
441
|
+
return;
|
|
442
|
+
await this.enableEditorExtension(knownPackage.path);
|
|
443
|
+
}
|
|
444
|
+
async importEditorExtension(extensionPath) {
|
|
445
|
+
await this.requestEditorExtensionMessage('import-package-by-path', [extensionPath]);
|
|
446
|
+
await this.requestEditorExtensionMessage('scanning');
|
|
447
|
+
}
|
|
448
|
+
async enableEditorExtension(extensionPath) {
|
|
449
|
+
try {
|
|
450
|
+
await Editor.Message.request('extension', 'enable', extensionPath);
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
try {
|
|
454
|
+
await Editor.Package?.enable?.(extensionPath, { force: true });
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
// Leave the extension enabled in project profile; it can still be enabled from Extension Manager.
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
await this.requestEditorExtensionMessage('reload', extensionPath);
|
|
461
|
+
try {
|
|
462
|
+
await Editor.Package?.reload?.(extensionPath);
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
// Reload is best-effort because Creator versions differ slightly here.
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async requestEditorExtensionMessage(message, ...args) {
|
|
469
|
+
try {
|
|
470
|
+
await Editor.Message.request('extension', message, ...args);
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
// These messages are available in Creator 3.8.x. If a patch version differs, profile enabling still remains.
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
findKnownEditorPackage(normalizedTarget) {
|
|
477
|
+
return Editor.Package?.getPackages?.({ invalid: true })
|
|
478
|
+
.find(item => (0, path_1.resolve)(item.path) === normalizedTarget);
|
|
479
|
+
}
|
|
480
|
+
sleep(ms) {
|
|
481
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
482
|
+
}
|
|
483
|
+
removeLibraryEditorTemplates(module) {
|
|
484
|
+
const packageRoot = this.resolveInstalledPackageRoot(module.npmPackage);
|
|
485
|
+
const packageManifest = packageRoot ? this.readLibraryPackageManifest(packageRoot) : {};
|
|
486
|
+
const editorTemplates = packageManifest.editorTemplates ?? module.editorTemplates ?? [];
|
|
487
|
+
for (const template of editorTemplates) {
|
|
488
|
+
const target = (0, path_1.join)(this.projectPath, template.to);
|
|
489
|
+
if (!(0, fs_1.existsSync)(target) || !this.isSafeProjectPath(target))
|
|
490
|
+
continue;
|
|
491
|
+
this.removeDirectory(target);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
copyDirectory(source, target) {
|
|
495
|
+
(0, fs_1.mkdirSync)(target, { recursive: true });
|
|
496
|
+
for (const entry of (0, fs_1.readdirSync)(source)) {
|
|
497
|
+
const sourcePath = (0, path_1.join)(source, entry);
|
|
498
|
+
const targetPath = (0, path_1.join)(target, entry);
|
|
499
|
+
const stats = (0, fs_1.statSync)(sourcePath);
|
|
500
|
+
if (stats.isDirectory()) {
|
|
501
|
+
this.copyDirectory(sourcePath, targetPath);
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(targetPath), { recursive: true });
|
|
505
|
+
(0, fs_1.copyFileSync)(sourcePath, targetPath);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
removeDirectory(target) {
|
|
509
|
+
if (!(0, fs_1.existsSync)(target))
|
|
510
|
+
return;
|
|
511
|
+
for (const entry of (0, fs_1.readdirSync)(target)) {
|
|
512
|
+
const entryPath = (0, path_1.join)(target, entry);
|
|
513
|
+
const stats = (0, fs_1.statSync)(entryPath);
|
|
514
|
+
if (stats.isDirectory()) {
|
|
515
|
+
this.removeDirectory(entryPath);
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
(0, fs_1.unlinkSync)(entryPath);
|
|
519
|
+
}
|
|
520
|
+
(0, fs_1.rmdirSync)(target);
|
|
521
|
+
}
|
|
522
|
+
isSafeProjectPath(target) {
|
|
523
|
+
const projectRoot = (0, path_1.resolve)(this.projectPath);
|
|
524
|
+
const resolvedTarget = (0, path_1.resolve)(target);
|
|
525
|
+
return resolvedTarget === projectRoot || resolvedTarget.startsWith(projectRoot + '\\') || resolvedTarget.startsWith(projectRoot + '/');
|
|
526
|
+
}
|
|
527
|
+
writeLibraryBootstrap() {
|
|
528
|
+
const config = this.readConfig();
|
|
529
|
+
const packageJson = this.readPackageJson();
|
|
530
|
+
const dependencies = {
|
|
531
|
+
...(packageJson.dependencies ?? {}),
|
|
532
|
+
...(packageJson.devDependencies ?? {})
|
|
533
|
+
};
|
|
534
|
+
const enabledModules = config.enabledModules
|
|
535
|
+
.filter(item => item.enabled ?? true)
|
|
536
|
+
.map(item => {
|
|
537
|
+
const module = config.supportedModules.find(candidate => candidate.id === item.id);
|
|
538
|
+
if (!module || !dependencies[module.npmPackage] || (module.initMode ?? 'none') !== 'auto')
|
|
539
|
+
return null;
|
|
540
|
+
const packageRoot = this.resolveInstalledPackageRoot(module.npmPackage);
|
|
541
|
+
const packageManifest = packageRoot ? this.readLibraryPackageManifest(packageRoot) : {};
|
|
542
|
+
const runtimeInit = packageManifest.runtimeInit ?? module.runtimeInit;
|
|
543
|
+
if (!runtimeInit)
|
|
544
|
+
return null;
|
|
545
|
+
return {
|
|
546
|
+
id: module.id,
|
|
547
|
+
order: this.normalizeOrder(item.order ?? module.defaultOrder ?? 100),
|
|
548
|
+
runtimeInit
|
|
549
|
+
};
|
|
550
|
+
})
|
|
551
|
+
.filter((item) => Boolean(item))
|
|
552
|
+
.sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
|
|
553
|
+
const bootstrapPath = (0, path_1.join)(this.projectPath, 'assets', 'scripts', 'core', 'WKLibBootstrap.ts');
|
|
554
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(this.projectPath, 'assets', 'scripts', 'core'), { recursive: true });
|
|
555
|
+
if (enabledModules.length === 0) {
|
|
556
|
+
(0, fs_1.writeFileSync)(bootstrapPath, 'export async function initWukongLibraries(): Promise<void> {}\n', 'utf8');
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const imports = enabledModules
|
|
560
|
+
.map((item, index) => `import { ${item.runtimeInit.export} as initLibrary${index} } from '${item.runtimeInit.import}';`)
|
|
561
|
+
.join('\n');
|
|
562
|
+
const calls = enabledModules
|
|
563
|
+
.map((item, index) => ` await initLibrary${index}();`)
|
|
564
|
+
.join('\n');
|
|
565
|
+
(0, fs_1.writeFileSync)(bootstrapPath, `${imports}\n\nexport async function initWukongLibraries(): Promise<void> {\n${calls}\n}\n`, 'utf8');
|
|
566
|
+
}
|
|
567
|
+
readLibraryPackageManifest(packageRoot) {
|
|
568
|
+
const manifestPath = (0, path_1.join)(packageRoot, 'wukong-lib.json');
|
|
569
|
+
if (!(0, fs_1.existsSync)(manifestPath))
|
|
570
|
+
return {};
|
|
571
|
+
return JSON.parse((0, fs_1.readFileSync)(manifestPath, 'utf8'));
|
|
572
|
+
}
|
|
573
|
+
resolveInstalledPackageRoot(npmPackage) {
|
|
574
|
+
const parts = npmPackage.split('/');
|
|
575
|
+
const packageRoot = npmPackage.startsWith('@')
|
|
576
|
+
? (0, path_1.join)(this.projectPath, 'node_modules', parts[0], parts[1] ?? '')
|
|
577
|
+
: (0, path_1.join)(this.projectPath, 'node_modules', npmPackage);
|
|
578
|
+
return (0, fs_1.existsSync)(packageRoot) ? packageRoot : null;
|
|
579
|
+
}
|
|
580
|
+
async refreshLibraryAssets() {
|
|
581
|
+
await Editor.Message.request('asset-db', 'refresh-asset', 'db://assets').catch(() => undefined);
|
|
582
|
+
}
|
|
583
|
+
normalizeVersion(version) {
|
|
584
|
+
return version.replace(/^[~^]/, '').replace(/^latest$/, '');
|
|
585
|
+
}
|
|
586
|
+
toPascalCase(input) {
|
|
587
|
+
const name = input
|
|
588
|
+
.replace(/[^a-zA-Z0-9]+/g, ' ')
|
|
589
|
+
.split(' ')
|
|
590
|
+
.filter(Boolean)
|
|
591
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
592
|
+
.join('');
|
|
593
|
+
return name || 'NewModule';
|
|
594
|
+
}
|
|
595
|
+
withWKPrefix(name) {
|
|
596
|
+
return name.startsWith('WK') ? name : `WK${name}`;
|
|
597
|
+
}
|
|
598
|
+
toFolderName(name) {
|
|
599
|
+
return `${name.charAt(0).toLowerCase()}${name.slice(1)}`;
|
|
600
|
+
}
|
|
601
|
+
normalizeOrder(order) {
|
|
602
|
+
const value = Number(order);
|
|
603
|
+
if (!Number.isFinite(value))
|
|
604
|
+
return 100;
|
|
605
|
+
return Math.trunc(value);
|
|
606
|
+
}
|
|
607
|
+
upsertModuleEntry(folderName, className) {
|
|
608
|
+
const modulesDir = (0, path_1.join)(this.projectPath, 'assets', 'scripts', 'game', 'modules');
|
|
609
|
+
(0, fs_1.mkdirSync)(modulesDir, { recursive: true });
|
|
610
|
+
const entryPath = (0, path_1.join)(modulesDir, 'WKModules.ts');
|
|
611
|
+
const importLine = `import './${folderName}/${className}Module';`;
|
|
612
|
+
const content = (0, fs_1.existsSync)(entryPath) ? (0, fs_1.readFileSync)(entryPath, 'utf8') : '// Wukong module side-effect imports.\n';
|
|
613
|
+
if (content.includes(importLine))
|
|
614
|
+
return;
|
|
615
|
+
(0, fs_1.writeFileSync)(entryPath, `${content.trimEnd()}\n${importLine}\n`, 'utf8');
|
|
616
|
+
}
|
|
617
|
+
modelScript(baseName, className) {
|
|
618
|
+
return `import { WKModel } from '@wukong-kit/core';
|
|
619
|
+
|
|
620
|
+
export class ${className}Model extends WKModel {
|
|
621
|
+
readonly name = '${className}Model';
|
|
622
|
+
|
|
623
|
+
enabled = true;
|
|
624
|
+
moduleName = '${baseName}';
|
|
625
|
+
}
|
|
626
|
+
`;
|
|
627
|
+
}
|
|
628
|
+
controllerScript(baseName, className) {
|
|
629
|
+
return `import { App, WKController, WKLogger } from '@wukong-kit/core';
|
|
630
|
+
import { ${className}Model } from './${className}Model';
|
|
631
|
+
|
|
632
|
+
export class ${className}Controller extends WKController<${className}Model> {
|
|
633
|
+
onInit(): void {
|
|
634
|
+
this.model = App.getModel(${className}Model);
|
|
635
|
+
WKLogger.info('${baseName} module ready.', this.model.enabled);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
`;
|
|
639
|
+
}
|
|
640
|
+
moduleScript(baseName, className, order) {
|
|
641
|
+
return `import { defineWKModule, WKLayer } from '@wukong-kit/core';
|
|
642
|
+
import { ${className}Controller } from './${className}Controller';
|
|
643
|
+
import { ${className}Model } from './${className}Model';
|
|
644
|
+
|
|
645
|
+
export const ${className}Module = defineWKModule({
|
|
646
|
+
name: '${baseName}',
|
|
647
|
+
order: ${order},
|
|
648
|
+
model: ${className}Model,
|
|
649
|
+
controller: ${className}Controller,
|
|
650
|
+
views: [
|
|
651
|
+
// <wk-views>
|
|
652
|
+
// </wk-views>
|
|
653
|
+
],
|
|
654
|
+
});
|
|
655
|
+
`;
|
|
656
|
+
}
|
|
657
|
+
viewScript(className) {
|
|
658
|
+
return `import { _decorator } from 'cc';
|
|
659
|
+
import { WKView } from '@wukong-kit/core';
|
|
660
|
+
|
|
661
|
+
const { ccclass } = _decorator;
|
|
662
|
+
|
|
663
|
+
@ccclass('${className}')
|
|
664
|
+
export class ${className} extends WKView {
|
|
665
|
+
override init(): void {
|
|
666
|
+
// Bind 2D node presentation here.
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
`;
|
|
670
|
+
}
|
|
671
|
+
prefabContent(nodeName, compressedScriptUuid) {
|
|
672
|
+
const nodeFileId = this.generateUUID();
|
|
673
|
+
const compFileId = this.generateUUID();
|
|
674
|
+
const data = [
|
|
675
|
+
{
|
|
676
|
+
__type__: 'cc.Prefab',
|
|
677
|
+
_name: nodeName,
|
|
678
|
+
_objFlags: 0,
|
|
679
|
+
__editorExtras__: {},
|
|
680
|
+
_native: '',
|
|
681
|
+
data: { __id__: 1 },
|
|
682
|
+
optimizationPolicy: 0,
|
|
683
|
+
persistent: false,
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
__type__: 'cc.Node',
|
|
687
|
+
_name: nodeName,
|
|
688
|
+
_objFlags: 0,
|
|
689
|
+
__editorExtras__: {},
|
|
690
|
+
_parent: null,
|
|
691
|
+
_children: [],
|
|
692
|
+
_active: true,
|
|
693
|
+
_components: [{ __id__: 2 }],
|
|
694
|
+
_prefab: { __id__: 4 },
|
|
695
|
+
_lpos: { __type__: 'cc.Vec3', x: 0, y: 0, z: 0 },
|
|
696
|
+
_lrot: { __type__: 'cc.Quat', x: 0, y: 0, z: 0, w: 1 },
|
|
697
|
+
_lscale: { __type__: 'cc.Vec3', x: 1, y: 1, z: 1 },
|
|
698
|
+
_mobility: 0,
|
|
699
|
+
_layer: 1073741824,
|
|
700
|
+
_euler: { __type__: 'cc.Vec3', x: 0, y: 0, z: 0 },
|
|
701
|
+
_id: '',
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
__type__: compressedScriptUuid,
|
|
705
|
+
_name: '',
|
|
706
|
+
_objFlags: 0,
|
|
707
|
+
__editorExtras__: {},
|
|
708
|
+
node: { __id__: 1 },
|
|
709
|
+
_enabled: true,
|
|
710
|
+
__prefab: { __id__: 3 },
|
|
711
|
+
_id: '',
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
__type__: 'cc.CompPrefabInfo',
|
|
715
|
+
fileId: compFileId,
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
__type__: 'cc.PrefabInfo',
|
|
719
|
+
root: { __id__: 1 },
|
|
720
|
+
asset: { __id__: 0 },
|
|
721
|
+
fileId: nodeFileId,
|
|
722
|
+
instance: null,
|
|
723
|
+
targetOverrides: null,
|
|
724
|
+
},
|
|
725
|
+
];
|
|
726
|
+
return JSON.stringify(data, null, 2) + '\n';
|
|
727
|
+
}
|
|
728
|
+
requestWithTimeout(pkg, msg, ...args) {
|
|
729
|
+
const req = Editor.Message.request(pkg, msg, ...args);
|
|
730
|
+
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error(`Editor.Message.request timeout: ${pkg}:${msg}`)), 10000));
|
|
731
|
+
return Promise.race([req, timeout]);
|
|
732
|
+
}
|
|
733
|
+
compressUuid(uuid) {
|
|
734
|
+
const hex = uuid.replace(/-/g, '');
|
|
735
|
+
if (!/^[0-9a-fA-F]{32}$/.test(hex)) {
|
|
736
|
+
throw new Error(`Invalid script uuid: ${uuid}`);
|
|
737
|
+
}
|
|
738
|
+
const base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
739
|
+
let result = hex.slice(0, 5);
|
|
740
|
+
for (let i = 5; i < hex.length; i += 3) {
|
|
741
|
+
const value = parseInt(hex.slice(i, i + 3).padEnd(3, '0'), 16);
|
|
742
|
+
result += base64chars[(value >> 6) & 63];
|
|
743
|
+
result += base64chars[value & 63];
|
|
744
|
+
}
|
|
745
|
+
return result;
|
|
746
|
+
}
|
|
747
|
+
generateUUID() {
|
|
748
|
+
// Fallback for environments where crypto.randomUUID is unavailable (e.g. Cocos extension sandbox)
|
|
749
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
750
|
+
const r = (Math.random() * 16) | 0;
|
|
751
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
752
|
+
return v.toString(16);
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
async readScriptUuid(viewPath, timeoutMs) {
|
|
756
|
+
const metaPath = viewPath + '.meta';
|
|
757
|
+
const start = Date.now();
|
|
758
|
+
let attempts = 0;
|
|
759
|
+
while (!(0, fs_1.existsSync)(metaPath)) {
|
|
760
|
+
if (Date.now() - start >= timeoutMs) {
|
|
761
|
+
throw new Error(`Timed out waiting for script .meta after ${attempts} attempts: ${metaPath}`);
|
|
762
|
+
}
|
|
763
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
764
|
+
attempts++;
|
|
765
|
+
}
|
|
766
|
+
const raw = (0, fs_1.readFileSync)(metaPath, 'utf8');
|
|
767
|
+
const meta = JSON.parse(raw);
|
|
768
|
+
if (!meta.uuid) {
|
|
769
|
+
throw new Error(`Script .meta missing uuid: ${metaPath}`);
|
|
770
|
+
}
|
|
771
|
+
return meta.uuid;
|
|
772
|
+
}
|
|
773
|
+
waitForFile(path, timeoutMs) {
|
|
774
|
+
const start = Date.now();
|
|
775
|
+
return new Promise((resolve, reject) => {
|
|
776
|
+
const check = () => {
|
|
777
|
+
if ((0, fs_1.existsSync)(path)) {
|
|
778
|
+
resolve();
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (Date.now() - start >= timeoutMs) {
|
|
782
|
+
reject(new Error(`Timed out waiting for file: ${path}`));
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
setTimeout(check, 100);
|
|
786
|
+
};
|
|
787
|
+
check();
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
managerScript(className, exportName) {
|
|
791
|
+
return `import { WKSingleton } from '@wukong-kit/core';
|
|
792
|
+
|
|
793
|
+
export class ${className} extends WKSingleton {
|
|
794
|
+
init(): void {}
|
|
795
|
+
|
|
796
|
+
dispose(): void {
|
|
797
|
+
${className}.clear();
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
export const ${exportName} = ${className}.get();
|
|
802
|
+
`;
|
|
803
|
+
}
|
|
804
|
+
upsertViewInModule(modulePath, viewClassName, viewName, prefabPath, layer, cache) {
|
|
805
|
+
let content = (0, fs_1.readFileSync)(modulePath, 'utf8');
|
|
806
|
+
const importLine = `import { ${viewClassName} } from './views/${viewClassName}';`;
|
|
807
|
+
if (!content.includes(importLine)) {
|
|
808
|
+
const exportIndex = content.indexOf('export const ');
|
|
809
|
+
content = exportIndex >= 0 ? `${content.slice(0, exportIndex)}${importLine}\n${content.slice(exportIndex)}` : `${importLine}\n${content}`;
|
|
810
|
+
}
|
|
811
|
+
if (!content.includes('// <wk-views>')) {
|
|
812
|
+
content = content.replace(/views:\s*\[/, 'views: [\n // <wk-views>\n // </wk-views>');
|
|
813
|
+
}
|
|
814
|
+
const entry = ` {
|
|
815
|
+
name: '${viewName}',
|
|
816
|
+
type: ${viewClassName},
|
|
817
|
+
prefab: '${prefabPath}',
|
|
818
|
+
layer: WKLayer.${layer},
|
|
819
|
+
cache: ${cache},
|
|
820
|
+
},`;
|
|
821
|
+
if (!content.includes(`type: ${viewClassName}`)) {
|
|
822
|
+
content = content.replace(' // </wk-views>', `${entry}\n // </wk-views>`);
|
|
823
|
+
}
|
|
824
|
+
(0, fs_1.writeFileSync)(modulePath, content, 'utf8');
|
|
825
|
+
}
|
|
826
|
+
removeViewFromModule(modulePath, viewClassName) {
|
|
827
|
+
let content = (0, fs_1.readFileSync)(modulePath, 'utf8');
|
|
828
|
+
const importPattern = new RegExp(`^import \\{ ${viewClassName} \\} from './views/${viewClassName}';\\r?\\n`, 'm');
|
|
829
|
+
content = content.replace(importPattern, '');
|
|
830
|
+
const viewEntryPattern = new RegExp(`\\s*\\{\\r?\\n\\s*name: '[^']+',\\r?\\n\\s*type: ${viewClassName},\\r?\\n\\s*prefab: '[^']+',\\r?\\n\\s*layer: WKLayer\\.[A-Za-z]+,\\r?\\n\\s*cache: (?:true|false),\\r?\\n\\s*\\},`, 'm');
|
|
831
|
+
content = content.replace(viewEntryPattern, '');
|
|
832
|
+
(0, fs_1.writeFileSync)(modulePath, content, 'utf8');
|
|
833
|
+
}
|
|
834
|
+
scanProjectModule(dir) {
|
|
835
|
+
const files = (0, fs_1.readdirSync)(dir).filter(file => file.endsWith('.ts'));
|
|
836
|
+
const moduleFile = files.find(file => file.endsWith('Module.ts')) ?? null;
|
|
837
|
+
const modelFile = files.find(file => file.endsWith('Model.ts')) ?? null;
|
|
838
|
+
const controllerFile = files.find(file => file.endsWith('Controller.ts')) ?? null;
|
|
839
|
+
const className = moduleFile ? moduleFile.replace(/Module\.ts$/, '') : this.withWKPrefix(this.toPascalCase((0, path_1.basename)(dir)));
|
|
840
|
+
const name = className.replace(/^WK/, '');
|
|
841
|
+
const order = moduleFile ? this.readModuleOrder((0, path_1.join)(dir, moduleFile)) : 100;
|
|
842
|
+
const viewsDir = (0, path_1.join)(dir, 'views');
|
|
843
|
+
const views = (0, fs_1.existsSync)(viewsDir)
|
|
844
|
+
? (0, fs_1.readdirSync)(viewsDir)
|
|
845
|
+
.filter(file => file.endsWith('View.ts'))
|
|
846
|
+
.map(file => ({
|
|
847
|
+
name: this.toViewDisplayName(file.replace(/\.ts$/, ''), name),
|
|
848
|
+
className: file.replace(/\.ts$/, ''),
|
|
849
|
+
scriptPath: (0, path_1.join)(viewsDir, file),
|
|
850
|
+
displayPath: this.toProjectRelativePath((0, path_1.join)(viewsDir, file))
|
|
851
|
+
}))
|
|
852
|
+
: [];
|
|
853
|
+
if (!moduleFile && !modelFile && !controllerFile && views.length === 0)
|
|
854
|
+
return null;
|
|
855
|
+
return {
|
|
856
|
+
name,
|
|
857
|
+
className,
|
|
858
|
+
order,
|
|
859
|
+
folderPath: dir,
|
|
860
|
+
displayPath: this.toProjectRelativePath(dir),
|
|
861
|
+
modelPath: modelFile ? (0, path_1.join)(dir, modelFile) : null,
|
|
862
|
+
controllerPath: controllerFile ? (0, path_1.join)(dir, controllerFile) : null,
|
|
863
|
+
modulePath: moduleFile ? (0, path_1.join)(dir, moduleFile) : null,
|
|
864
|
+
displayFiles: [moduleFile, modelFile, controllerFile].filter((file) => Boolean(file)).map(file => this.toProjectRelativePath((0, path_1.join)(dir, file))),
|
|
865
|
+
views
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
toViewDisplayName(className, moduleName) {
|
|
869
|
+
return (className
|
|
870
|
+
.replace(/^WK/, '')
|
|
871
|
+
.replace(new RegExp(`^${moduleName}`), '')
|
|
872
|
+
.replace(/View$/, '') || 'Main');
|
|
873
|
+
}
|
|
874
|
+
toManagerExportName(name) {
|
|
875
|
+
const cleanName = name.replace(/^WK/, '');
|
|
876
|
+
return `${cleanName.charAt(0).toLowerCase()}${cleanName.slice(1)}Manager`;
|
|
877
|
+
}
|
|
878
|
+
readModuleOrder(modulePath) {
|
|
879
|
+
const content = (0, fs_1.readFileSync)(modulePath, 'utf8');
|
|
880
|
+
const match = content.match(/order:\s*(-?\d+)/);
|
|
881
|
+
return this.normalizeOrder(match?.[1]);
|
|
882
|
+
}
|
|
883
|
+
toProjectRelativePath(path) {
|
|
884
|
+
return path
|
|
885
|
+
.replace(this.projectPath, '')
|
|
886
|
+
.replace(/^[/\\]/, '')
|
|
887
|
+
.replace(/\\/g, '/');
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
exports.ModuleRegistryService = ModuleRegistryService;
|
|
891
|
+
//# sourceMappingURL=ModuleRegistryService.js.map
|