Enable oxlint's import/no-cycle rule (import plugin) so circular module dependencies are caught in CI, similar to eslint-plugin-import/no-cycle and biome no-import-cycles discussed in the issue. Relocate shared types and dependencies to remove cycles under src/common: - Types.ts: host IInsertEvent, IDeleteEvent, IParams, UnderlineStyle, IBuffer, and IBufferSet; drop imports from CircularList, buffer/Types, parser/Types, and Services - CoreTerminal.ts: define and export ICoreTerminal - ServiceRegistry.ts: define IServiceIdentifier (break Services registry loop) - UnicodeService: stop constructing UnicodeV6; CoreTerminal registers the default v6 provider after service creation - buffer/Types.ts and parser/Types.ts: re-export moved types for callers Fixes https://github.com/xtermjs/xterm.js/issues/5897 Co-authored-by: Cursor <cursoragent@cursor.com>
86 lines
2.8 KiB
TypeScript
86 lines
2.8 KiB
TypeScript
/**
|
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
|
* @license MIT
|
|
*
|
|
* This was heavily inspired from microsoft/vscode's dependency injection system (MIT).
|
|
*/
|
|
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { IInstantiationService } from './Services';
|
|
import { IServiceIdentifier, getServiceDependencies } from './ServiceRegistry';
|
|
|
|
export class ServiceCollection {
|
|
|
|
private _entries = new Map<IServiceIdentifier<any>, any>();
|
|
|
|
constructor(...entries: [IServiceIdentifier<any>, any][]) {
|
|
for (const [id, service] of entries) {
|
|
this.set(id, service);
|
|
}
|
|
}
|
|
|
|
public set<T>(id: IServiceIdentifier<T>, instance: T): T {
|
|
const result = this._entries.get(id);
|
|
this._entries.set(id, instance);
|
|
return result;
|
|
}
|
|
|
|
public forEach(callback: (id: IServiceIdentifier<any>, instance: any) => any): void {
|
|
for (const [key, value] of this._entries.entries()) {
|
|
callback(key, value);
|
|
}
|
|
}
|
|
|
|
public has(id: IServiceIdentifier<any>): boolean {
|
|
return this._entries.has(id);
|
|
}
|
|
|
|
public get<T>(id: IServiceIdentifier<T>): T | undefined {
|
|
return this._entries.get(id);
|
|
}
|
|
}
|
|
|
|
export class InstantiationService implements IInstantiationService {
|
|
public serviceBrand: undefined;
|
|
|
|
private readonly _services: ServiceCollection = new ServiceCollection();
|
|
|
|
constructor() {
|
|
this._services.set(IInstantiationService, this);
|
|
}
|
|
|
|
public setService<T>(id: IServiceIdentifier<T>, instance: T): void {
|
|
this._services.set(id, instance);
|
|
}
|
|
|
|
public getService<T>(id: IServiceIdentifier<T>): T | undefined {
|
|
return this._services.get(id);
|
|
}
|
|
|
|
public createInstance<T>(ctor: any, ...args: any[]): T {
|
|
const serviceDependencies = getServiceDependencies(ctor).sort((a, b) => a.index - b.index);
|
|
|
|
const serviceArgs: any[] = [];
|
|
for (const dependency of serviceDependencies) {
|
|
const service = this._services.get(dependency.id);
|
|
if (!service) {
|
|
throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id._id}.`);
|
|
}
|
|
serviceArgs.push(service);
|
|
}
|
|
|
|
const firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length;
|
|
|
|
// check for argument mismatches, adjust static args if needed
|
|
if (args.length !== firstServiceArgPos) {
|
|
throw new Error(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`);
|
|
}
|
|
|
|
// now create the instance
|
|
return new ctor(...[...args, ...serviceArgs]);
|
|
}
|
|
}
|