Formatting
This commit is contained in:
12
.prettierrc
Normal file
12
.prettierrc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"bracketSameLine": true,
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"printWidth": 180,
|
||||||
|
"semi": true,
|
||||||
|
"singleAttributePerLine": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"useTabs": false
|
||||||
|
}
|
||||||
@@ -1,35 +1,35 @@
|
|||||||
import js from "@eslint/js";
|
import js from '@eslint/js';
|
||||||
import globals from "globals";
|
import globals from 'globals';
|
||||||
import tseslint from "typescript-eslint";
|
import tseslint from 'typescript-eslint';
|
||||||
import json from "@eslint/json";
|
import json from '@eslint/json';
|
||||||
import markdown from "@eslint/markdown";
|
import markdown from '@eslint/markdown';
|
||||||
import css from "@eslint/css";
|
import css from '@eslint/css';
|
||||||
import { defineConfig } from "eslint/config";
|
import {defineConfig} from 'eslint/config';
|
||||||
|
|
||||||
export default defineConfig([
|
export default defineConfig([
|
||||||
{
|
{
|
||||||
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
|
||||||
plugins: {js},
|
plugins: {js},
|
||||||
extends: ["js/recommended"],
|
extends: ['js/recommended'],
|
||||||
languageOptions: { globals: globals.node },
|
languageOptions: {globals: globals.node}
|
||||||
},
|
},
|
||||||
tseslint.configs.recommended,
|
tseslint.configs.recommended,
|
||||||
{
|
{
|
||||||
files: ["**/*.json"],
|
files: ['**/*.json'],
|
||||||
plugins: {json},
|
plugins: {json},
|
||||||
language: "json/json",
|
language: 'json/json',
|
||||||
extends: ["json/recommended"],
|
extends: ['json/recommended']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ["**/*.md"],
|
files: ['**/*.md'],
|
||||||
plugins: {markdown},
|
plugins: {markdown},
|
||||||
language: "markdown/commonmark",
|
language: 'markdown/commonmark',
|
||||||
extends: ["markdown/recommended"],
|
extends: ['markdown/recommended']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ["**/*.css"],
|
files: ['**/*.css'],
|
||||||
plugins: {css},
|
plugins: {css},
|
||||||
language: "css/css",
|
language: 'css/css',
|
||||||
extends: ["css/recommended"],
|
extends: ['css/recommended']
|
||||||
},
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type IDependencyProvider from "./IDependencyProvider";
|
import type IDependencyProvider from './IDependencyProvider';
|
||||||
import type IDisposable from "../lang/IDisposable";
|
import type IDisposable from '../lang/IDisposable';
|
||||||
import Disposable from "../lang/Disposable";
|
import Disposable from '../lang/Disposable';
|
||||||
import Type from "./Type";
|
import Type from './Type';
|
||||||
import type IDependencyManager from "./IDependencyManager";
|
import type IDependencyManager from './IDependencyManager';
|
||||||
|
|
||||||
type Constructor<T = unknown> = new (...args: unknown[]) => T;
|
type Constructor<T = unknown> = new (...args: unknown[]) => T;
|
||||||
type ModuleLoader = (modulePath: string) => Promise<Constructor | {default: Constructor}>;
|
type ModuleLoader = (modulePath: string) => Promise<Constructor | {default: Constructor}>;
|
||||||
@@ -16,8 +16,8 @@ export default class DependencyManager implements IDependencyManager {
|
|||||||
private readonly _pending: Type[];
|
private readonly _pending: Type[];
|
||||||
|
|
||||||
constructor(moduleLoader: ModuleLoader) {
|
constructor(moduleLoader: ModuleLoader) {
|
||||||
if (typeof moduleLoader !== "function") {
|
if (typeof moduleLoader !== 'function') {
|
||||||
throw new Error("Module loader must be a function");
|
throw new Error('Module loader must be a function');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._moduleLoader = moduleLoader;
|
this._moduleLoader = moduleLoader;
|
||||||
@@ -30,25 +30,23 @@ export default class DependencyManager implements IDependencyManager {
|
|||||||
|
|
||||||
public defineDependencies(type: Type, dependencies: Type[]): IDisposable {
|
public defineDependencies(type: Type, dependencies: Type[]): IDisposable {
|
||||||
if (!(type instanceof Type)) {
|
if (!(type instanceof Type)) {
|
||||||
throw new Error("Type must be a Type");
|
throw new Error('Type must be a Type');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(dependencies)) {
|
if (!Array.isArray(dependencies)) {
|
||||||
throw new Error("Dependencies must be an array");
|
throw new Error('Dependencies must be an array');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const dependency of dependencies) {
|
for (const dependency of dependencies) {
|
||||||
if (!(dependency instanceof Type)) {
|
if (!(dependency instanceof Type)) {
|
||||||
throw new Error("Dependency must be a Type");
|
throw new Error('Dependency must be a Type');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = type.toURN();
|
const key = type.toURN();
|
||||||
|
|
||||||
if (this._injections.has(key)) {
|
if (this._injections.has(key)) {
|
||||||
throw new Error(
|
throw new Error(`Dependencies already defined for type [${type.toString()}]`);
|
||||||
`Dependencies already defined for type [${type.toString()}]`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._injections.set(key, dependencies);
|
this._injections.set(key, dependencies);
|
||||||
@@ -61,7 +59,7 @@ export default class DependencyManager implements IDependencyManager {
|
|||||||
|
|
||||||
public getDependencies(type: Type): Type[] | undefined {
|
public getDependencies(type: Type): Type[] | undefined {
|
||||||
if (!(type instanceof Type)) {
|
if (!(type instanceof Type)) {
|
||||||
throw new Error("Type must be an instance of Type");
|
throw new Error('Type must be an instance of Type');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._injections.get(type.toURN());
|
return this._injections.get(type.toURN());
|
||||||
@@ -69,7 +67,7 @@ export default class DependencyManager implements IDependencyManager {
|
|||||||
|
|
||||||
public addProvider(provider: IDependencyProvider): IDisposable {
|
public addProvider(provider: IDependencyProvider): IDisposable {
|
||||||
if (!this.isProvider(provider)) {
|
if (!this.isProvider(provider)) {
|
||||||
throw new Error("Provider must implement IDependencyProvider");
|
throw new Error('Provider must implement IDependencyProvider');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._providers.push(provider);
|
this._providers.push(provider);
|
||||||
@@ -83,7 +81,22 @@ export default class DependencyManager implements IDependencyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async resolveProvider(type: Type): Promise<IDependencyProvider> {
|
public async resolveProvider(type: Type): Promise<IDependencyProvider> {
|
||||||
return this.resolve(type);
|
if (!(type instanceof Type)) {
|
||||||
|
throw new Error('Type must be an instance of Type');
|
||||||
|
}
|
||||||
|
|
||||||
|
const candidates = this._providers.filter(provider => provider.canProvide(type));
|
||||||
|
|
||||||
|
if (candidates.length === 0) {
|
||||||
|
throw new Error(`No provider for [${type.toString()}]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.length > 1) {
|
||||||
|
const candidateNames = candidates.map(c => c.toString()).join(', ');
|
||||||
|
throw new Error(`Multiple providers for ${type}: ${candidateNames}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates[0] as IDependencyProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async instantiateType(type: Type): Promise<unknown> {
|
public async instantiateType(type: Type): Promise<unknown> {
|
||||||
@@ -95,10 +108,8 @@ export default class DependencyManager implements IDependencyManager {
|
|||||||
|
|
||||||
// Check for circular dependencies
|
// Check for circular dependencies
|
||||||
if (this._pending.includes(type)) {
|
if (this._pending.includes(type)) {
|
||||||
const pendingChain = this._pending.map((t) => t.toString()).join(' -> ');
|
const pendingChain = this._pending.map(t => t.toString()).join(' -> ');
|
||||||
throw new Error(
|
throw new Error(`Failed to resolve ${type} due to circular dependency: ${pendingChain}`);
|
||||||
`Failed to resolve ${type} due to circular dependency: ${pendingChain}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._pending.push(type);
|
this._pending.push(type);
|
||||||
@@ -109,7 +120,7 @@ export default class DependencyManager implements IDependencyManager {
|
|||||||
|
|
||||||
// Resolve all dependencies
|
// Resolve all dependencies
|
||||||
const resolvedDependencies = await Promise.all(
|
const resolvedDependencies = await Promise.all(
|
||||||
dependencies.map(async (dependency) => {
|
dependencies.map(async dependency => {
|
||||||
const provider = await this.resolveProvider(dependency);
|
const provider = await this.resolveProvider(dependency);
|
||||||
let instance = await provider.provide(dependency);
|
let instance = await provider.provide(dependency);
|
||||||
|
|
||||||
@@ -150,7 +161,7 @@ export default class DependencyManager implements IDependencyManager {
|
|||||||
|
|
||||||
public addEagerType(type: Type): void {
|
public addEagerType(type: Type): void {
|
||||||
if (!(type instanceof Type)) {
|
if (!(type instanceof Type)) {
|
||||||
throw new Error("Type must be an instance of Type");
|
throw new Error('Type must be an instance of Type');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._eagerTypes.push(type);
|
this._eagerTypes.push(type);
|
||||||
@@ -167,35 +178,14 @@ export default class DependencyManager implements IDependencyManager {
|
|||||||
private isProvider(obj: unknown): obj is IDependencyProvider {
|
private isProvider(obj: unknown): obj is IDependencyProvider {
|
||||||
return (
|
return (
|
||||||
obj !== null &&
|
obj !== null &&
|
||||||
typeof obj === "object" &&
|
typeof obj === 'object' &&
|
||||||
"canProvide" in obj &&
|
'canProvide' in obj &&
|
||||||
"provide" in obj &&
|
'provide' in obj &&
|
||||||
typeof (obj as IDependencyProvider).canProvide === "function" &&
|
typeof (obj as IDependencyProvider).canProvide === 'function' &&
|
||||||
typeof (obj as IDependencyProvider).provide === "function"
|
typeof (obj as IDependencyProvider).provide === 'function'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolve(type: Type): IDependencyProvider {
|
|
||||||
if (!(type instanceof Type)) {
|
|
||||||
throw new Error("Type must be an instance of Type");
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidates = this._providers.filter((provider) =>
|
|
||||||
provider.canProvide(type),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (candidates.length === 0) {
|
|
||||||
throw new Error(`No provider for [${type.toString()}]`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (candidates.length > 1) {
|
|
||||||
const candidateNames = candidates.map((c) => c.toString()).join(", ");
|
|
||||||
throw new Error(`Multiple providers for ${type}: ${candidateNames}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return candidates[0] as IDependencyProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
private injectDependencies(Constructor: Constructor, dependencies: unknown[]): unknown {
|
private injectDependencies(Constructor: Constructor, dependencies: unknown[]): unknown {
|
||||||
if (dependencies.length === 0) {
|
if (dependencies.length === 0) {
|
||||||
return new Constructor();
|
return new Constructor();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type Type from "./Type";
|
import type Type from './Type';
|
||||||
import type IDisposable from "../lang/IDisposable";
|
import type IDisposable from '../lang/IDisposable';
|
||||||
import type IDependencyProvider from "./IDependencyProvider";
|
import type IDependencyProvider from './IDependencyProvider';
|
||||||
|
|
||||||
export default interface IDependencyManager {
|
export default interface IDependencyManager {
|
||||||
defineDependencies(type: Type, dependencies: Type[]): IDisposable;
|
defineDependencies(type: Type, dependencies: Type[]): IDisposable;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import Type from "./Type";
|
import Type from './Type';
|
||||||
|
|
||||||
export default interface IDependencyProvider {
|
export default interface IDependencyProvider {
|
||||||
canProvide(type: Type): boolean;
|
canProvide(type: Type): boolean;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import Type from "./Type";
|
import Type from './Type';
|
||||||
|
|
||||||
export default class NamedType extends Type {
|
export default class NamedType extends Type {
|
||||||
private readonly _name: string;
|
private readonly _name: string;
|
||||||
@@ -13,11 +13,7 @@ export default class NamedType extends Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override equals(other: Type) {
|
public override equals(other: Type) {
|
||||||
return (
|
return super.equals(other) && other instanceof Type && this.getName() === (other as NamedType).getName();
|
||||||
super.equals(other) &&
|
|
||||||
other instanceof Type &&
|
|
||||||
this.getName() === (other as NamedType).getName()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public overridetoURN(): string {
|
public overridetoURN(): string {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import IDisposable from "./IDisposable";
|
import IDisposable from './IDisposable';
|
||||||
|
|
||||||
export default class Disposable implements IDisposable {
|
export default class Disposable implements IDisposable {
|
||||||
private readonly _cleanup: () => void;
|
private readonly _cleanup: () => void;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import IDisposable from "./IDisposable";
|
import IDisposable from './IDisposable';
|
||||||
|
|
||||||
export default class DisposableList implements IDisposable {
|
export default class DisposableList implements IDisposable {
|
||||||
private readonly _list: IDisposable[];
|
private readonly _list: IDisposable[];
|
||||||
|
|||||||
Reference in New Issue
Block a user