@wessberg/di-compiler

A Custom Transformer for Typescript that enables compile-time Dependency Injection

Usage no npm install needed!

<script type="module">
  import wessbergDiCompiler from 'https://cdn.skypack.dev/@wessberg/di-compiler';
</script>

README

Logo

A Custom Transformer for Typescript that enables compile-time Dependency Injection

Downloads per month NPM version Dependencies Contributors License: MIT Support on Patreon

Description

This is a CustomTransformer for Typescript that enables you to use the DI library.

This has been implemented as a TypeScript Custom Transformer in order to be so low-level that it can be used as the underlying implementation in anything you want, whether it be directly with Typescript's Compiler APIs, Webpack loaders, Rollup plugins, or something else.

Features

  • Really lightweight
  • Really fast
  • Low-level implementation that can be used as the foundation for other tools such as Loaders, Plugins, and others.
  • It doesn't ask you to reflect metadata or to annotate your classes with decorators. "It just works".

Backers

Bubbles Christopher Blanchard Ideal Postcodes Xerox Trent Raymond
Bubbles
Twitter: @usebubbles
Christopher Blanchard Ideal Postcodes Xerox Trent Raymond

Patreon

Patrons on Patreon

Table of Contents

Install

npm

$ npm install @wessberg/di-compiler

Yarn

$ yarn add @wessberg/di-compiler

pnpm

$ pnpm add @wessberg/di-compiler

Peer Dependencies

@wessberg/di-compiler depends on typescript, so you need to manually install this as well.

Usage

Since this is a Custom Transformer, it can be used practically anywhere you use TypeScript.

The most obvious place would be to use it directly with Typescript's compiler APIs:

Usage with TypeScript's Compiler APIs

There's several ways to do this, but here's a simple example:

import {createProgram, getDefaultCompilerOptions, createCompilerHost} from "typescript";
import {di} from "@wessberg/di-compiler";

const compilerOptions = getDefaultCompilerOptions();
const compilerHost = createCompilerHost(compilerOptions);

// Create a Typescript program
const program = createProgram(
  ["my-file-1.ts", "my-file-2.ts"],
  compilerOptions,
  compilerHost
);

// Transform the SourceFiles within the program, and pass them through the 'di' transformer
program.emit(undefined, undefined, undefined, undefined, di({program}));

Usage with ts-node

One of the simplest ways to use DI-compiler is with ts-node:

node -r @wessberg/di-compiler/register

You can also do it programmatically. Here's an example using CommonJS:

const {di} = require("@wessberg/rollup-plugin-ts");

require("ts-node").register({
  transformers: program => di({program})
});
import {createProgram, getDefaultCompilerOptions, createCompilerHost} from "typescript";
import {di} from "@wessberg/di-compiler";

const compilerOptions = getDefaultCompilerOptions();
const compilerHost = createCompilerHost(compilerOptions);

// Create a Typescript program
const program = createProgram(
  ["my-file-1.ts", "my-file-2.ts"],
  compilerOptions,
  compilerHost
);

// Transform the SourceFiles within the program, and pass them through the 'di' transformer
program.emit(undefined, undefined, undefined, undefined, di({program}));

Usage with Rollup

There are two popular TypeScript plugins for Rollup that support Custom Transformers:

Usage with rollup-plugin-ts

import ts from "@wessberg/rollup-plugin-ts";
import {di} from "@wessberg/di-compiler";

export default {
  input: "...",
  output: [
    /* ... */
  ],
  plugins: [
    ts({
      transformers: [di]
    })
  ]
};

Usage with rollup-plugin-typescript2

import ts from "rollup-plugin-typescript2";
import {di} from "@wessberg/di-compiler";

export default {
  input: "...",
  output: [
    /* ... */
  ],
  plugins: [
    ts({
      transformers: [service => di({program: service.getProgram()})]
    })
  ]
};

Usage with Webpack

There are two popular TypeScript loaders for Webpack that support Custom Transformers:

Usage with awesome-typescript-loader

import {di} from "@wessberg/di-compiler";
const config = {
  // ...
  module: {
    rules: [
      {
        // Match .mjs, .js, .jsx, and .tsx files
        test: /(\.mjs)|(\.[jt]sx?)$/,
        loader: "awesome-typescript-loader",
        options: {
          // ...
          getCustomTransformers: program => di({program})
        }
      }
    ]
  }
  // ...
};

Usage with ts-loader

import {di} from "@wessberg/di";
const config = {
  // ...
  module: {
    rules: [
      {
        // Match .mjs, .js, .jsx, and .tsx files
        test: /(\.mjs)|(\.[jt]sx?)$/,
        loader: "ts-loader",
        options: {
          // ...
          getCustomTransformers: program => di({program})
        }
      }
    ]
  }
  // ...
};

Usage with ava

You can also use DI-compiler with the ava test runner with the require property in the ava configuration:

{
  // Other options...
  extensions: ["ts"],
  require: ["@wessberg/di-compiler/register"]
}

Options

You can provide options to the di Custom Transformer to configure its behavior:

Option Description
program A full TypeScript program (required).
typescript (optional) If given, the TypeScript version to use internally for all operations.

Contributing

Do you want to contribute? Awesome! Please follow these recommendations.

Maintainers

Frederik Wessberg
Frederik Wessberg
Twitter: @FredWessberg
Github: @wessberg
Lead Developer

FAQ

How does it work, exactly?

First, classes that are discovered as part of your Typescript program/bundle will be parsed for their constructor argument types and positions. Then, instances of the DIContainer will be discovered and their expressions will be upgraded. For example, an expression such as:

import {DIContainer} from "@wessberg/di";
import {MyInterface} from "./my-interface";
import {MyImplementation} from "./my-implementation";

const container = new DIContainer();
container.registerSingleton<MyInterface, MyImplementation>();

Will be compiled into:

// ...
container.registerSingleton(undefined, {
  identifier: `MyInterface`,
  implementation: MyImplementation
});

License

MIT © Frederik Wessberg (@FredWessberg) (Website)