Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 0 additions & 24 deletions .eslintrc.js

This file was deleted.

6 changes: 3 additions & 3 deletions bin/compile-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// Compiles a configuration to a module (single file) that exports the instantiated instance,
// where all dependencies are injected.

import * as Path from 'path';
import * as Path from 'node:path';
import type { ParsedArgs } from 'minimist';
import minimist = require('minimist');
import minimist from 'minimist';
import { compileConfig } from '..';

const args: ParsedArgs = minimist(process.argv.slice(2));
Expand Down Expand Up @@ -41,7 +41,7 @@ compileConfig(
asFunction,
)
.then((output: string) => process.stdout.write(`${output}\n`))
.catch(error => {
.catch((error) => {
process.stderr.write(`${error.stack}\n`);
process.exit(1);
});
56 changes: 56 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const config = require('@rubensworks/eslint-config');

module.exports = config([
{
ignores: [
'node_modules',
'coverage',
'**/*.js',
'**/*.d.ts',
'**/*.js.map',
'**/*.md',
'**/*.yml',
'**/*.yaml',
'**/*.json',
],
},
{
files: [ '**/*.ts' ],
languageOptions: {
parserOptions: {
tsconfigRootDir: __dirname,
project: [ './tsconfig.eslint.json' ],
},
},
},
{
rules: {
'no-implicit-coercion': 'off',
'no-sync': 'off',
// This is a Node.js library, it must import Node.js builtins
'import/no-nodejs-modules': 'off',
// The DI framework necessarily works with unknown types at runtime
'ts/no-unsafe-assignment': 'off',
'ts/no-unsafe-argument': 'off',
'ts/no-unsafe-return': 'off',
// Don't flag unused function parameters (common in interface implementations)
'unused-imports/no-unused-vars': [ 'error', { args: 'none' }],
},
},
{
// PrefetchedDocumentLoader imports JSON files which need import/extensions disabled;
// the disable comments between imports break the newline-after-import chain detection
files: [ 'lib/rdf/PrefetchedDocumentLoader.ts' ],
rules: {
'import/extensions': 'off',
'import/newline-after-import': 'off',
},
},
{
// Specific rules for test files
files: [ '**/test/**/*.ts' ],
rules: {
'ts/require-array-sort-compare': 'off',
},
},
]);
16 changes: 8 additions & 8 deletions lib/ComponentsManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as fs from 'fs';
import * as fs from 'node:fs';
import type { Resource, RdfObjectLoader } from 'rdf-object';
import { stringToTerm } from 'rdf-string';
import type { Logger } from 'winston';
Expand All @@ -14,16 +14,16 @@ import { ErrorResourcesContext } from './util/ErrorResourcesContext';
* A components manager can instantiate components.
* This manager should be created using {@link ComponentsManager.build}.
*/
export class ComponentsManager<Instance> {
export class ComponentsManager<TInstance> {
public readonly moduleState: IModuleState;
public readonly objectLoader: RdfObjectLoader;
public readonly componentResources: Record<string, Resource>;
public readonly configRegistry: ConfigRegistry;
public readonly dumpErrorState: boolean;
public readonly configConstructorPool: IConfigConstructorPool<Instance>;
public readonly configConstructorPool: IConfigConstructorPool<TInstance>;
public readonly logger: Logger;

public constructor(options: IComponentsManagerOptions<Instance>) {
public constructor(options: IComponentsManagerOptions<TInstance>) {
this.moduleState = options.moduleState;
this.objectLoader = options.objectLoader;
this.componentResources = options.componentResources;
Expand All @@ -38,7 +38,7 @@ export class ComponentsManager<Instance> {
* @see IComponentsManagerBuilderOptions
* @param options Settings of the new manager.
*/
public static build<I>(options: IComponentsManagerBuilderOptions<I>): Promise<ComponentsManager<I>> {
public static build<TI>(options: IComponentsManagerBuilderOptions<TI>): Promise<ComponentsManager<TI>> {
return new ComponentsManagerBuilder(options).build();
}

Expand All @@ -50,7 +50,7 @@ export class ComponentsManager<Instance> {
* @param instanceIri The IRI of an instance inside a config.
* @param settings Optional settings that may influence instantiation.
*/
public async instantiate<T = Instance>(instanceIri: string, settings: IConstructionSettings = {}): Promise<T> {
public async instantiate<T = TInstance>(instanceIri: string, settings: IConstructionSettings = {}): Promise<T> {
try {
const instanceResource: Resource = this.objectLoader.resources[instanceIri];
if (!instanceResource) {
Expand Down Expand Up @@ -97,12 +97,12 @@ export class ComponentsManager<Instance> {
}
}

export interface IComponentsManagerOptions<Instance> {
export interface IComponentsManagerOptions<TInstance> {
moduleState: IModuleState;
objectLoader: RdfObjectLoader;
componentResources: Record<string, Resource>;
configRegistry: ConfigRegistry;
dumpErrorState: boolean;
configConstructorPool: IConfigConstructorPool<Instance>;
configConstructorPool: IConfigConstructorPool<TInstance>;
logger: Logger;
}
28 changes: 14 additions & 14 deletions lib/construction/ConfigConstructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ import type { IConstructionStrategy } from './strategy/IConstructionStrategy';
* If you want to make sure that instances are reused,
* be sure to call {@link ConfigConstructorPool} instead.
*/
export class ConfigConstructor<Instance> implements IArgumentsConstructor<Instance> {
private static readonly ARGS_HANDLERS: IArgumentConstructorHandler[] = [
export class ConfigConstructor<TInstance> implements IArgumentsConstructor<TInstance> {
private static readonly argsHandlers: IArgumentConstructorHandler[] = [
new ArgumentConstructorHandlerUndefined(),
new ArgumentConstructorHandlerHash(),
new ArgumentConstructorHandlerArray(),
Expand All @@ -40,11 +40,11 @@ export class ConfigConstructor<Instance> implements IArgumentsConstructor<Instan
];

public readonly objectLoader: RdfObjectLoader;
public readonly configConstructorPool: IConfigConstructorPool<Instance>;
public readonly constructionStrategy: IConstructionStrategy<Instance>;
public readonly configConstructorPool: IConfigConstructorPool<TInstance>;
public readonly constructionStrategy: IConstructionStrategy<TInstance>;
private readonly moduleState: IModuleState;

public constructor(options: IConfigConstructorOptions<Instance>) {
public constructor(options: IConfigConstructorOptions<TInstance>) {
this.objectLoader = options.objectLoader;
this.configConstructorPool = options.configConstructorPool;
this.constructionStrategy = options.constructionStrategy;
Expand All @@ -54,7 +54,7 @@ export class ConfigConstructor<Instance> implements IArgumentsConstructor<Instan
public async getArgumentValues(
values: Resource[],
settings: IConstructionSettings,
): Promise<Instance> {
): Promise<TInstance> {
if (values.length === 0) {
return this.constructionStrategy.createUndefined();
}
Expand All @@ -69,9 +69,9 @@ export class ConfigConstructor<Instance> implements IArgumentsConstructor<Instan
public async getArgumentValue(
value: Resource,
settings: IConstructionSettings,
): Promise<Instance> {
): Promise<TInstance> {
// Check if this args resource can be handled by one of the built-in handlers.
for (const handler of ConfigConstructor.ARGS_HANDLERS) {
for (const handler of ConfigConstructor.argsHandlers) {
if (handler.canHandle(value, settings, this)) {
return handler.handle(value, settings, this);
}
Expand All @@ -90,7 +90,7 @@ export class ConfigConstructor<Instance> implements IArgumentsConstructor<Instan
public async createArguments(
config: Resource,
settings: IConstructionSettings,
): Promise<Instance[]> {
): Promise<TInstance[]> {
if (config.property.arguments) {
if (!config.property.arguments.list) {
throw new ErrorResourcesContext('Detected non-RDF-list as value for config arguments', { config });
Expand All @@ -110,8 +110,8 @@ export class ConfigConstructor<Instance> implements IArgumentsConstructor<Instan
public async createInstance(
config: Resource,
settings: IConstructionSettings,
): Promise<Instance> {
const args: Instance[] = await this.createArguments(config, settings);
): Promise<TInstance> {
const args: TInstance[] = await this.createArguments(config, settings);
return this.constructionStrategy.createInstance({
settings,
moduleState: this.moduleState,
Expand All @@ -128,19 +128,19 @@ export class ConfigConstructor<Instance> implements IArgumentsConstructor<Instan
/**
* Options for a component factory.
*/
export interface IConfigConstructorOptions<Instance> {
export interface IConfigConstructorOptions<TInstance> {
/**
* The RDF object loader.
*/
objectLoader: RdfObjectLoader;
/**
* The instance pool.
*/
configConstructorPool: IConfigConstructorPool<Instance>;
configConstructorPool: IConfigConstructorPool<TInstance>;
/**
* The strategy for construction.
*/
constructionStrategy: IConstructionStrategy<Instance>;
constructionStrategy: IConstructionStrategy<TInstance>;
/**
* The module state.
*/
Expand Down
21 changes: 10 additions & 11 deletions lib/construction/ConfigConstructorPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ import type { IConstructionStrategy } from './strategy/IConstructionStrategy';
* This will make sure that configs with the same id will only be instantiated once,
* and multiple references to configs will always reuse the same instance.
*/
export class ConfigConstructorPool<Instance> implements IConfigConstructorPool<Instance> {
export class ConfigConstructorPool<TInstance> implements IConfigConstructorPool<TInstance> {
private readonly configPreprocessors: IConfigPreprocessor<any>[];
private readonly configConstructor: ConfigConstructor<Instance>;
private readonly constructionStrategy: IConstructionStrategy<Instance>;
private readonly configConstructor: ConfigConstructor<TInstance>;
private readonly constructionStrategy: IConstructionStrategy<TInstance>;

private instances: Record<string, Promise<any>> = {};

public constructor(options: IInstancePoolOptions<Instance>) {
public constructor(options: IInstancePoolOptions<TInstance>) {
this.configPreprocessors = options.configPreprocessors;
this.configConstructor = new ConfigConstructor({
objectLoader: options.objectLoader,
Expand All @@ -37,10 +37,10 @@ export class ConfigConstructorPool<Instance> implements IConfigConstructorPool<I
public instantiate(
configResource: Resource,
settings: IConstructionSettings,
): Promise<Instance> {
): Promise<TInstance> {
// Check if this resource is required as argument in its own chain,
// if so, return a dummy value, to avoid infinite recursion.
const resourceBlacklist = settings.resourceBlacklist || {};
const resourceBlacklist = settings.resourceBlacklist ?? {};
const configResourceId = termToString(configResource.term);
if (resourceBlacklist[configResourceId]) {
return Promise.reject(new ErrorResourcesContext(`Circular dependency was detected on ${configResource.value}`, { config: configResource }));
Expand Down Expand Up @@ -123,11 +123,10 @@ export class ConfigConstructorPool<Instance> implements IConfigConstructorPool<I
*/
public validateParam(config: Resource, field: string, type: string, optional?: boolean): void {
if (!config.property[field]) {
if (!optional) {
throw new ErrorResourcesContext(`Invalid config: Missing ${field}`, { config });
} else {
if (optional) {
return;
}
throw new ErrorResourcesContext(`Invalid config: Missing ${field}`, { config });
}
if (config.property[field].type !== type) {
throw new ErrorResourcesContext(`Invalid config: ${field} "${config.property[field].value}" must be a ${type}, but got ${config.property[field].type}`, { config });
Expand All @@ -152,7 +151,7 @@ export class ConfigConstructorPool<Instance> implements IConfigConstructorPool<I
}
}

export interface IInstancePoolOptions<Instance> {
export interface IInstancePoolOptions<TInstance> {
/**
* The RDF object loader.
*/
Expand All @@ -164,7 +163,7 @@ export interface IInstancePoolOptions<Instance> {
/**
* The strategy for construction.
*/
constructionStrategy: IConstructionStrategy<Instance>;
constructionStrategy: IConstructionStrategy<TInstance>;
/**
* The module state.
*/
Expand Down
6 changes: 3 additions & 3 deletions lib/construction/IConfigConstructorPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { IConstructionSettings } from './IConstructionSettings';
/**
* Manages and creates instances of components.
*/
export interface IConfigConstructorPool<Instance> {
export interface IConfigConstructorPool<TInstance> {
/**
* Instantiate a component based on a Resource.
* @param configResource A config resource.
Expand All @@ -14,13 +14,13 @@ export interface IConfigConstructorPool<Instance> {
instantiate: (
configResource: Resource,
settings: IConstructionSettings,
) => Promise<Instance>;
) => Promise<TInstance>;

/**
* Return the instance regsitry.
* This is a hash from registered id to a Promise of the Instance.
*/
getInstanceRegistry: () => Record<string, Promise<Instance>>;
getInstanceRegistry: () => Record<string, Promise<TInstance>>;

/**
* Resets any internal state to what it originally was.
Expand Down
10 changes: 5 additions & 5 deletions lib/construction/argument/ArgumentConstructorHandlerArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ import type { IArgumentsConstructor } from './IArgumentsConstructor';
* Handles arguments with elements as array.
*/
export class ArgumentConstructorHandlerArray implements IArgumentConstructorHandler {
public canHandle<Instance>(
public canHandle<TInstance>(
value: Resource,
settings: IConstructionSettings,
argsCreator: IArgumentsConstructor<Instance>,
argsCreator: IArgumentsConstructor<TInstance>,
): boolean {
return Boolean(value.property.elements);
}

public async handle<Instance>(
public async handle<TInstance>(
argument: Resource,
settings: IConstructionSettings,
argsCreator: IArgumentsConstructor<Instance>,
): Promise<Instance> {
argsCreator: IArgumentsConstructor<TInstance>,
): Promise<TInstance> {
// Recursively handle all sub-args in the array
const elements = await Promise.all(argument.properties.elements.map(async(entry: Resource) => {
if (!entry.property.value) {
Expand Down
12 changes: 6 additions & 6 deletions lib/construction/argument/ArgumentConstructorHandlerHash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ import type { IArgumentsConstructor } from './IArgumentsConstructor';
* Handles arguments with fields as hashes.
*/
export class ArgumentConstructorHandlerHash implements IArgumentConstructorHandler {
public canHandle<Instance>(
public canHandle<TInstance>(
value: Resource,
settings: IConstructionSettings,
argsCreator: IArgumentsConstructor<Instance>,
argsCreator: IArgumentsConstructor<TInstance>,
): boolean {
return Boolean(value.property.fields);
}

public async handle<Instance>(
public async handle<TInstance>(
argument: Resource,
settings: IConstructionSettings,
argsCreator: IArgumentsConstructor<Instance>,
): Promise<Instance> {
const fields = argument.property.fields.list || [];
argsCreator: IArgumentsConstructor<TInstance>,
): Promise<TInstance> {
const fields = argument.property.fields.list ?? [];

// Determine all key-value pairs
const entries = await Promise.all(fields.map(async(entry: Resource) => {
Expand Down
Loading