Skip to content
This repository was archived by the owner on Mar 25, 2021. It is now read-only.
Merged
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
3 changes: 2 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ jobs:
- run: |
if git log -1 --pretty=%B | grep "^v\?[0-9]\+\.[0-9]\+\.[0-9]\+$";
then
lerna publish from-package -y
yarn global add lerna
lerna publish from-git --no-verify-access
else
echo "Not a release, skipping publish"
fi
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"packages/*"
],
"scripts": {
"build": "shx rm -rf ./packages/{http,types,utils}/**/{esm,lib} && tsc -b tsconfig.project.json && rollup --config rollup.config.js",
"build": "shx rm -rf ./packages/{di,http,types,utils}/**/{esm,lib} && shx rm -rf ./packages/**/tsconfig.tsbuildinfo && tsc -b tsconfig.project.json && rollup --config rollup.config.js",
"circular": "madge --circular ./packages/{http,utils,types}/src/index.ts",
"cover": "shx rm -rf .nyc_output && shx rm -rf coverage && nyc --reporter=html --reporter=lcov ava",
"dev": "node tools/cli/webpack-cli.js",
Expand All @@ -37,6 +37,7 @@
"prettier": "^1.16.4",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"reflect-metadata": "^0.1.13",
"rollup": "^1.7.4",
"shx": "^0.3.2",
"source-map-loader": "^0.2.4",
Expand Down
146 changes: 146 additions & 0 deletions packages/di/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# `@asuka/di`

> Dependencies injection library

## Usage

### Basic

```ts
import { Injectable, InjectableFactory } from '@asuka/di'

@Injectable()
class Engine { }

@Injectable()
class Car {
constructor(public engine: Engine) { }
}

const car = InjectableFactory.getInstance(Car)
expect(car).to.be.instanceof(Car)
expect(car.engine).to.be.instanceof(Engine)
```

### Value Provider

```ts
import { Inject, InjectionToken, Injectable, InjectableFactory, ValueProvider } from '@asuka/di'
import Axios from 'axios'

const token = new InjectionToken<Axios>('Axios client')

const provider: ValueProvider = {
provide: token,
useValue: Axios,
}

@Injectable({
providers: [provider],
})
class HttpClient {
constructor(@Inject(token) public axios: Axios) { }
}

const client = InjectableFactory.getInstance(HttpClient)
expect(client).to.be.instanceof(HttpClient)
expect(client.axios).to.equal(Axios)
```

### Factory Provider

```ts
import { Inject, InjectionToken, Injectable, InjectableFactory, FactoryProvider } from '@asuka/di'
import Axios from 'axios'

const token = new InjectionToken<Axios>('Axios client')
const baseURL = 'https://leetcode-cn.com/api'
const provider: FactoryProvider = {
provide: token,
useFactory: () => {
return Axios.create({
baseURL,
})
},
}

@Injectable({
providers: [provider],
})
class HttpClient {
constructor(@Inject(token) public axios: Axios) { }
}

const client = InjectableFactory.getInstance(HttpClient)
expect(client).to.be.instanceof(HttpClient)
expect(client.axios).to.equal(Axios) // baseURL of client.axios is 'https://leetcode-cn.com/api'
```

## Testing

### Override Provider by configureModule

```ts
function whatever() {
return true
}

function replacement() {
return false
}

const token = new InjectionToken<typeof whatever>('replacable')

const provider: ValueProvider = {
useValue: replacement,
provide: token,
}

@Injectable({
providers: [provider],
})
class Service {
constructor(@Inject(token) public dep: typeof whatever) {}
}

const testModule = Test.createTestingModule({ providers: [{ provide: token, useValue: replacement }] }).compile()
const service = testModule.getInstance(Service)
t.true(service instanceof Service)
t.is(service.dep, replacement)
t.false(service.dep())
```

### Override Provider by overrideProvider method

```ts
function whatever() {
return true
}

function replacement() {
return false
}

const token = new InjectionToken<typeof whatever>('replacabel')

const provider: ValueProvider = {
useValue: replacement,
provide: token,
}

@Injectable({
providers: [provider],
})
class Service {
constructor(@Inject(token) public dep: typeof whatever) {}
}

const testModule = Test.createTestingModule()
.overrideProvider(token)
.useValue(replacement)
.compile()
const service = testModule.getInstance(Service)
t.true(service instanceof Service)
t.is(service.dep, replacement)
t.false(service.dep())
```
30 changes: 30 additions & 0 deletions packages/di/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@asuka/di",
"version": "0.0.5",
"description": "Dependencies inject library",
"keywords": [
"DI",
"Dependencies inject",
"Injectable"
],
"author": "LongYinan <lynweklm@gmail.com>",
"homepage": "https://github.com/LeetCode-OpenSource/asuka",
"license": "MIT",
"main": "lib/index.js",
"module": "esm/bundle.esm.js",
"typings": "esm/index.d.ts",
"sideEffects": false,
"repository": {
"type": "git",
"url": "git+https://github.com/LeetCode-OpenSource/asuka.git"
},
"bugs": {
"url": "https://github.com/LeetCode-OpenSource/asuka/issues"
},
"dependencies": {
"injection-js": "^2.2.1"
},
"optionalDependencies": {
"reflect-metadata": "^0.1.13"
}
}
178 changes: 178 additions & 0 deletions packages/di/src/__tests__/injectable.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import 'reflect-metadata'
import test from 'ava'

