0.8.0 • Published 3 years ago

@technote-space/velona v0.8.0

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

Table of Contents

Installation

  • Using npm:

    $ npm install velona
  • Using Yarn:

    $ yarn add velona

Usage

index.ts

import { depend } from 'velona'

const add = (a: number, b: number) => a + b

export const basicFn = depend(
  { add },
  ({ add }, a: number, b: number, c: number) => add(a, b) * c
)

sample.ts

import { basicFn } from './'

console.log(basicFn(2, 3, 4)) // 20

index.spec.ts

import { basicFn } from './'

const injectedFn = basicFn.inject({ add: (a, b) => a * b })

expect(injectedFn(2, 3, 4)).toBe(2 * 3 * 4) // pass
expect(basicFn(2, 3, 4)).toBe((2 + 3) * 4) // pass

DI to browser API callback

handler.ts

import { depend } from 'velona'

export const handler = depend(
  { print: (text: string) => alert(text) },
  ({ print }, e: Pick<MouseEvent, 'type' | 'x' | 'y'>) => print(`type: ${e.type}, x: ${e.x}, y: ${e.y}`)
)

index.ts

import { handler } from './handler'

document.body.addEventListener('click', handler, false)
document.body.click() // alert('type: click, x: 0, y: 0')

index.spec.ts

import { handler } from './handler'

const event = { type: 'click', x: 1, y: 2 }

expect(() => handler(event)).toThrow() // ReferenceError: alert is not defined (on Node.js)

const injectedHandler = handler.inject({ print: text => text })

expect(injectedHandler(event)).toBe(
  `type: ${event.type}, x: ${event.x}, y: ${event.y}`
) // pass

Comparison with no DI

add.ts

export const add = (a: number, b: number) => a + b

noDI.ts

import { add } from './add'

export const noDIFn = (a: number, b: number, c: number) => add(a, b) * c

index.ts

import { depend } from 'velona'
import { add } from './add'

export const basicFn = depend(
  { add },
  ({ add }, a: number, b: number, c: number) => add(a, b) * c
)

sample.ts

import { basicFn } from './'
import { noDIFn } from './noDI'

console.log(basicFn(2, 3, 4)) // 20
console.log(noDIFn(2, 3, 4)) // 20

index.spec.ts

import { basicFn } from './'
import { noDIFn } from './noDI'

const injectedFn = basicFn.inject({ add: (a, b) => a * b })

expect(injectedFn(2, 3, 4)).toBe(2 * 3 * 4) // pass
expect(basicFn(2, 3, 4)).toBe((2 + 3) * 4) // pass
expect(noDIFn(2, 3, 4)).toBe((2 + 3) * 4) // pass

Usage with fs

index.ts

import fs from 'fs'
import { depend } from 'velona'

type FS = {
  readFile(path: string, option: 'utf8'): Promise<string>
  writeFile(path: string, text: string, option: 'utf8'): Promise<void>
}

export const basicFn = depend(
  fs.promises as FS, // downcast for injection
  async (dependencies, path: string, text: string) => {
    await dependencies.writeFile(path, text, 'utf8')
    return dependencies.readFile(path, 'utf8')
  }
)

sample.ts

import { basicFn } from './'

const text = await basicFn('sample.txt', 'Hello world!') // create sample.txt
console.log(text) // 'Hello world!'

index.spec.ts

import { basicFn } from './'

const data: Record<string, string> = {}
const injectedFn = basicFn.inject({
  readFile: path => Promise.resolve(data[path]),
  writeFile: (path, text) => {
    data[path] = text
    return Promise.resolve()
  }
})

const text = 'Hello world!'
await expect(injectedFn('test.txt', text)).resolves.toBe(text)

Usage with prisma

tasks.ts

import { depend } from 'velona'
import { PrismaClient } from '@prisma/client'

type Task = {
  id: number
  label: string
  done: boolean
}

const prisma = new PrismaClient()

export const getTasks = depend(
  { prisma: prisma as { task: { findMany(): Promise<Task[]> } } }, // inject prisma
  ({ prisma }) => prisma.task.findMany() // prisma is injected object
)

tasks.spec.ts

import { getTasks } from '$/service/tasks'

const injectedGetTasks = getTasks.inject({
  prisma: {
    task: {
      findMany: () =>
        Promise.resolve([
          { id: 0, label: 'task1', done: false },
          { id: 1, label: 'task2', done: false },
          { id: 2, label: 'task3', done: true },
          { id: 3, label: 'task4', done: true },
          { id: 4, label: 'task5', done: false }
        ])
    }
  }
})

await expect(injectedGetTasks()).resolves.toHaveLength(5)

Integration test

add.ts

export const add = (a: number, b: number) => a + b

grandchild.ts

import { depend } from 'velona'
import { add } from './add'

export const grandchild = depend(
  { add },
  ({ add }, a: number, b: number) => add(a, b)
)

child.ts

import { depend } from 'velona'
import { grandchild } from './grandchild'

export const child = depend(
  { grandchild },
  ({ grandchild }, a: number, b: number, c: number) => grandchild(a, b) * c
)

parentFn.ts

import { depend } from 'velona'
import { child } from './child'

export const parentFn = depend(
  { child, print: (data: number) => alert(data) },
  ({ child, print }, a: number, b: number, c: number) => print(child(a, b, c))
)

index.ts

import { parentFn } from './parentFn'

parentFn(2, 3, 4) // alert(20)

parentFn.spec.ts

import { parentFn } from './parentFn'

const injectedFn = parentFn.inject((parentDeps) => ({
  child: parentDeps.child.inject((childDeps) => ({
    grandchild: clildDeps.grandchild.inject({
      add: (a, b) => a * b
    }))
  })),
  print: data => data
})

expect(injectedFn(2, 3, 4)).toBe(2 * 3 * 4) // pass

License

Velona is licensed under a MIT License.