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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@b08/injector-generator

v2.2.1

Published

generator for injector functions

Downloads

47

Readme

@b08/injector-generator, seeded from @b08/generator-seed, library type: generator

generator for injector functions

conceptual thoughts

  1. This is a static dependency injection mechanism. Static means that all resolving logic works in design time. Once it is generated and compiled, all dependencies are locked.
  2. Unresolved dependencies float up. So, if dependency of the service you want to resolve has an unresolved dependency, then your service has an unresolved dependency, thus, in order to call injector function for that service, you will have to provide a parameter of that type.
    When several dependencies require the parameter of the same type, it will be collapsed into one parameter for higher level service.

what is an injector

It is a function, creating an instance of your class, calling corresponding injectors to create dependencies for your class Example:

export class Svc1 {
  constructor(private svc2: Svc2) { }
}

export class Svc2 { }

For these 2 classes following injectors will be generated:

export function svc1(): Svc1 { return new Svc1(svc2()); }
export function svc2(): Svc2 { return new Svc2(); }

This is a simplified example of the code that is actually generated, only to demonstrate what those functions do. Actual code includes a passing "instances" object, so that only one instance would be generated per type. I.e. two different services, depending of third service will receive the same instance of the third service.

emerging parameters

if a dependency is not a class or not in parsed files at all, it acts as an external dependency and emerges on top of the injector chain Example:

export class Svc1 {
  constructor(private svc2: Svc2) { }
}

export class Svc2 {
  constructor(private name: string) { }
 }

For these 2 classes following injectors will be generated:

export function svc1(name: string): Svc1 { return new Svc1(svc2(name)); }
export function svc2(name: string): Svc2 { return new Svc2(name); }

implementation replacements

Classes implementing interfaces will be automatically used in place of those interfaces. Example, source:

export interface IMyService { }

export class MyService implements IMyService { }

export class SecondService {
  constructor(private svc: IMyService) { }
}

injectors to be generated:

export function myService(): MyService {
  return new MyService();
}

export function secondService(): SecondService {
  return new SecondService(myService());
}

No generics supported in all replacements.

mock replacements

Classes extending other classes and having same name with Mock suffix will be used in place of those classes.

// app folder
export class MyClass {}
// test folder
export class MyClassMock extends MyClass {}

Usage case: production build only affects "app" folder, so dependency will be used directly, test build also affects "test" folder, mocks in that folder will replace original classes.

named replacement via comment

// app folder
export class Dal1 { }

// test folder

// replacement for Dal1
export class Dal1Mock { }

Comment above Dal1Mock says that this mock is to replace "Dal1" class in injection chain.

configurable replacements

This config triggers the same replacement

const dalMock =  { folder: "./folder-of-Dal1Mock", file: "file-name", name: "Dal1Mock", isModulesPath: false }
const options = {
  nameReplacements:[{typeNameRegex: /^Dal1$/, replacement: dalMock }]
}

replacements, order of precedence

  1. Type replacements from options
  2. implementations and extensions
  3. Name(regex) replacements from options
  4. Comment replacements First rule to match wins.

context replacement

Second reason for a replacement is using a big context object for your application while services could be depending on fields of that context. Example:

export interface IContext {
  db: IDb
}

export class Svc {
  constructor(private db: IDb) { }  
}

// this will be generated
export function svc(context: IContext) {
 return new Svc(context.db);
}

You will need to include file with context into the sources. If you don't include that file for some reason, you can also specify the replacement in the options.

generating injectors

import { generateInjectors } from "@b08/injector-generator"; import { transformRange, transform } from "@b08/gulp-transform"; import * as changed from "gulp-changed";

const options = { lineFeed: "\n", quotes: """ };

export function injectors(): NodeJS.ReadWriteStream { // this is a gulp task
return gulp.src("./app/**/*.@(service|context).ts") .pipe(transformRange(files => generateInjectors(files, options))) .pipe(changed(dest, { hasChanged: changed.compareContents })) .pipe(logWrittenFilesToConsole) .pipe(gulp.dest("./app")); }

const logWrittenFilesToConsole = transform(file => { console.log(Writing ${file.folder}/${file.name}${file.extension}); return file; });