Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial version #1

Closed
wants to merge 159 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
159 commits
Select commit Hold shift + click to select a range
3355f3c
work in progress
danieldietrich Sep 30, 2022
1e545ec
work in progress
danieldietrich Sep 30, 2022
19f8496
cleanup types
danieldietrich Sep 30, 2022
c375c65
cleanup (wip)
danieldietrich Sep 30, 2022
5567448
cleanup
danieldietrich Sep 30, 2022
9efadc0
compiles & runs, 3 of 183 tests are failing
danieldietrich Sep 30, 2022
972689f
added eslint
danieldietrich Oct 1, 2022
d17a5fc
cleanup
danieldietrich Oct 1, 2022
5757031
cleanup
danieldietrich Oct 1, 2022
ac13249
cleanup
danieldietrich Oct 1, 2022
f5ec92e
cleanup
danieldietrich Oct 1, 2022
a8912a2
cleanup
danieldietrich Oct 1, 2022
668a480
cleanup
danieldietrich Oct 1, 2022
b55c034
cleanup
danieldietrich Oct 1, 2022
e055c0a
cleanup
danieldietrich Oct 1, 2022
1eb2124
cleanup
danieldietrich Oct 2, 2022
a95fef9
eager eval
danieldietrich Oct 3, 2022
10440f6
better error messages
danieldietrich Oct 3, 2022
440873b
Added GitHub actions
danieldietrich Oct 3, 2022
6808c40
cleanup
danieldietrich Oct 3, 2022
ad35781
cleanup
danieldietrich Oct 3, 2022
1bf3a8c
types + readme
danieldietrich Oct 3, 2022
7f695e8
typings
danieldietrich Oct 3, 2022
dc21144
typescript 4.9-beta
danieldietrich Oct 4, 2022
f78431a
readme
danieldietrich Oct 4, 2022
ddf8b8e
re-organized types
danieldietrich Oct 4, 2022
f573ec4
todo
danieldietrich Oct 4, 2022
463818e
typescript 4.8.x
danieldietrich Oct 4, 2022
b9049e1
tests
danieldietrich Oct 4, 2022
f3dc1d2
work in progress / open todos
danieldietrich Oct 4, 2022
f962132
docs
danieldietrich Oct 5, 2022
8478f42
dist -> lib
danieldietrich Oct 5, 2022
7f2c780
docs
danieldietrich Oct 5, 2022
a745085
docs
danieldietrich Oct 5, 2022
eff9ae8
bundling for stats
danieldietrich Oct 5, 2022
306968c
cleanup
danieldietrich Oct 5, 2022
38ba5c9
docs
danieldietrich Oct 5, 2022
b68e13f
fix bundle names
danieldietrich Oct 5, 2022
c44e7db
TODO
danieldietrich Oct 5, 2022
28d5f90
linter
danieldietrich Oct 5, 2022
f848d4c
lint arrays
danieldietrich Oct 5, 2022
8820b41
naming
danieldietrich Oct 5, 2022
32f5352
TODO
danieldietrich Oct 5, 2022
3416b7e
docs
danieldietrich Oct 5, 2022
ebde0b1
docs
danieldietrich Oct 5, 2022
3932285
docs
danieldietrich Oct 5, 2022
e247c96
docs
danieldietrich Oct 5, 2022
6199762
docs
danieldietrich Oct 5, 2022
7ea8cf8
docs
danieldietrich Oct 5, 2022
d136ec2
docs
danieldietrich Oct 5, 2022
b5fe998
badges
danieldietrich Oct 5, 2022
10693e8
ci
danieldietrich Oct 5, 2022
3a16cb5
docs
danieldietrich Oct 5, 2022
730d001
Rename actions.yml to build.yml
danieldietrich Oct 5, 2022
fda449f
docs
danieldietrich Oct 5, 2022
d146d3b
docs
danieldietrich Oct 5, 2022
b8e29fd
docs
danieldietrich Oct 5, 2022
0707e1f
docs
danieldietrich Oct 5, 2022
c2900ef
docs
danieldietrich Oct 5, 2022
267c2e5
docs
danieldietrich Oct 5, 2022
4a3e9c4
docs
danieldietrich Oct 6, 2022
63902a6
cleanup
danieldietrich Oct 6, 2022
b4b142b
refactoring
danieldietrich Oct 6, 2022
42a61ea
types
danieldietrich Oct 6, 2022
d2dfcd8
types
danieldietrich Oct 6, 2022
8991205
cleanup
danieldietrich Oct 6, 2022
2479049
types
danieldietrich Oct 6, 2022
00f54b6
types
danieldietrich Oct 6, 2022
085472b
types
danieldietrich Oct 6, 2022
9149211
types
danieldietrich Oct 6, 2022
ed25dbf
tests
danieldietrich Oct 6, 2022
ce359d9
tests & types
danieldietrich Oct 6, 2022
5eae078
tests
danieldietrich Oct 6, 2022
96078ce
generalized MergeModules to MergeArray
danieldietrich Oct 6, 2022
88f3f47
reflect container
danieldietrich Oct 6, 2022
0a6edff
types
danieldietrich Oct 6, 2022
9e25889
validate container
danieldietrich Oct 6, 2022
84da9ea
inject argument validation
danieldietrich Oct 7, 2022
6a17f78
types & tests
danieldietrich Oct 7, 2022
1874cf9
types
danieldietrich Oct 7, 2022
49cdf7e
types
danieldietrich Oct 7, 2022
3ca3ad2
cleanup
danieldietrich Oct 7, 2022
64ecd09
100% test coverage
danieldietrich Oct 7, 2022
3e5dca2
100% test coverage
danieldietrich Oct 7, 2022
dfe82bb
tests
danieldietrich Oct 7, 2022
66e43ff
gitprompt
danieldietrich Oct 7, 2022
267d45a
gitpod.yml
danieldietrich Oct 7, 2022
5d0e689
gitprompt
danieldietrich Oct 7, 2022
3d4e6fd
split terminal
danieldietrich Oct 7, 2022
8281661
shell split mode
danieldietrich Oct 7, 2022
1d21619
gitpod
danieldietrich Oct 7, 2022
2c037fb
relaxed factory type
danieldietrich Oct 7, 2022
6614765
tests
danieldietrich Oct 7, 2022
9a58fde
missing dependencies
danieldietrich Oct 7, 2022
545a25b
nearly there...
danieldietrich Oct 7, 2022
5ebfefb
docs
danieldietrich Oct 8, 2022
bbcb844
docs
danieldietrich Oct 8, 2022
319aa9e
docs
danieldietrich Oct 8, 2022
9dbcfe0
docs
danieldietrich Oct 8, 2022
9e1324f
docs
danieldietrich Oct 8, 2022
0807fbc
docs
danieldietrich Oct 8, 2022
f5a7774
docs
danieldietrich Oct 8, 2022
fe725da
docs
danieldietrich Oct 8, 2022
2b51c2d
docs
danieldietrich Oct 8, 2022
83d3292
docs
danieldietrich Oct 8, 2022
a2f641a
docs
danieldietrich Oct 8, 2022
96fada4
docs
danieldietrich Oct 8, 2022
6ca5431
docs
danieldietrich Oct 8, 2022
56922ad
inject
danieldietrich Oct 8, 2022
b6516d5
docs
danieldietrich Oct 8, 2022
c23f490
docs
danieldietrich Oct 8, 2022
8b81bec
docs
danieldietrich Oct 8, 2022
c5005bf
docs
danieldietrich Oct 8, 2022
aadda3d
docs
danieldietrich Oct 8, 2022
8a36343
docs
danieldietrich Oct 8, 2022
f718692
docs
danieldietrich Oct 8, 2022
d7a2014
docs
danieldietrich Oct 8, 2022
16227a2
docs
danieldietrich Oct 9, 2022
f9c481b
docs
danieldietrich Oct 9, 2022
aae900c
missing types & error urls
danieldietrich Oct 9, 2022
6940ab9
Ginject type
danieldietrich Oct 9, 2022
7fa8d17
Ginject type
danieldietrich Oct 9, 2022
767acd1
Ginject type
danieldietrich Oct 9, 2022
3d7789b
Ginject type
danieldietrich Oct 9, 2022
cd79202
cleanup
danieldietrich Oct 10, 2022
fc163aa
types
danieldietrich Oct 10, 2022
d2bf3b8
todo
danieldietrich Oct 10, 2022
13ada5d
types
danieldietrich Oct 10, 2022
4a7c558
cleanup
danieldietrich Oct 10, 2022
f761845
types
danieldietrich Oct 10, 2022
d711c0c
cleanup & tests
danieldietrich Oct 10, 2022
dbad093
cleanup
danieldietrich Oct 10, 2022
5a32529
ReflectContainer
danieldietrich Oct 11, 2022
abc1d4d
x
danieldietrich Oct 13, 2022
5f0442b
docs
danieldietrich Oct 13, 2022
d192548
tests
danieldietrich Oct 13, 2022
21bafb5
reworked Merge type
danieldietrich Oct 13, 2022
b22f123
ReflectContainer
danieldietrich Oct 13, 2022
302fd7b
cleanup
danieldietrich Oct 13, 2022
a0ad280
cleanup
danieldietrich Oct 13, 2022
d6e1e3c
finished ReflectContainer
danieldietrich Oct 14, 2022
8b093a1
added type tests
danieldietrich Oct 14, 2022
ac7a916
more validation tests
danieldietrich Oct 14, 2022
e8804b1
validation
danieldietrich Oct 15, 2022
18ea4bb
types
danieldietrich Oct 15, 2022
2f3789c
types
danieldietrich Oct 15, 2022
36fe785
types
danieldietrich Oct 15, 2022
376b9ec
cleanup
danieldietrich Oct 15, 2022
6866499
cleanup
danieldietrich Oct 15, 2022
01d3ff3
validation
danieldietrich Oct 15, 2022
6272d9d
validation
danieldietrich Oct 15, 2022
1c38e6b
ValidationError draft
danieldietrich Oct 15, 2022
e5c8283
ValidationError and cleanup
danieldietrich Oct 15, 2022
dcba644
ValidationError
danieldietrich Oct 15, 2022
f676f53
types & tests
danieldietrich Oct 15, 2022
1f3fd92
types & tests
danieldietrich Oct 15, 2022
6b55eb8
types & tests
danieldietrich Oct 15, 2022
43b7f59
types & tests
danieldietrich Oct 15, 2022
9581822
merge
danieldietrich Oct 16, 2022
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
79 changes: 79 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// see [ESLint Configuration](https://eslint.org/docs/user-guide/configuring)
{
// libs that run in both envs, browser & node, don't access global variables that are browser or node specific
"env": {
"browser": false, // enables Browser global variables, like localStorage
"es6": true, // enables new ES6 global variables, such as Set
"node": true // enables Node global variables, like process
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": { // [typescript-eslint parser configuration](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser#configuration)
"project": "tsconfig.eslint.json", // mandatory when using types
"sourceType": "module" // needed in order to use import declarations
},
"plugins": [
"@typescript-eslint",
"typescript-enum",
"unused-imports"
],
"rules": {

// List of [ESLint rules](https://eslint.org/docs/rules/)
"arrow-parens": ["off", "as-needed"], // do not force arrow function parentheses
"constructor-super": "error", // checks the correct use of super() in sub-classes
"curly": "error", // if statement needs curly braces
"dot-notation": "error", // obj.a instead of obj['a'] when possible
"eqeqeq": "error", // ban '==', don't use 'smart' option!
"guard-for-in": "error", // needs obj.hasOwnProperty(key) checks
"new-parens": "error", // new Error() instead of new Error
"no-bitwise": "error", // bitwise operators &, | can be confused with &&, ||
"no-caller": "error", // ECMAScript deprecated arguments.caller and arguments.callee
"no-cond-assign": "error", // assignments if (a = '1') are error-prone
"no-debugger": "error", // disallow debugger; statements
"no-eval": "error", // eval is considered unsafe
"no-inner-declarations": "off", // we need to have 'namespace' functions when using TS 'export ='
"no-labels": "error", // GOTO is only used in BASIC ;)
"no-multiple-empty-lines": ["error", {"max": 1}], // two or more empty lines need to be fused to one
"no-new-wrappers": "error", // there is no reason to wrap primitve values
"no-throw-literal": "error", // only throw Error but no objects {}
"no-trailing-spaces": "error", // trim end of lines
"no-unsafe-finally": "error", // safe try/catch/finally behavior
"no-unused-vars": "off", // we need unused vars for proper typing
"no-var": "error", // use const and let instead of var
"prefer-const": "error", // use const when possible
"quote-props": ["error", "as-needed", { // defines how object-keys are quoted
"keywords": false,
"unnecessary": true,
"numbers": false
}],
"space-before-function-paren": ["error", { // space in function decl: f() vs async () => {}
"anonymous": "never",
"asyncArrow": "always",
"named": "never"
}],
"unused-imports/no-unused-imports": "error", // no unsused imports
"use-isnan": "error", // isNaN(i) Number.isNaN(i) instead of i === NaN

// List of [typescript-enum rules](https://github.com/shian15810/eslint-plugin-typescript-enum)
"typescript-enum/no-enum": "error", // disallow enums, see https://2ality.com/2020/02/enum-alternatives-typescript.html

// List of [@typescript-eslint rules](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules)
"@typescript-eslint/adjacent-overload-signatures": "error", // grouping same method names
"@typescript-eslint/array-type": ["error", { // string[] instead of Array<string>
"default": "array"
}],
"@typescript-eslint/indent": "error", // consistent indentation
"@typescript-eslint/consistent-type-assertions": "error", // needed for .tsx, bad = <Foo>bar, good = bar as Foo
"@typescript-eslint/no-misused-new": "error", // no constructors for interfaces or new for classes
"@typescript-eslint/no-parameter-properties": "error", // no property definitions in class constructors
"@typescript-eslint/no-var-requires": "error", // use import instead of require
"@typescript-eslint/prefer-for-of": "error", // prefer for-of loop over arrays
"@typescript-eslint/prefer-namespace-keyword": "error", // prefer namespace over module in TypeScript
"@typescript-eslint/triple-slash-reference": "error", // ban /// <reference />, prefer imports
"@typescript-eslint/type-annotation-spacing": "error" // consistent space around colon ':'
}
}
40 changes: 40 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: "Bug report"
about: Report a reproducible bug or regression.
labels: 'bug'