import {
Inject,
InjectionToken,
InjectableFactory,
Injectable,
ValueProvider,
FactoryProvider,
ClassProvider,
} from '../index'

test.afterEach(() => {
InjectableFactory.reset()
})

test('should get single instance', (t) => {
@Injectable()
class Single {}

const instance = InjectableFactory.getInstance(Single)

t.true(instance instanceof Single)
})

test('should get dependencies', (t) => {
@Injectable()
class Dep {}

@Injectable()
class DepTwo {
constructor(public dep: Dep) {}
}

@Injectable()
class Service {
constructor(public dep: Dep, public depTwo: DepTwo) {}
}

const service = InjectableFactory.getInstance(Service)

t.true(InjectableFactory.getInstance(Dep) instanceof Dep)
t.true(InjectableFactory.getInstance(DepTwo) instanceof DepTwo)
t.true(service instanceof Service)
})

test('should singleton by default', (t) => {
@Injectable()
class Dep {}

@Injectable()
class DepTwo {
constructor(public dep: Dep) {}
}

@Injectable()
class Service {
constructor(public dep: Dep, public depTwo: DepTwo) {}
}

const service = InjectableFactory.getInstance(Service)
const dep = InjectableFactory.getInstance(Dep)
const depTwo = InjectableFactory.getInstance(DepTwo)

t.is(service.dep, dep)
t.is(service.depTwo, depTwo)
})

test('should be able to inject by useValue', (t) => {
function whatever() {}
const token = new InjectionToken<typeof whatever>('whatever')

const provider: ValueProvider = {
provide: token,
useValue: whatever,
}

@Injectable({
providers: [provider],
})
class Service {
constructor(@Inject(token) public dep: typeof whatever) {}
}
const service = InjectableFactory.getInstance(Service)
t.true(service instanceof Service)
t.is(service.dep, whatever)
})

test('should be able to inject by useFactory', (t) => {
class Dep {
constructor(public cacheSize: number) {}
}

const cacheSize = 5

const token = new InjectionToken<Dep>('whatever')

const provider: FactoryProvider = {
provide: token,
useFactory() {
return new Dep(cacheSize)
},
}

@Injectable({
providers: [provider],
})
class Service {
constructor(@Inject(token) public dep: Dep) {}
}

const service = InjectableFactory.getInstance(Service)

t.true(service.dep instanceof Dep)
t.is(service.dep.cacheSize, cacheSize)
})

test('should be able to resolve deps from useFactory', (t) => {
class Dep {
constructor(public cacheSize: number, public depTwo: DepTwo) {}
}

@Injectable()
class DepTwo {}

const cacheSize = 5

const token = new InjectionToken<Dep>('whatever')

const provider: FactoryProvider = {
provide: token,
useFactory(depTwo: DepTwo) {
return new Dep(cacheSize, depTwo)
},
deps: [DepTwo],
}

@Injectable({
providers: [provider],
})
class Service {
constructor(@Inject(token) public dep: Dep) {}
}

const service = InjectableFactory.getInstance(Service)
const depTwo = InjectableFactory.getInstance(DepTwo)

t.true(service.dep instanceof Dep)
t.is(service.dep.cacheSize, cacheSize)
t.true(depTwo instanceof DepTwo)
t.is(service.dep.depTwo, depTwo)
})

test('should be able to inject by useClass', (t) => {
class Dep {
constructor() {}
}

const token = new InjectionToken<Dep>('whatever')

const provider: ClassProvider = {
provide: token,
useClass: Dep,
}

@Injectable({
providers: [provider],
})
class Service {
constructor(@Inject(token) public dep: Dep) {}
}

const service = InjectableFactory.getInstance(Service)

t.true(service instanceof Service)
t.true(service.dep instanceof Dep)
})
Loading