@yuebai008/cli
Version:
Command line interface for rapid qg-minigame development
1 lines • 9.16 kB
JavaScript
import*as Common from"../../core/common/common.js";import*as Host from"../../core/host/host.js";import*as SDK from"../../core/sdk/sdk.js";import*as Bindings from"../bindings/bindings.js";import*as Workspace from"../workspace/workspace.js";import{FileSystemWorkspaceBinding}from"./FileSystemWorkspaceBinding.js";import{PersistenceImpl}from"./PersistenceImpl.js";export class Automapping{workspace;onStatusAdded;onStatusRemoved;statuses;fileSystemUISourceCodes;sweepThrottler;sourceCodeToProcessingPromiseMap;sourceCodeToAutoMappingStatusMap;sourceCodeToMetadataMap;filesIndex;projectFoldersIndex;activeFoldersIndex;interceptors;constructor(e,t,o){this.workspace=e,this.onStatusAdded=t,this.onStatusRemoved=o,this.statuses=new Set,this.fileSystemUISourceCodes=new FileSystemUISourceCodes,this.sweepThrottler=new Common.Throttler.Throttler(100),this.sourceCodeToProcessingPromiseMap=new WeakMap,this.sourceCodeToAutoMappingStatusMap=new WeakMap,this.sourceCodeToMetadataMap=new WeakMap,this.filesIndex=new FilePathIndex,this.projectFoldersIndex=new FolderIndex,this.activeFoldersIndex=new FolderIndex,this.interceptors=[],this.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded,(e=>this.onUISourceCodeAdded(e.data))),this.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRemoved,(e=>this.onUISourceCodeRemoved(e.data))),this.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRenamed,this.onUISourceCodeRenamed,this),this.workspace.addEventListener(Workspace.Workspace.Events.ProjectAdded,(e=>this.onProjectAdded(e.data)),this),this.workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved,(e=>this.onProjectRemoved(e.data)),this);for(const t of e.projects())this.onProjectAdded(t);for(const t of e.uiSourceCodes())this.onUISourceCodeAdded(t)}addNetworkInterceptor(e){this.interceptors.push(e),this.scheduleRemap()}scheduleRemap(){for(const e of this.statuses.values())this.clearNetworkStatus(e.network);this.scheduleSweep()}scheduleSweep(){this.sweepThrottler.schedule(function(){const e=this.workspace.projectsForType(Workspace.Workspace.projectTypes.Network);for(const t of e)for(const e of t.uiSourceCodes())this.computeNetworkStatus(e);return this.onSweepHappenedForTest(),Promise.resolve()}.bind(this))}onSweepHappenedForTest(){}onProjectRemoved(e){for(const t of e.uiSourceCodes())this.onUISourceCodeRemoved(t);if(e.type()!==Workspace.Workspace.projectTypes.FileSystem)return;const t=e;for(const e of t.initialGitFolders())this.projectFoldersIndex.removeFolder(e);this.projectFoldersIndex.removeFolder(t.fileSystemPath()),this.scheduleRemap()}onProjectAdded(e){if(e.type()!==Workspace.Workspace.projectTypes.FileSystem)return;const t=e;for(const e of t.initialGitFolders())this.projectFoldersIndex.addFolder(e);this.projectFoldersIndex.addFolder(t.fileSystemPath());for(const t of e.uiSourceCodes())this.onUISourceCodeAdded(t);this.scheduleRemap()}onUISourceCodeAdded(e){const t=e.project();if(t.type()===Workspace.Workspace.projectTypes.FileSystem){if(!FileSystemWorkspaceBinding.fileSystemSupportsAutomapping(t))return;this.filesIndex.addPath(e.url()),this.fileSystemUISourceCodes.add(e),this.scheduleSweep()}else t.type()===Workspace.Workspace.projectTypes.Network&&this.computeNetworkStatus(e)}onUISourceCodeRemoved(e){if(e.project().type()===Workspace.Workspace.projectTypes.FileSystem){this.filesIndex.removePath(e.url()),this.fileSystemUISourceCodes.delete(e.url());const t=this.sourceCodeToAutoMappingStatusMap.get(e);t&&this.clearNetworkStatus(t.network)}else e.project().type()===Workspace.Workspace.projectTypes.Network&&this.clearNetworkStatus(e)}onUISourceCodeRenamed(e){const{uiSourceCode:t,oldURL:o}=e.data;if(t.project().type()!==Workspace.Workspace.projectTypes.FileSystem)return;this.filesIndex.removePath(o),this.fileSystemUISourceCodes.delete(o);const s=this.sourceCodeToAutoMappingStatusMap.get(t);s&&this.clearNetworkStatus(s.network),this.filesIndex.addPath(t.url()),this.fileSystemUISourceCodes.add(t),this.scheduleSweep()}computeNetworkStatus(e){const t=this.sourceCodeToProcessingPromiseMap.get(e);if(t)return t;if(this.sourceCodeToAutoMappingStatusMap.has(e))return Promise.resolve();if(this.interceptors.some((t=>t(e))))return Promise.resolve();if(e.url().startsWith("wasm://"))return Promise.resolve();const o=this.createBinding(e).then(async function(t){if(!t)return null;if(this.sourceCodeToProcessingPromiseMap.get(e)!==o)return null;if(t.network.contentType().isFromSourceMap()||!t.fileSystem.contentType().isTextType())return t;if(t.fileSystem.isDirty()&&(t.network.isDirty()||t.network.hasCommits()))return null;const[s,r]=await Promise.all([t.fileSystem.requestContent(),t.network.project().requestFileContent(t.network)]);if(null===s.content||null===r)return null;if(this.sourceCodeToProcessingPromiseMap.get(e)!==o)return null;const i=Bindings.NetworkProject.NetworkProject.targetForUISourceCode(t.network);let n=!1;const a=s.content;if(i&&i.type()===SDK.Target.Type.Node){if(r.content){n=a===PersistenceImpl.rewrapNodeJSContent(t.fileSystem,a,r.content)}}else r.content&&(n=a.trimEnd()===r.content.trimEnd());if(!n)return this.prevalidationFailedForTest(t),null;return t}.bind(this)).then(async function(t){if(this.sourceCodeToProcessingPromiseMap.get(e)!==o)return;if(!t)return this.onBindingFailedForTest(),void this.sourceCodeToProcessingPromiseMap.delete(e);if(this.sourceCodeToAutoMappingStatusMap.has(t.network)||this.sourceCodeToAutoMappingStatusMap.has(t.fileSystem))return void this.sourceCodeToProcessingPromiseMap.delete(e);if(this.statuses.add(t),this.sourceCodeToAutoMappingStatusMap.set(t.network,t),this.sourceCodeToAutoMappingStatusMap.set(t.fileSystem,t),t.exactMatch){const e=this.projectFoldersIndex.closestParentFolder(t.fileSystem.url());!!e&&this.activeFoldersIndex.addFolder(e)&&this.scheduleSweep()}await this.onStatusAdded.call(null,t),this.sourceCodeToProcessingPromiseMap.delete(e)}.bind(this));return this.sourceCodeToProcessingPromiseMap.set(e,o),o}prevalidationFailedForTest(e){}onBindingFailedForTest(){}clearNetworkStatus(e){if(this.sourceCodeToProcessingPromiseMap.has(e))return void this.sourceCodeToProcessingPromiseMap.delete(e);const t=this.sourceCodeToAutoMappingStatusMap.get(e);if(t){if(this.statuses.delete(t),this.sourceCodeToAutoMappingStatusMap.delete(t.network),this.sourceCodeToAutoMappingStatusMap.delete(t.fileSystem),t.exactMatch){const e=this.projectFoldersIndex.closestParentFolder(t.fileSystem.url());e&&this.activeFoldersIndex.removeFolder(e)}this.onStatusRemoved.call(null,t)}}async createBinding(e){const t=e.url();if(t.startsWith("file://")||t.startsWith("snippet://")){const o=this.fileSystemUISourceCodes.get(t);return o?new AutomappingStatus(e,o,!1):null}let o=Common.ParsedURL.ParsedURL.extractPath(t);if(null===o)return null;o.endsWith("/")&&(o=Common.ParsedURL.ParsedURL.concatenate(o,"index.html"));const s=this.filesIndex.similarFiles(o).map((e=>this.fileSystemUISourceCodes.get(e)));if(!s.length)return null;await Promise.all(s.concat(e).map((async e=>{this.sourceCodeToMetadataMap.set(e,await e.requestMetadata())})));const r=s.filter((e=>Boolean(this.activeFoldersIndex.closestParentFolder(e.url())))),i=this.sourceCodeToMetadataMap.get(e);if(!i||!i.modificationTime&&"number"!=typeof i.contentSize)return 1!==r.length?null:new AutomappingStatus(e,r[0],!1);let n=this.filterWithMetadata(r,i);return n.length||(n=this.filterWithMetadata(s,i)),1!==n.length?null:new AutomappingStatus(e,n[0],!0)}filterWithMetadata(e,t){return e.filter((e=>{const o=this.sourceCodeToMetadataMap.get(e);if(!o)return!1;const s=!t.modificationTime||!o.modificationTime||Math.abs(t.modificationTime.getTime()-o.modificationTime.getTime())<1e3,r=!t.contentSize||o.contentSize===t.contentSize;return s&&r}))}}class FilePathIndex{#e=Common.Trie.Trie.newArrayTrie();addPath(e){const t=e.split("/").reverse();this.#e.add(t)}removePath(e){const t=e.split("/").reverse();this.#e.remove(t)}similarFiles(e){const t=e.split("/").reverse(),o=this.#e.longestPrefix(t,!1);return 0===o.length?[]:this.#e.words(o).map((e=>e.reverse().join("/")))}}class FolderIndex{#t=Common.Trie.Trie.newArrayTrie();#o=new Map;addFolder(e){const t=this.#s(e).split("/");this.#t.add(t);const o=t.join("/"),s=this.#o.get(o)??0;return this.#o.set(o,s+1),0===s}removeFolder(e){const t=this.#s(e).split("/"),o=t.join("/"),s=this.#o.get(o)??0;return!!s&&(s>1?(this.#o.set(o,s-1),!1):(this.#t.remove(t),this.#o.delete(o),!0))}closestParentFolder(e){const t=e.split("/");return this.#t.longestPrefix(t,!0).join("/")}#s(e){return e.endsWith("/")?Common.ParsedURL.ParsedURL.substring(e,0,e.length-1):e}}class FileSystemUISourceCodes{sourceCodes;constructor(){this.sourceCodes=new Map}getPlatformCanonicalFileUrl(e){return Host.Platform.isWin()?Common.ParsedURL.ParsedURL.toLowerCase(e):e}add(e){const t=this.getPlatformCanonicalFileUrl(e.url());this.sourceCodes.set(t,e)}get(e){return e=this.getPlatformCanonicalFileUrl(e),this.sourceCodes.get(e)}delete(e){e=this.getPlatformCanonicalFileUrl(e),this.sourceCodes.delete(e)}}export class AutomappingStatus{network;fileSystem;exactMatch;constructor(e,t,o){this.network=e,this.fileSystem=t,this.exactMatch=o}}