---

# Bug Report

Ginject version:

<!--
Please provide a clear and concise description of what the bug is. Include
screenshots if needed. Please test using the latest version of Ginject to
make sure your issue has not already been fixed.
-->

## Steps To Reproduce

1.
2.

<!--
Your bug will get fixed much faster if we can run your code and it doesn't
have dependencies other than Ginject. Issues without reproduction steps or
code examples may be immediately closed as not actionable.
-->

Link to code example:

<!--
Please provide a link to a repository on GitHub or provide a minimal code
example that reproduces the problem. You may provide a screenshot of some
application if you think it is relevant to your bug report. Here are some
tips for providing a minimal example: https://stackoverflow.com/help/mcve.
-->

## The current behavior

## The expected behavior
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Question
url: https://github.com/langium/ginject/discussions
about: Please ask questions here.
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
name: Feature request
about: Suggest an idea for this project
labels: 'feature-request'

---
<!-- Please search existing issues to avoid creating duplicates. -->
<!-- Describe the feature you'd like. -->
7 changes: 7 additions & 0 deletions .github/ISSUE_TEMPLATE/improvement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
name: General improvement
about: Suggest an improvement for this project

---
<!-- Please search existing issues to avoid creating duplicates. -->
<!-- Describe the improvement you'd like to see in the project. -->
27 changes: 27 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Build

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
name: Ginject
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Build
shell: bash
run: |
npm ci
npm test
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.DS_Store
*.tgz
coverage/
dist/
lib/
node_modules/
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 TypeFox GmbH
Copyright (c) 2022 TypeFox GmbH

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
209 changes: 209 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<div id="ginject-logo" align="center">
<a href="https://github.com/langium/ginject">
<img alt="Ginject Logo" width="450" src="https://user-images.githubusercontent.com/743833/193610222-cf9a7feb-b1d9-4d5c-88de-6ce9fbca8299.png">
</a>
<h3>
Dependency injection done right.
</h3>
</div>

