removeDependency - greydragon888/real-router GitHub Wiki

getDependenciesApi().remove

1. Overview

  • What it does: Removes a dependency by name. When attempting to remove a non-existent dependency, logs a warning but does not throw (idempotent behavior).
  • When to use:
    • For removing unnecessary dependencies
    • During resource cleanup
    • For safe removal without checking existence first

2. Signature

import { createRouter } from "@real-router/core";
import { getDependenciesApi } from "@real-router/core/api";

const router = createRouter<Dependencies>(routes, options, dependencies);
const depsApi = getDependenciesApi(router);

depsApi.remove(name: keyof Dependencies): void

Usage Examples

import { createRouter } from "@real-router/core";
import { getDependenciesApi } from "@real-router/core/api";

interface AppDependencies {
  apiClient: ApiClient;
  tempService: TempService;
  cache: CacheService;
}

const router = createRouter<AppDependencies>(
  routes,
  {},
  {
    apiClient: new ApiClient(),
    tempService: new TempService(),
    cache: new CacheService(),
  },
);

const depsApi = getDependenciesApi(router);

// Remove dependency
depsApi.remove("tempService");

// Safe cleanup (without existence check)
const cleanupDeps = ["cache", "tempService"] as const;
cleanupDeps.forEach((dep) => depsApi.remove(dep));

// After removal
depsApi.has("tempService"); // false
depsApi.get("tempService"); // ReferenceError

3. Parameters

name (required)

  • Type: keyof Dependencies
  • Purpose: Name of dependency to remove
  • Constraint: Must be a string -- TypeError if not

Behavior for Different Values

Value Behavior
Existing key Removed via delete
Non-existing key console.warn, no error
Non-string TypeError thrown

4. Return Value

  • Type: void

5. Side Effects

  • Dependency removal: Dependency is removed from the router's dependency store
  • Warning: console.warn when attempting to remove non-existent dependency
    • Format: [router.removeDependency] Attempted to remove non-existent dependency: "{type}"

6. Possible Errors

Condition Error Type Message
Router disposed RouterError DISPOSED
name not string TypeError [router.removeDependency]: dependency name must be a string, got {type}

Non-existent dependency does not throw -- it logs a warning instead. This is intentional for idempotent cleanup.

Error Examples

const depsApi = getDependenciesApi(router);

// TypeError: not a string
// @ts-expect-error
depsApi.remove(123);
// TypeError: [router.removeDependency]: dependency name must be a string, got number

// RouterError: disposed router
router.dispose();
const depsAfterDispose = getDependenciesApi(router);
depsAfterDispose.remove("foo");
// RouterError: DISPOSED

7. Related Methods

Method When to use
getDependenciesApi().has Check before removal (to avoid warning)
getDependenciesApi().set Set a dependency
getDependenciesApi().get Get a dependency
getDependenciesApi().getAll Get all dependencies (shallow copy)
getDependenciesApi().reset Remove all dependencies

Comparison with reset

const depsApi = getDependenciesApi(router);

// remove -- removes one dependency at a time
depsApi.remove("service1");
depsApi.remove("service2");

// reset -- removes all at once
depsApi.reset();

Idempotency vs has

const depsApi = getDependenciesApi(router);

// Option 1: Safe removal (idempotent)
// Warning if doesn't exist, but no error
depsApi.remove("maybeNotExist");

// Option 2: Check before removal (no warning)
if (depsApi.has("maybeNotExist")) {
  depsApi.remove("maybeNotExist");
}

8. Behavior

Main Scenarios

  • Removes existing dependency: has() returns false after removal
  • Idempotent: Safe to call multiple times; subsequent calls log a warning
  • Disposed router: Throws RouterError(DISPOSED) on any mutating call

From Tests

import { createRouter } from "@real-router/core";
import { getDependenciesApi } from "@real-router/core/api";

interface Deps {
  foo?: number;
}

const router = createRouter<Deps>([], {}, { foo: 1 });
const depsApi = getDependenciesApi(router);

// Removes existing dependency
depsApi.remove("foo");
expect(depsApi.has("foo")).toBe(false);

Warning for Non-Existent

const depsApi = getDependenciesApi(router);
const warnSpy = vi.spyOn(console, "warn");

depsApi.remove("nonexistent");
expect(warnSpy).toHaveBeenCalledWith(
  expect.stringContaining("[router.removeDependency]"),
  expect.stringContaining(
    'Attempted to remove non-existent dependency: "string"',
  ),
);

Idempotent Removal

const depsApi = getDependenciesApi(router);
const warnSpy = vi.spyOn(console, "warn");

// First removal -- success
depsApi.remove("foo");
expect(depsApi.has("foo")).toBe(false);

// Second removal -- warning, but no error
depsApi.remove("foo");
expect(warnSpy).toHaveBeenCalled();

Disposed Router

const depsApi = getDependenciesApi(router);

router.dispose();

expect(() => {
  depsApi.remove("foo");
}).toThrowError("DISPOSED");

Safe Cleanup Pattern

const depsApi = getDependenciesApi(router);
const warnSpy = vi.spyOn(console, "warn");

// Cleanup without existence check
const cleanupDeps = ["dep1", "dep2", "dep3"];

depsApi.set("dep1", 1);
// dep2 and dep3 don't exist

// Doesn't throw error
expect(() => {
  cleanupDeps.forEach((dep) => depsApi.remove(dep));
}).not.toThrowError();

// Warnings for non-existent (dep2 and dep3)
expect(warnSpy).toHaveBeenCalledTimes(2);

Edge Cases

  • Repeated removal: Idempotent (warning, not error)
  • Integration with getAll: Removed keys do not appear in result
  • After removal: get() throws ReferenceError for the removed key

Guarantees

  • Returns void (no chaining)
  • Throws RouterError(DISPOSED) if router is disposed
  • Throws TypeError if name is not a string
  • Idempotent -- safe to call multiple times
  • Warning for non-existent (informative without breaking)
  • Uses Object.hasOwn() for prototype pollution protection

8. Migration from router5

Before (router5) After (real-router)
router.removeDependency(name) getDependenciesApi(router).remove(name)
// Before (router5)
router.removeDependency("tempService");

// After (real-router)
import { getDependenciesApi } from "@real-router/core/api";

const depsApi = getDependenciesApi(router);
depsApi.remove("tempService");

Key Differences

Aspect router5 (router.removeDependency) real-router (depsApi.remove)
API style Facade method on router Standalone tree-shakeable function
Return type Router (fluent chaining) void
Disposed check None RouterError(DISPOSED)
Name validation Type coercion (non-strings coerced) TypeError for non-strings
Non-existent key Warning (same) Warning (same)
⚠️ **GitHub.com Fallback** ⚠️