├── .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 | 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 | 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 |
15 | 16 | Vite logo 17 | 18 | 19 | React logo 20 | 21 | 22 | Deepkit logo 23 | 24 |
25 |

Vite + React + Deepkit

26 | 27 |
28 | 31 |

32 | Edit src/App.tsx and save to test HMR 33 |

34 |
35 | 36 | 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 | --------------------------------------------------------------------------------