Skip to content

Commit

Permalink
Turborepka (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
ch1ller0 committed Dec 26, 2022
1 parent 9b1fc61 commit 5f2c076
Show file tree
Hide file tree
Showing 68 changed files with 2,730 additions and 2,204 deletions.
8 changes: 8 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)

We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
11 changes: 11 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [["@fridgefm/inverter", "@fridgefm/inverter-test"]],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@fridgefm/examples"]
}
6 changes: 6 additions & 0 deletions .changeset/tricky-knives-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fridgefm/inverter-test": patch
"@fridgefm/inverter": patch
---

feat(inverter): Move to a monorepo setup
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"parserOptions": {
"project": "./tsconfig.dev.json"
},
"ignorePatterns": ["build/", "coverage/"],
"overrides": [{
"files": ["**/*.spec.ts", "src/examples/**/*"],
"rules": {
Expand Down
59 changes: 28 additions & 31 deletions .github/workflows/push-or-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,56 +6,53 @@ on:
branches:
- main

name: Build, Test and maybe Publish
name: Push or PR

jobs:
test:
name: Test
name: Build, Test and maybe Publish
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v2
- name: Check out code
uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
- name: Cache node_modules
id: cache-modules
uses: actions/cache@v2
id: cache-node-modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ matrix.node-version }}-${{ runner.OS }}-build-${{ hashFiles('yarn.lock') }}
key: ${{ matrix.node-version }}-${{ runner.OS }}-yarn-${{ hashFiles('yarn.lock') }}
- name: Install deps
if: steps.cache-modules.outputs.cache-hit != 'true'
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn install
- name: Unit test
- name: Building packages
run: yarn build
- name: Unit testing
run: yarn test:unit
- name: ESLint
run: yarn test:lint
- name: Types test
run: yarn test:types
publish:
name: Publish
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- name: Cache node_modules
id: cache-modules
uses: actions/cache@v2
- name: Type checking
run: yarn test:typecheck
- name: Create Release Pull Request or Publish to npm
id: changesets
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: changesets/action@v1
with:
path: node_modules
key: 16.x-${{ runner.OS }}-build-${{ hashFiles('yarn.lock') }} # get cache from the latest node version
- name: Install deps
if: steps.cache-modules.outputs.cache-hit != 'true'
run: yarn install
- name: Build package
run: yarn build
- name: Publish
uses: mikeal/merge-release@v4.0.7
version: yarn changeset version
publish: yarn changeset publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Send a notification if a publish happens
if: steps.changesets.outputs.published == 'true'
# You can do something when a publish happens.
run: echo "A new version of ${GITHUB_REPOSITORY} was published!"
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
node_modules
lib
build
.idea/*
.vscode
coverage

*.log
*.tsbuildinfo
.env
.turbo
174 changes: 7 additions & 167 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,176 +1,16 @@
# @fridgefm/inverter
# Fridgefm Inverter

A powerful and tiny IoC library with Typescript-first support. Lets you create _highly scalable modular apps and libraries_.

[![npm package](https://img.shields.io/npm/v/@fridgefm/inverter?style=flat-square)](https://www.npmjs.com/package/@fridgefm/inverter)
[![minzipped size](https://img.shields.io/bundlephobia/minzip/@fridgefm/inverter?style=flat-square)](https://bundlephobia.com/package/@fridgefm/inverter)
[![downloads](https://img.shields.io/npm/dt/@fridgefm/inverter?style=flat-square)](https://www.npmjs.com/package/@fridgefm/inverter)
[![open issues](https://img.shields.io/github/issues-raw/ch1ller0/fridgefm-inverter?style=flat-square)](https://github.com/ch1ller0/fridgefm-inverter/issues)

## Key features
- Powerful TS support
- NestJS-similar API
- Zero-dependency (even `reflect-metadata`)
- Async auto-resolve providers
- [Hierarchical containerss](#container-hierarchy) and [Injection scopes](#injection-scopes)
- [Multi-token support](#token-modifications)
- Static provider declaration and no inconsistency
- Works in Node and browser

## Installation
```
npm install @fridgefm/inverter --save
```
or
```
yarn add @fridgefm/inverter
```
## Examples
Example usage is shown [here in examples](./examples/). You can start the example app
```
# cli calculator app
yarn example ./examples/calc-app/
```
```
# basic chat app
yarn example ./examples/chat-app/
```

## Basic features
### Injection types
There are several methods to provide a value for injectables.
First of all, create a token you want to provide
```typescript
const myToken = createToken<{a: number}>('my:token')
```
1. `useValue`\
The most basic method of providing a value is `useValue`. Simply provide a value, which is assignable to `myTokens`'s interface.
```typescript
const myProvider = injectable({
provide: myToken,
useValue: { a: 1 }
})
```
1. `useFactory`\
Sometimes you want to generate a value and save something in the closure.
```typescript
const myProvider = injectable({
provide: myToken,
useFactory: () => {
// do something here if you want... It will run while constructing the provider
const a = Date.now()
return { a }
}
})
```
or with depdendencies
```typescript
const myProvider = injectable({
provide: myToken,
// type is automatically inferred from all the tokens your provider depends on
useFactory: (num, anotherNum) => num + anotherNum + 10,
inject: [numberToken, anotherNumberToken] as const
})
```
> `useFactory` also has a `scope` field, which you can configure. Refer to [Injection scopes](#injection-scopes) for more info
### Token modifications
Set of modificators that allow you to modify your tokens.
```typescript
import { modifyToken, createToken } from '@fridgefm/inverter'

// value will be overwritten with the last registered provider, which provides this token
const baseToken = createToken<number>('num')
// value will be injected as an array and each new provider adds to the array
const multiToken = modifyToken.multi(baseToken)
// if token is not provided, it will return default value
const defaultToken = modifyToken.defaultValue(baseToken, 101)
```
1. `defaultValue`
```typescript
const myProvider = injectable({
provide: baseToken,
useFactory: (num) => num * 10, // if the token is not registered in the container, you still get the default value for `num`
inject: [defaultToken] as const
})
const finalValue = await container.get(baseToken) // 1010 (a result of 101*10)
```
1. `multi`
```typescript
const myProvider = injectable({
provide: baseToken,
useFactory: (multiNums) => multiNums.reduce((acc, cur) => acc + cur, 0), // here `multiNums` is a an array of numbers
inject: [multiToken] as const
})
const num1Provider = injectable({ provide: multiToken, useValue: 15 })
const num2Provider = injectable({ provide: multiToken, useValue: 25 })
const num3Provider = injectable({ provide: multiToken, useValue: 35 })
const finalValue = await container.get(baseToken) // 75 (it is a sum of all the multiNums)
```
This is a monorepo that consists of several packages:

### Container hierarchy
It is possible to create child containers and develop hierarchies. Child containers are useful when you have some private information (for example for each request) and _no other containers (including the parent one) have access to it_.
- [@fridgefm/inverter](./packages/inverter/README.md) - The core part of the inverter.
- [@fridgefm/inverter-test](./packages/inverter-test/README.md) - Testing utilities for inverter.

```typescript
const globalContainer = createContainer({ providers: [] })
const childContainer = createChildContainer(rootContainer, {
providers: [injectable({ provide: PRIVATE_TOKEN, useValue: token })],
}).get(VALUE_THAT_DEPENDS_ON_PRIVATE_TOKEN);
```
In a real world such an invocation is sometimes not possible, because it might lead to a cyclic dependencies between modules/files. It happens because we might want to import an inverter module inside the container, and the module needs to reference the container to create a child one. In this case it might lead to errors, where imported modules might end up being undefined.
That is why there is a different method to create child container:
```typescript
import { internalTokens } from './'
injectable({
provide: SERVER_INIT,
inject: [internalTokens.SELF_CONTAINER] as const,
useFactory: (baseContainer) => {
const server = new Server();
server.on('connection', (clientInfo) => {
// it creates a child container, which can shadow some of the deps
return createChildContainer(baseContainer, {
providers: [injectable({ provide: CLIENT_INFO, useValue: clientInfo })],
}).get(SESSION_ROOT);
});
}
})
```
> Warning: this is an experimental feature and the API might change in the future

### Injection scopes
Imagine we have created a bunch of conatiners and
```typescript
const container = createContainer({ providers });
const childContainers = [createChildContainer(container), createChildContainer(container)]
const allContainers = [container, ...childContainers]
const provider = injectable({ provide: RANDOM, useFactory: () => randomString(), scope: SCOPE })
const resolveAll = () => Promise.all(allContainers.map((cont) => Promise.all([cont.get(RANDOM), cont.get(RANDOM)])));
```
Now the result of calling `resolveAll` depends on the `SCOPE` variable. There are different variants:

1. `singleton`\
Caches the value globally for the parent and all the chilren
```typescript
const provider = injectable({ provide: RANDOM, useFactory: () => randomString(), scope: 'singleton' })
await resolveAll()
// parent child-1 child-2
// [5e546e, 5e546e] [5e546e, 5e546e] [5e546e, 5e546e]
```
1. `scoped`\
This is the default\
Caches the result per-container
```typescript
const provider = injectable({ provide: RANDOM, useFactory: () => randomString(), scope: 'scoped' })
await resolveAll()
// parent child-1 child-2
// [b539d7, b539d7] [02176f, 02176f] [99a3e0, 99a3e0]
```
1. `transient`\
Does not cache at all
```typescript
const provider = injectable({ provide: RANDOM, useFactory: () => randomString(), scope: 'transient' })
await resolveAll()
// parent child-1 child-2
// [e63742, 59defd] [0abb46, 5cd9a1] [690125, 227e8c]
```
## Examples
You can find the examples [here](./apps/examples/)
18 changes: 18 additions & 0 deletions apps/examples/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@fridgefm/examples",
"version": "0.0.0-stub",
"description": "Examples",
"private": true,
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only src/chat-app",
"test:typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"@fridgefm/inverter": "0.2.3",
"@fridgefm/inverter-test": "0.2.3",
"pino": "^8.7.0",
"pino-pretty": "^9.1.1",
"ts-node-dev": "^2.0.0",
"ws": "^8.11.0"
}
}
Loading

0 comments on commit 5f2c076

Please sign in to comment.