diff --git a/.gitignore b/.gitignore index e7cdf2f60..7790334ba 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ e2e/reports/ # auto generated files src/i18n/types/generated.d.ts +CLAUDE.md diff --git a/src/core/scripting/index.ts b/src/core/scripting/index.ts index e379ef8e7..255035c8e 100644 --- a/src/core/scripting/index.ts +++ b/src/core/scripting/index.ts @@ -15,38 +15,25 @@ let executor: Executor | null = null; class GlobalEnv { public async record(fn: () => Promise) { this.clear(); - this._queue.push(async () => { - const beforeKeys = Object.keys(globalThis); - await fn(); - const afterKeys = Object.keys(globalThis); - for (const afterKey of afterKeys) { - if (!beforeKeys.includes(afterKey)) { - this._incrementalKeys.add(afterKey); - } + const beforeKeys = Object.keys(globalThis); + await fn(); + const afterKeys = Object.keys(globalThis); + for (const afterKey of afterKeys) { + if (!beforeKeys.includes(afterKey)) { + this._incrementalKeys.add(afterKey); } - console.debug(`Incremental keys: ${Array.from(this._incrementalKeys)}`); - }); - await this.processQueue(); // 处理队列 + } + console.debug(`Incremental keys: ${Array.from(this._incrementalKeys)}`); } private clear() { - this._queue.push(async () => { - for (const incrementalKey of this._incrementalKeys) { - delete (globalThis as any)[incrementalKey]; - } - this._incrementalKeys.clear(); - }); - } - - private async processQueue() { - while (this._queue.length > 0) { - const next = this._queue.shift(); - if (next) await next(); // 执行队列中的下一个任务 + for (const incrementalKey of this._incrementalKeys) { + delete (globalThis as any)[incrementalKey]; } + this._incrementalKeys.clear(); } private _incrementalKeys = new Set(); - private _queue: (() => Promise)[] = []; } const globalEnv = new GlobalEnv(); @@ -153,7 +140,7 @@ class ScriptManager { this._pendingCompileTimer = setTimeout(async () => { if (this.isCompiling()) { this.postCompileScripts(delay); - return taskId; + return; } this._pendingCompileTimer = null; @@ -224,10 +211,6 @@ class ScriptManager { executor.addPolyfillFile(require.resolve('@cocos/build-polyfills/prebuilt/editor/bundle')); } - if (!executor) { - console.error('Failed to init executor'); - return; - } executor.setPluginScripts(pluginScripts || []); await executor.reload(); }); diff --git a/src/core/scripting/language-service/index.ts b/src/core/scripting/language-service/index.ts index 941e452c3..c5669fcd7 100644 --- a/src/core/scripting/language-service/index.ts +++ b/src/core/scripting/language-service/index.ts @@ -141,6 +141,7 @@ export class LanguageServiceAdapter { protected _executingCommandID: Command['id'] = ''; protected readonly _changedFileSet: Set = new Set(); protected readonly _afterOutputTasks: (() => void)[] = []; + private readonly _delegateCallback: (changes: ModifiedAssetChange[]) => Promise; constructor( protected readonly _tsconfigPath: FilePath, protected readonly _currentDirectory: FilePath, @@ -153,11 +154,16 @@ export class LanguageServiceAdapter { this._parseConfigFileHost = new ParseConfigFileHostAdapter(_currentDirectory); this.host = new LanguageServiceHostAdapter(this._parseConfigFileHost, this._tsconfigPath, this._currentDirectory, this._compilerOptions); this.languageService = ts.createLanguageService(this.host, undefined, ts.LanguageServiceMode.Semantic); - this._beforeBuildDelegate.add(async (assetChanges) => { + this._delegateCallback = async (assetChanges) => { assetChanges.forEach(item => item.oldFilePath && item.newFilePath && this.requestRenameFile(item.oldFilePath, item.newFilePath)); await this.finishCommand(assetChanges); - }); + }; + this._beforeBuildDelegate.add(this._delegateCallback); + + } + public dispose() { + this._beforeBuildDelegate.remove(this._delegateCallback); } public isExecuting(commandID: string): boolean { @@ -203,7 +209,7 @@ export class LanguageServiceAdapter { /** 请求更新路径 */ public async requestRenameFile(oldFilePath: FilePath, newFilePath: FilePath): Promise { - if (oldFilePath && newFilePath && oldFilePath.endsWith('.ts') && newFilePath.endsWith('.ts') || !extname(oldFilePath)) { + if ((oldFilePath && newFilePath && oldFilePath.endsWith('.ts') && newFilePath.endsWith('.ts')) || (oldFilePath && newFilePath && !extname(oldFilePath))) { if (oldFilePath === newFilePath) { return; } diff --git a/src/core/scripting/packer-driver/asset-db-interop.ts b/src/core/scripting/packer-driver/asset-db-interop.ts index ddbd88ec8..1fb7260ed 100644 --- a/src/core/scripting/packer-driver/asset-db-interop.ts +++ b/src/core/scripting/packer-driver/asset-db-interop.ts @@ -63,6 +63,11 @@ export class AssetDbInterop { onAssetChange( changeInfo: AssetChangeInfo ) { + const importer = changeInfo.importer; + if (!(importer === 'javascript' || importer === 'typescript')) { + return; + } + const filePath = resolveFileName(changeInfo.filePath); const uuid = changeInfo.uuid; const assetChange: AssetChange = { @@ -73,11 +78,7 @@ export class AssetDbInterop { type: changeInfo.type === AssetActionEnum.none ? AssetActionEnum.change : changeInfo.type, isPluginScript: isPluginScript(changeInfo.userData), }; - - const importer = changeInfo.importer; - if (!(importer === 'javascript' || importer === 'typescript')) { - return; - } + let info : TypeScriptAssetInfoCache | null = null; if (importer === 'typescript') { info = mapperForTypeScriptAssetInfoCache(changeInfo); diff --git a/src/core/scripting/packer-driver/index.ts b/src/core/scripting/packer-driver/index.ts index 0bc019797..b55d20c33 100644 --- a/src/core/scripting/packer-driver/index.ts +++ b/src/core/scripting/packer-driver/index.ts @@ -29,7 +29,6 @@ import { compressUuid } from '../../builder/worker/builder/utils'; import { TypeScriptConfigBuilder } from '../intelligence'; import { eventEmitter } from '../event-emitter'; import { DBInfo } from '../@types/config-export'; -import path from 'path'; const VERSION = '20'; @@ -294,6 +293,9 @@ export class PackerDriver { const projectPath = tsBuilder.getProjectPath(); const compilerOptions = await tsBuilder.getCompilerOptions(); const internalDbURLInfos = await tsBuilder.getInternalDbURLInfos(); + if (self.languageService) { + self.languageService.dispose(); + } self.languageService = new LanguageServiceAdapter(realTsConfigPath, projectPath, self.beforeEditorBuildDelegate, compilerOptions, internalDbURLInfos); for (const target of Object.values(this._targets)) { target.updateDbInfos(this._dbInfos); @@ -302,7 +304,7 @@ export class PackerDriver { }; if (this.busy()) { this._beforeBuildTasks.push(() => { - update(); + return update(); }); } else { await update(); @@ -393,7 +395,7 @@ export class PackerDriver { } public queryScriptDeps(queryPath: string): string[] { - const scriptPath: string = path.normalize(queryPath).replace(/\\/g, '/'); + const scriptPath: string = ps.normalize(queryPath).replace(/\\/g, '/'); this._transformDepsGraph(); if (this._depsGraphCache[scriptPath]) { return Array.from(this._depsGraphCache[scriptPath]); @@ -401,7 +403,7 @@ export class PackerDriver { return []; } public queryScriptUsers(queryPath: string): string[] { - const scriptPath: string = path.normalize(queryPath).replace(/\\/g, '/'); + const scriptPath: string = ps.normalize(queryPath).replace(/\\/g, '/'); this._transformDepsGraph(); if (this._usedGraphCache[scriptPath]) { return Array.from(this._usedGraphCache[scriptPath]); @@ -423,7 +425,7 @@ export class PackerDriver { private _assetChangeQueue: AssetChange[] = []; private _building = false; private _featureChanged = false; - private _beforeBuildTasks: (() => void)[] = []; + private _beforeBuildTasks: (() => Promise | void)[] = []; private _depsGraph: Record = {}; private _needUpdateDepsCache = false; private _usedGraphCache: Record> = {}; @@ -505,7 +507,7 @@ export class PackerDriver { const beforeTasks = this._beforeBuildTasks.slice(); this._beforeBuildTasks.length = 0; for (const beforeTask of beforeTasks) { - beforeTask(); + await beforeTask(); } await this.beforeEditorBuildDelegate.dispatch(assetChanges.filter(item => item.type === AssetActionEnum.change) as ModifiedAssetChange[]); const nonDTSChanges = assetChanges.filter(item => !item.filePath.endsWith('.d.ts')); @@ -1069,6 +1071,7 @@ function matchObject(lhs: unknown, rhs: unknown) { } else if (typeof lhs === 'object' && lhs !== null) { return typeof rhs === 'object' && rhs !== null + && Object.keys(lhs).length === Object.keys(rhs as object).length && Object.keys(lhs).every((key) => matchLhs((lhs as any)[key], (rhs as any)[key])); } else if (lhs === null) { return rhs === null; diff --git a/src/core/scripting/shared/query-shared-settings.ts b/src/core/scripting/shared/query-shared-settings.ts index 8ed13e87b..095369832 100644 --- a/src/core/scripting/shared/query-shared-settings.ts +++ b/src/core/scripting/shared/query-shared-settings.ts @@ -140,7 +140,11 @@ function verifyImportMapJson(input: unknown): input is ImportMap { } } if ('scopes' in input) { - for (const value of Object.values(input)) { + const scopes = (input as { scopes: unknown }).scopes; + if (typeof scopes !== 'object' || !scopes) { + return false; + } + for (const value of Object.values(scopes)) { if (!verifySpecifierMap(value)) { return false; } diff --git a/src/core/scripting/utils/awaiter.ts b/src/core/scripting/utils/awaiter.ts deleted file mode 100644 index 2e76f291b..000000000 --- a/src/core/scripting/utils/awaiter.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { asserts } from './asserts'; - -export class Awaiter { - public resolve(value: TValue) { - asserts(this._state === State.PENDING); - this._result = value; - this._state = State.RESOLVED; - for (const { resolve } of this._queue) { - resolve(value); - } - this._queue.length = 0; - } - - public reject(err?: TError) { - asserts(this._state === State.PENDING); - this._result = err; - this._state = State.RESOLVED; - for (const { reject } of this._queue) { - reject(err); - } - this._queue.length = 0; - } - - public async wait() { - switch (this._state) { - case State.RESOLVED: - return (this._result as TValue); - case State.REJECTED: - throw (this._result as TError | undefined); - } - return await new Promise((resolve, reject) => { - this._queue.push({ - resolve, - reject, - }); - }); - } - - private _state = State.PENDING; - - private _result: TValue | TError | undefined | null = null; - - private _queue: Array<{ - resolve: (value: TValue) => void; - reject: (err?: any) => void; - }> = []; -} - -enum State { - PENDING, - RESOLVED, - REJECTED, -} diff --git a/src/core/scripting/utils/delegate.ts b/src/core/scripting/utils/delegate.ts index 625db4205..e7870d60b 100644 --- a/src/core/scripting/utils/delegate.ts +++ b/src/core/scripting/utils/delegate.ts @@ -1,4 +1,3 @@ -/* eslint-disable prefer-rest-params */ /** * @zh * 移除首个指定的数组元素。判定元素相等时相当于于使用了 `Array.prototype.indexOf`。 @@ -97,6 +96,6 @@ export class AsyncDelegate (Promise | void) = * @returns @en The promise awaiting all async callback resolved. @zh 等待所有异步回调结束的 Promise 对象。 */ public dispatch(...args: Parameters) { - return Promise.all(this._delegates.map((func) => func(...arguments)).filter(Boolean)); + return Promise.all(this._delegates.map((func) => func(...args)).filter(Boolean)); } }