Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Container class #4

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
31 changes: 6 additions & 25 deletions playground/container.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
import { Container } from 'nuxt-di';
import { $MyService } from '~/symbol';
import MyService from '~/services/myService';
import UserService from '~/services/userService';

/**
* Function to register dependencies in the Awilix container.
*
* - This function is called by the module to allow the user to define custom dependencies.
* - Dependencies are registered using the provided `registerDependency` function and resolver aliases.
*
* @param {object} context - The context provided to register dependencies.
* @param {Function} context.registerDependency - Function to register a dependency in the Awilix container.
* @param {Function} context.registerClass - Function to register a class as a dependency.
* @param {object} context.resolvers - Aliases for Awilix resolver types (asClass, asFunction, asValue).
* @returns {void}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default ({ registerDependency, resolvers, registerClass }: any): void => {
const { asClass, asValue, asFunction } = resolvers;
const container = new Container();

registerClass($MyService, MyService);
registerDependency('userService', asClass(UserService).singleton());
registerDependency('config', asValue({ apiUrl: 'https://api.example.com' }));
registerDependency(
'logger',
asFunction(() => ({
log: (message: string) => console.log(`[LOG]: ${message}`),
})).singleton(),
);
};
container.registerClass($MyService, MyService);
container.registerClass('userService', UserService);

export default container;
2 changes: 1 addition & 1 deletion playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
},
"dependencies": {
"nuxt": "^3.14.159",
"nuxt-di": "0.1.4"
"nuxt-di": "file:../src"
}
}
22 changes: 7 additions & 15 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/runtime/composables/useContainer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AwilixContainer } from 'awilix';
import { useNuxtApp } from '#app';
import type {Container} from 'nuxt-di';

/**
* Provides access to the Awilix dependency injection container.
Expand All @@ -8,9 +8,9 @@ import { useNuxtApp } from '#app';
* @throws {Error} If the container is not available in the Nuxt app context.
* @returns {T} The Awilix dependency injection container instance or resolved dependency.
*/
export function useContainer<T = AwilixContainer>(dependencyName?: string): T {
export function useContainer<T = Container>(dependencyName?: string): T {
const nuxtApp = useNuxtApp();
const container = nuxtApp.$container as AwilixContainer;
const container = nuxtApp.$container;

if (!container) {
throw new Error('[NuxtDi] Dependency Injection container is not available.');
Expand Down
85 changes: 21 additions & 64 deletions src/runtime/container.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,30 @@
import { createContainer, type AwilixContainer, asClass, asFunction, asValue, type Resolver } from 'awilix/browser';
import { EventSystem } from './eventSystem';
import { createContainer, type AwilixContainer, asClass, asValue, type Resolver } from 'awilix/browser';
import { EventSystem } from 'nuxt-di';

export const diContainer: AwilixContainer = createContainer();
export class Container {
private readonly fContainer: AwilixContainer;

/**
* Registers a dependency in the Awilix container.
*
* @template T - The type of the registered dependency.
* @param {string} name - The name of the dependency to register.
* @param {Resolver<T>} resolver - The resolver configuration for the dependency.
* @returns {void}
*/
export function registerDependency<T>(
name: string,
resolver: Resolver<T>,
): void {
diContainer.register(name, resolver);
}

/**
* Registers a dependency in the Awilix container as a class.
*
* @param {string} name - The name of the dependency to register.
* @param {any} dependencyClass - The class to register as a dependency.
* @returns {void}
*/
export function registerClass<T>(name: string | symbol, dependencyClass: new (...args: unknown[]) => T): void {
diContainer.register(name, asClass(dependencyClass).singleton());
}
constructor() {
this.fContainer = createContainer();
this.fContainer.register('eventSystem', asValue(new EventSystem()));
}

/**
* Provides convenient aliases for Awilix resolver types.
*/
export const resolvers = {
asClass,
asFunction,
asValue,
};
public registerDependency<T>(name: string | symbol, resolver: Resolver<T>): void {
this.fContainer.register(name, resolver);
}

/**
* Loads dependencies into the Awilix container from a user-defined container file.
*
* @param {string} containerPath - The path to the user-defined container file.
* @returns {Promise<void>} Resolves when the dependencies are successfully loaded.
* @throws {Error} If the container file does not export a default function or fails to load.
*/
export async function loadDependencies(containerPath: string): Promise<void> {
try {
// @TODO: Add containerPath dynamic import
const { default: register } = await import('../../playground/container');

if (typeof register === 'function') {
register({
registerDependency,
registerClass,
resolvers,
});
}
else {
console.warn(`[NuxtDi] The container file at ${containerPath} does not export a default function.`);
}
public register<T>(name: string | symbol, resolver: Resolver<T>): void {
this.fContainer.register(name, resolver);
}
catch (error) {
console.error(`[NuxtDi] Failed to load dependencies from ${containerPath}`, error);


public registerClass<T>(name: string | symbol, dependencyClass: new (...args: unknown[]) => T): void {
this.fContainer.register(name, asClass(dependencyClass).singleton());
}
}

function runContainer(): void {
diContainer.register('eventSystem', asValue(new EventSystem()));
}

runContainer();
public resolve<T>(name: string): T {
return this.fContainer.resolve<T>(name);
}
}
8 changes: 4 additions & 4 deletions src/runtime/eventSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class EventSystem {
* @param eventName - The name of the event.
* @param callback - The callback to invoke when the event is emitted.
*/
on(eventName: string, callback: EventCallback) {
public on(eventName: string, callback: EventCallback) {
if (!this.events.has(eventName)) {
this.events.set(eventName, []);
}
Expand All @@ -24,7 +24,7 @@ export class EventSystem {
* @param eventName - The name of the event.
* @param callback - The callback to remove.
*/
off(eventName: string, callback: EventCallback) {
public off(eventName: string, callback: EventCallback) {
const listeners = this.events.get(eventName);
if (!listeners) return;
this.events.set(
Expand All @@ -38,7 +38,7 @@ export class EventSystem {
* @param eventName - The name of the event.
* @param args - Arguments to pass to the event listeners.
*/
emit(eventName: string, ...args: unknown[]) {
public emit(eventName: string, ...args: unknown[]) {
const listeners = this.events.get(eventName);
if (!listeners) return;
listeners.forEach((listener) => listener(...args));
Expand All @@ -48,7 +48,7 @@ export class EventSystem {
* Clear all listeners for a given event or all events.
* @param eventName - The name of the event to clear. If not provided, clears all events.
*/
clear(eventName?: string) {
public clear(eventName?: string) {
if (eventName) {
this.events.delete(eventName);
} else {
Expand Down
20 changes: 12 additions & 8 deletions src/runtime/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { diContainer, loadDependencies } from './container';
import { defineNuxtPlugin, useRuntimeConfig } from '#app';
import { defineNuxtPlugin } from '#app';

/**
* Nuxt plugin to initialize and provide the Awilix dependency injection container.
Expand All @@ -11,12 +10,17 @@ import { defineNuxtPlugin, useRuntimeConfig } from '#app';
* @returns {void}
*/
export default defineNuxtPlugin(async (nuxtApp) => {
const config = useRuntimeConfig();
const containerPath = config.public.nuxtDi.containerPath;
// const containerPath = nuxtApp.$config.public.nuxtDi.containerPath;

// Load user-defined dependencies from the specified container file
await loadDependencies(containerPath);
const { default: containerModule } = await import('../../playground/container');

// Provide the container globally in the Nuxt app context
nuxtApp.provide('container', diContainer);
// console.log('plugin config', containerPath);

// console.log('plugin container', container);
//
// // Load user-defined dependencies from the specified container file
// // await loadDependencies(containerPath);
//
// // Provide the container globally in the Nuxt app context
nuxtApp.provide('containerTest', containerModule);
});
8 changes: 5 additions & 3 deletions test/container.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { describe, it, expect } from 'vitest';
import { diContainer, registerClass } from 'nuxt-di';
import { Container } from 'nuxt-di';
import UserService from '../playground/services/userService';

describe('container.ts', () => {
const diContainer = new Container();

it('should register and resolve a class dependency', () => {
// Register UserService as a class dependency
registerClass('userService', UserService);
diContainer.registerClass('userService', UserService);

// Resolve the dependency from the container
const userService = diContainer.resolve<UserService>('userService');
Expand All @@ -15,7 +17,7 @@ describe('container.ts', () => {

it('should resolve services and retain singleton instance', () => {
// Register UserService as a singleton class
registerClass('userService', UserService);
diContainer.registerClass('userService', UserService);

// Resolve the service twice from the container
const userService1 = diContainer.resolve<UserService>('userService');
Expand Down
9 changes: 2 additions & 7 deletions test/eventSystem.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { describe, it, expect, vi } from 'vitest';
import { EventSystem } from 'nuxt-di';

describe('EventSystem', () => {
const eventSystem = new EventSystem();

it('should register and emit an event', () => {
const eventSystem = new EventSystem();
const callback = vi.fn();

eventSystem.on('testEvent', callback);
Expand All @@ -14,7 +15,6 @@ describe('EventSystem', () => {
});

it('should handle multiple listeners for the same event', () => {
const eventSystem = new EventSystem();
const callback1 = vi.fn();
const callback2 = vi.fn();

Expand All @@ -29,7 +29,6 @@ describe('EventSystem', () => {
});

it('should remove a listener with off', () => {
const eventSystem = new EventSystem();
const callback = vi.fn();

eventSystem.on('testEvent', callback);
Expand All @@ -40,7 +39,6 @@ describe('EventSystem', () => {
});

it('should clear all listeners for a specific event', () => {
const eventSystem = new EventSystem();
const callback1 = vi.fn();
const callback2 = vi.fn();

Expand All @@ -54,7 +52,6 @@ describe('EventSystem', () => {
});

it('should clear all listeners for all events', () => {
const eventSystem = new EventSystem();
const callback1 = vi.fn();
const callback2 = vi.fn();

Expand All @@ -69,15 +66,13 @@ describe('EventSystem', () => {
});

it('should handle emitting an event with no listeners', () => {
const eventSystem = new EventSystem();

expect(() => {
eventSystem.emit('nonExistentEvent', 'payload');
}).not.toThrow();
});

it('should handle removing a listener that does not exist', () => {
const eventSystem = new EventSystem();
const callback = vi.fn();

expect(() => {
Expand Down
Loading