├── .eslintrc.cjs
├── .gitignore
├── README.md
├── index.html
├── media
├── deepkit_logo.svg
└── deepkit_logo_dark.svg
├── package.json
├── public
└── vite.svg
├── src
├── App.css
├── App.tsx
├── assets
│ ├── deepkit_white.svg
│ └── react.svg
├── database.ts
├── index.css
├── logger.ts
├── main.tsx
└── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: { browser: true, es2020: true },
3 | extends: [
4 | 'eslint:recommended',
5 | 'plugin:@typescript-eslint/recommended',
6 | 'plugin:react-hooks/recommended',
7 | ],
8 | parser: '@typescript-eslint/parser',
9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
10 | plugins: ['react-refresh'],
11 | rules: {
12 | 'react-refresh/only-export-components': 'warn',
13 | '@typescript-eslint/no-unused-vars': 'off',
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React + Deepkit + Vite
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | For the first time, you can use full-featured Dependency Injection Container in React
16 | based on TypeScript types. No decorators, no magic strings, no extensive boilerplate.
17 |
18 | ```tsx
19 | import { ProviderWithScope, ServiceContainer } from "@deepkit/injector";
20 | import { Logger } from "./logger.ts";
21 |
22 | const providers: ProviderWithScope[] = [
23 | Logger,
24 | { provide: User, useValue: new User('anonymous') },
25 | ];
26 |
27 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
28 |
29 |
30 |
31 |
32 | ,
33 | )
34 |
35 | // in your components you can inject all providers, either reference a class or interface
36 | function App(props: object, logger: Logger, user: User) {
37 | return (
38 | <>
39 | logger.log('Hi there')}>Log message
40 | Current user: {user.name}
41 | >
42 | );
43 | }
44 | ```
45 |
46 | ## Try this repository
47 |
48 | ```sh
49 | git clone https://github.com/marcj/typescript-react-dependency-injection
50 | cd typescript-react-dependency-injection
51 | npm install
52 | npm run dev
53 | ```
54 |
55 | ## How to use
56 |
57 | You want to use this in your project? Here is how:
58 |
59 | First, setup your project. This repository used the following command
60 |
61 | ```json
62 | npm create vite@latest react-deepkit --template react-ts
63 | ```
64 |
65 | But it is up to you how you setup your project.
66 |
67 | ```sh
68 | npm install --save-dev @deepkit/type-compiler @deepkit/vite
69 | npm install @deepkit/injector @deepkit/type
70 | ```
71 |
72 | In your `vite.config.ts`:
73 |
74 | ```typescript
75 | import { defineConfig } from 'vite'
76 | import react from '@vitejs/plugin-react-swc'
77 | import { deepkitType } from "@deepkit/vite";
78 |
79 | // https://vitejs.dev/config/
80 | export default defineConfig({
81 | plugins: [react(), deepkitType()],
82 | })
83 | ```
84 |
85 | Instead of vite, you can also use the regular TypeScript compiler.
86 | This should work out of the box, but see
87 | https://docs.deepkit.io/english/runtime-types.html#runtime-types-installation for more information.
88 |
89 | Esbuild is not supported yet, but will be soon.
90 |
91 | Enable reflection (runtime type information from Deepkit).
92 |
93 | ```json
94 | {
95 | "compilerOptions": {
96 | //...
97 | },
98 | "reflection": true
99 | }
100 | ```
101 |
102 | Done. Now you can use the `@deepkit/injector` package in your main file.
103 |
104 | ```tsx
105 | import { ProviderWithScope, ServiceContainer } from "@deepkit/injector";
106 | import { Logger } from "./logger.ts";
107 |
108 | // all these providers are available in your whole application
109 | // by simply defining them in your function signature.
110 | const providers: ProviderWithScope[] = [
111 | Logger,
112 | { provide: User, useValue: new User('anonymous') },
113 | ];
114 |
115 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
116 |
117 |
118 |
119 |
120 | ,
121 | );
122 | ```
123 |
124 | In all your components (functional or classes based) you can now inject all providers.
125 | You can either reference the class directly or interfaces.
126 |
127 | ```tsx
128 | export function App(props: {}, auth: Auth, logger: Logger) {
129 | return (
130 | <>
131 | logger.log('Hi there')}>Log message
132 | Current user: {auth.user.name}
133 | >
134 | );
135 | }
136 | ```
137 |
138 | See https://docs.deepkit.io/english/dependency-injection.html for more information.
139 |
140 | ## How it works
141 |
142 | Deepkit Type Compiler is a TypeScript compiler plugin that generates metadata for
143 | all classes, interfaces, enums, and functions. This metadata is used by the
144 | `@deepkit/injector` package to create a dependency injection container.
145 |
146 | ## Why
147 |
148 | Dependency Injection is a great way to structure your application. It allows you
149 | to write testable code, and it makes it easy to replace dependencies with mocks
150 | or other implementations without maintaining your own registry of dependencies
151 | or adjusting all code that uses them.
152 |
153 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/media/deepkit_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | logo/deepkit_3
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/media/deepkit_logo_dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | deepkit_logo_dark
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-deepkit",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@deepkit/injector": "^1.0.1-alpha.94",
14 | "@deepkit/type": "^1.0.1-alpha.94",
15 | "react": "^18.2.0",
16 | "react-dom": "^18.2.0"
17 | },
18 | "devDependencies": {
19 | "@deepkit/type-compiler": "^1.0.1-alpha.94",
20 | "@deepkit/vite": "^1.0.1-alpha.94",
21 | "@types/react": "^18.0.28",
22 | "@types/react-dom": "^18.0.11",
23 | "@typescript-eslint/eslint-plugin": "^5.57.1",
24 | "@typescript-eslint/parser": "^5.57.1",
25 | "@vitejs/plugin-react-swc": "^3.0.0",
26 | "eslint": "^8.38.0",
27 | "eslint-plugin-react-hooks": "^4.6.0",
28 | "eslint-plugin-react-refresh": "^0.3.4",
29 | "typescript": "^5.0.2",
30 | "vite": "^4.3.2"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 | .logo.deepkit:hover {
21 | filter: drop-shadow(0 0 2em rgba(0, 128, 255, 0.71));
22 | }
23 |
24 | @keyframes logo-spin {
25 | from {
26 | transform: rotate(0deg);
27 | }
28 | to {
29 | transform: rotate(360deg);
30 | }
31 | }
32 |
33 | @media (prefers-reduced-motion: no-preference) {
34 | a:nth-of-type(2) .logo {
35 | animation: logo-spin infinite 20s linear;
36 | }
37 | }
38 |
39 | .card {
40 | padding: 2em;
41 | }
42 |
43 | .read-the-docs {
44 | color: #888;
45 | }
46 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import reactLogo from './assets/react.svg'
3 | import viteLogo from '/vite.svg'
4 | import deepkitLogo from './assets/deepkit_white.svg'
5 |
6 | import './App.css'
7 | import { Logger } from "./logger.ts";
8 |
9 | function App(props: object, logger: Logger) {
10 | const [count, setCount] = useState(0)
11 |
12 | return (
13 | <>
14 |
25 | Vite + React + Deepkit
26 |
27 |
28 |
setCount((count) => count + 1)}>
29 | count is {count}
30 |
31 |
32 | Edit src/App.tsx
and save to test HMR
33 |
34 |
35 |
36 | logger.log('Hi there')}>Log message
37 |
38 |
39 |
40 | Click on the Vite and React logos to learn more
41 |
42 | >
43 | )
44 | }
45 |
46 | export default App
47 |
--------------------------------------------------------------------------------
/src/assets/deepkit_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | deepkit_logo
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/database.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | export class Database {
7 | //datbase example
8 |
9 |
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3 | line-height: 1.5;
4 | font-weight: 400;
5 |
6 | color-scheme: light dark;
7 | color: rgba(255, 255, 255, 0.87);
8 | background-color: #242424;
9 |
10 | font-synthesis: none;
11 | text-rendering: optimizeLegibility;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | -webkit-text-size-adjust: 100%;
15 | }
16 |
17 | a {
18 | font-weight: 500;
19 | color: #646cff;
20 | text-decoration: inherit;
21 | }
22 | a:hover {
23 | color: #535bf2;
24 | }
25 |
26 | body {
27 | margin: 0;
28 | display: flex;
29 | place-items: center;
30 | min-width: 320px;
31 | min-height: 100vh;
32 | }
33 |
34 | h1 {
35 | font-size: 3.2em;
36 | line-height: 1.1;
37 | }
38 |
39 | button {
40 | border-radius: 8px;
41 | border: 1px solid transparent;
42 | padding: 0.6em 1.2em;
43 | font-size: 1em;
44 | font-weight: 500;
45 | font-family: inherit;
46 | background-color: #1a1a1a;
47 | cursor: pointer;
48 | transition: border-color 0.25s;
49 | }
50 | button:hover {
51 | border-color: #646cff;
52 | }
53 | button:focus,
54 | button:focus-visible {
55 | outline: 4px auto -webkit-focus-ring-color;
56 | }
57 |
58 | @media (prefers-color-scheme: light) {
59 | :root {
60 | color: #213547;
61 | background-color: #ffffff;
62 | }
63 | a:hover {
64 | color: #747bff;
65 | }
66 | button {
67 | background-color: #f9f9f9;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/logger.ts:
--------------------------------------------------------------------------------
1 | export class Logger {
2 | log(...messages: any[]) {
3 | console.log(...messages);
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 | import { ProviderWithScope, ServiceContainer } from "@deepkit/injector";
6 | import { Logger } from "./logger.ts";
7 |
8 | const providers: ProviderWithScope[] = [
9 | Logger
10 | ];
11 |
12 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
13 |
14 |
15 |
16 |
17 | ,
18 | )
19 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
5 | "module": "ESNext",
6 | "skipLibCheck": true,
7 |
8 | /* Bundler mode */
9 | "moduleResolution": "bundler",
10 | "allowImportingTsExtensions": true,
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "noEmit": true,
14 | "jsx": "react-jsx",
15 |
16 | /* Linting */
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true
21 | },
22 | "reflection": true,
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react-swc'
3 | import { deepkitType } from "@deepkit/vite";
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [react(), deepkitType()],
8 | })
9 |
--------------------------------------------------------------------------------