<div id="badges" align="center">

[![npm version](https://img.shields.io/npm/v/ginject?logo=npm&style=flat-square)](https://www.npmjs.com/package/ginject/)
[![build](https://img.shields.io/github/workflow/status/langium/ginject/Build/main?logo=github&style=flat-square)](https://github.com/langium/ginject/actions/workflows/build.yml)
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod&style=flat-square)](https://gitpod.io/#https://github.com/langium/ginject)

</div>

<br>

**Ginject** [ʤɪnject] is a non-intrusive and typesafe dependency injection library for Node.js and JavaScript, powered by TypeScript.

**Ginject** empowers developers designing decoupled applications and frameworks. **Ginject**'s main goal is increasing the developer experience by offering a tiny, yet powerful API, keeping dependencies in central module definitions and by using TypeScript's type system to restrain runtime challenges.

The concept of **ginject**'s central module definition is inspired by [Google Guice](https://github.com/google/guice). However, **ginject** is going further by lifting the API to the functional level.

Despite its simplicity, **ginject** is powerful enough to cover all features provided by [Inversify](https://github.com/inversify/InversifyJS). Direct support for classes and constructors, property injection, rebinding dependencies and dependency cycle detection are just a few of the features worth mentioning.

<br>

<div id="ginject vs inversify" align="center">

| | ginject | inversify |
|------------------|:----------:|:-----------:|
| minified | [![minified size](https://img.shields.io/bundlephobia/min/ginject?label=&style=flat-square)](https://bundlephobia.com/result?p=ginject@latest) | [![minified size](https://img.shields.io/bundlephobia/min/inversify?label=&style=flat-square)](https://bundlephobia.com/result?p=inversify@latest) |
| minzipped | [![minzipped size](https://img.shields.io/bundlephobia/minzip/ginject?label=&style=flat-square)](https://bundlephobia.com/result?p=ginject@latest) | [![minzipped size](https://img.shields.io/bundlephobia/minzip/inversify?label=&style=flat-square)](https://bundlephobia.com/result?p=inversify@latest) |
| typesafe | ✅ | ❌ |
| requirements | none | decorators |
| style | functional | imperative |
| API surface area | tiny | non-trivial |

</div>

<br>

## Quickstart

The first step is to add **ginject** to your application.

```sh
npm i ginject
```

Bascially, the only thing needed is to define **modules** of **factories** and finally call **inject**. The resulting **container** provides concrete **instances**.

```ts
import { inject } from 'ginject';

// create an inversion of control container
const container = inject({
hi: () => 'Hi',
sayHi: () => (name: string) => `${container.hi} ${name}!`
});

// prints 'Hi Ginject!'
console.log(container.sayHi('Ginject'));
```

## API

### Terminology

The **inject** function is turning **modules** into a **container**. A **module** is a plain vanilla JS object, composed of nested **groups** and **dependency factories**. Factories may return any JS value, e.g. constants, singletons and providers. Unlike [Inversify](https://github.com/inversify/InversifyJS), there is no need to decorate classes.

```ts
import { inject, Module } from 'ginject';

// Defining a _context_ of dependencies
type Context = {
group: {
value: Value // any JS type, here a class
}
}

// A _module_ contains nested _groups_ (optional) and _factories_
const module: Module<Context> = {
group: {
// a factory of type Factory<Context, Value>
value: (ctx: Context) => new Value(ctx)
}
};

// A _container_ of type Container<Module<Context>> = Context
const container = inject(module);

// Values can be obtained from the container
const value = container.group.value;
```

### Context

A **container** provides each **factory** with a parameter called **context**.

```ts
type C = {
value: string
}

const container = inject({
factory: (ctx: C) => () => ctx.value
});
```

The **context** of type **C** provides a **value** that can't be resolved. The **inject** call is type-checked by TS the way that the completeness of the arguments is validated.

Such **missing dependencies** need to be provided by adding additional **modules** to the **inject** call.

```ts
const container = inject({
factory: (ctx: C) => () => ctx.value
}, {
value: () => '🍸'
});
```

Now the compiler is satisfied and we can start using the **container**.

```ts
// prints 🍸
console.log(container.factory());
```

You might have noticed that the **container** automatically calls the **factory** and **injects** itself as the **context**. The use-site receives the **value**.

### Eager vs lazy initialization

A dependency **container.group.value** is **lazily** initialized when first accessed on the container. Turn a factory **eager** to initialize the dependency at the time of the **inject** call.

A use case for **eager initialization** would be to ensure that **side effects** take place during the initialization of the **container**.

```ts
import { eager, inject, Module } from 'ginject';

type C = {
gin: string
}

const module: Module<C> = {
gin: eager(() => {
const gin = '🍸';
console.log('Gin mixed');
return gin;
})
}

const ctr = inject(module);

console.log('App started');

ctr.gin
```

In the **eager** case, the output is

```
Gin mixed
App started
```

In the **lazy** case, the output is

```
App started
Gin mixed
```

Please note that **eager factories** overwrite **lazy factories** vice versa when **rebinding** them.

### Rebinding dependencies

The main advantage of **dependency injection** arises from the fact that an application is able to **rebind dependencies**. That way the **structure** of a system can be fixated while the **behavior** can be changed.

The main vehicle for **rebinding dependencies** is the **inject** function which receives a variable amount of **modules**.

The behavior of an application can be enhanced by overwriting existing functionality using additional modules.

```ts
type C = {
readonly print: () => void
eval: (a: number, b: number) => number
}

const module_0: Module<C> = {
print: (ctx) => () => {
console.log(ctx.eval(1, 1));
},
eval: () => (a, b) => a + b
};

const ctr = inject(module_0, {
eval: () => (a: number, b: number) => a * b
});

// = 1
ctr.print();
```

### Cyclic Dependencies

### Asynchronous Factories
Loading