npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vite-plugin-di-auto-register

v0.1.1

Published

Auto-register injection-js dependencies for Vite-based DDD projects. Scans @Injectable() classes and generates a register file at dev/build time.

Readme

vite-plugin-di-auto-register

A Vite plugin that auto-registers injection-js dependencies for DDD-style Vue / Vite projects. Scans @Injectable() classes at dev/build time and generates a strongly-typed register.ts.

npm License: MIT

中文用户:本 README 以英文为主,关键章节附中文注释。


Why

In a typical DDD frontend project using injection-js, every new @Injectable() class needs to be registered manually in a central provider list. That list grows fast and is easy to forget.

This plugin:

  • 🔍 Scans configured directories for @Injectable() classes
  • 🔗 Binds classes to InjectionTokens declared in tokens.ts (or anywhere) when they implements an interface or use @Injectable({ as })
  • 📝 Generates an app/di/register.ts with all providers wired up
  • Reacts to file add / change / unlink in dev mode (debounced)
  • 🛡 Skips writing when content unchanged (no HMR loops)
  • 🧪 CLI included for one-shot regeneration in CI / pre-commit

Install

npm i -D vite-plugin-di-auto-register
# peer
npm i injection-js reflect-metadata

Quick start

1. Configure Vite

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteDiPlugin } from 'vite-plugin-di-auto-register'

export default defineConfig({
  plugins: [viteDiPlugin(), vue()],
})

2. Project conventions

Put three files under src/app/di/ (locations are configurable, see Options):

// src/app/di/scan.ts — directories to scan, like Spring's @ComponentScan
export const scanDirs = ['features', 'core', 'app']
// src/app/di/tokens.ts — InjectionToken definitions
import { InjectionToken } from 'injection-js'
import type { AuthRepository } from '../../features/auth/domain/AuthRepository'

export const AUTH_REPOSITORY = new InjectionToken<AuthRepository>('AuthRepository')
// src/app/di/index.ts — your DI public surface
import { type Injector, type Provider } from 'injection-js'
export * from 'injection-js'

export let di: Injector
export function setInjector(i: Injector): void { di = i }

// Hand-written providers (useValue / useFactory / useExisting)
export const customProviders: Provider[] = []

3. Mark services with @Injectable()

// features/auth/infrastructure/AuthRepositoryImpl.ts
import { Injectable } from 'injection-js'
import type { AuthRepository } from '../domain/AuthRepository'

@Injectable()
export class AuthRepositoryImpl implements AuthRepository {
  async login() { return 'token' }
}
// features/auth/application/LoginUseCase.ts
import { Inject, Injectable } from 'injection-js'
import { AUTH_REPOSITORY } from '../../../app/di/tokens'

@Injectable()
export class LoginUseCase {
  constructor(@Inject(AUTH_REPOSITORY) private repo: AuthRepository) {}
}

4. Bootstrap

// src/main.ts
import 'reflect-metadata'
import { createApp } from 'vue'
import { registerDependencies } from './app/di/register'
import App from './App.vue'

registerDependencies()
createApp(App).mount('#app')

register.ts is fully auto-generated — never edit it by hand.

How matching works

When a class is decorated with @Injectable(), the plugin registers it according to:

| Source | Bound as | |---|---| | @Injectable() class X implements Foo and a new InjectionToken<Foo>(...) exists | { provide: TOKEN, useClass: X } | | @Injectable({ as: AbstractCls }) class X extends AbstractCls | { provide: AbstractCls, useClass: X } | | Otherwise | X (class itself as token, no @Inject needed for consumers) |

Tokens are picked up from:

  • The configured tokensFile (default app/di/tokens.ts)
  • Any *.ts file under the scanned directories that declares export const X = new InjectionToken<Y>('...')

CLI

A di-auto-register binary is exposed for one-shot regeneration (CI, pre-commit hooks, scripts):

npx di-auto-register --src=src

Flags:

| Flag | Default | Description | |---|---|---| | --src <dir> | 'src' | Source root, relative to cwd | | --register-file <path> | 'app/di/register.ts' | Generated file (relative to --src) | | --tokens-file <path> | 'app/di/tokens.ts' | Token definitions (relative to --src) | | --scan-config <path> | 'app/di/scan.ts' | Scan config (relative to --src) | | --silent | false | Suppress warnings | | -h, --help | | Show help |

Add it to your scripts:

{
  "scripts": {
    "generate:di": "di-auto-register"
  }
}

Options

viteDiPlugin({
  srcDir: 'src',                     // 默认 'src', relative to vite root
  registerFile: 'app/di/register.ts',
  tokensFile: 'app/di/tokens.ts',
  scanConfigFile: 'app/di/scan.ts',
  silent: false,
  debounceMs: 50,                    // dev-mode watcher debounce window
})

| Option | Type | Default | Notes | |---|---|---|---| | srcDir | string | 'src' | Relative to Vite root | | registerFile | string | 'app/di/register.ts' | Relative to srcDir | | tokensFile | string | 'app/di/tokens.ts' | Relative to srcDir | | scanConfigFile | string | 'app/di/scan.ts' | Relative to srcDir | | silent | boolean | false | Mute plugin & generator console output | | debounceMs | number | 50 | Watcher debounce in dev |

Programmatic API

import { generateDi, resolvePaths, DEFAULT_PATHS } from 'vite-plugin-di-auto-register'

const result = generateDi({ srcDir: '/abs/path/src' })
console.log(result.tokenBindings, result.standaloneClasses, result.warnings)

Exported types: ViteDiPluginOptions, GenerateDiOptions, GenerateDiResult, DiPaths, TokenBinding, StandaloneClass, ResolvedPaths.

How HMR is handled

  • The plugin writes register.ts only when content actually differs → no churn when nothing changed.
  • Changes to register.ts itself are ignored by the watcher → no infinite loop.
  • Rapid file events are debounced (debounceMs).

tsconfig

Make sure your app tsconfig.json has decorator metadata enabled:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

And import 'reflect-metadata' before anything else in main.ts.

FAQ

Q: SSR support? The plugin only does file generation; the generated register.ts is plain TS that any bundler / runtime can consume.

Q: Multiple Vite roots / monorepo? Use one viteDiPlugin() per Vite config. Each instance has its own srcDir.

Q: It generated a binding I didn't expect. Check result.warnings (or run npx di-auto-register) — unresolvable @Injectable({ as }) imports are logged with the offending file path.

Q: Can I commit the generated register.ts? Yes, recommended — it's deterministic and gives a clean diff when DI graph changes.

License

MIT