├── .changeset ├── README.md └── config.json ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── CHANGELOG.md ├── README.md ├── babel.config.cjs ├── package.json ├── renovate.json ├── rollup.config.js ├── src ├── createUseTransition.spec.tsx ├── createUseTransition.ts └── main.ts ├── tsconfig.build-cjs.json ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "linked": [], 6 | "access": "restricted", 7 | "baseBranch": "main", 8 | "updateInternalDependencies": "patch", 9 | "ignore": [] 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | jobs: 5 | ci: 6 | name: CI 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Cache node_modules 11 | id: cache-modules 12 | uses: actions/cache@v2 13 | with: 14 | path: node_modules 15 | key: 14.x-${{ runner.OS }}-build-${{ hashFiles('yarn.lock') }} 16 | - uses: actions/setup-node@v2 17 | with: 18 | node-version: "14.x" 19 | registry-url: "https://registry.npmjs.org" 20 | - name: Install dependencies 21 | if: steps.cache-modules.outputs.cache-hit != 'true' 22 | run: yarn 23 | - name: Test 24 | run: yarn test 25 | - name: Build 26 | run: yarn build 27 | - name: Type Check 28 | run: yarn type-check 29 | - name: Configure Git Credentials 30 | if: github.repository == 'n1ru4l/react-use-transition' 31 | run: | 32 | git config --global user.email "github-action@users.noreply.github.com" 33 | git config --global user.name "Github Action" 34 | - name: Publish 35 | if: github.repository == 'n1ru4l/react-use-transition' 36 | id: publish-step 37 | run: | 38 | cd dist 39 | yarn version --no-git-tag-version --prerelease --preid ${GITHUB_SHA::8} 40 | yarn publish --tag canary 41 | PACKAGE_VERSION=$(node -e "console.log(require('./package.json').version)") 42 | echo "::set-output name=PACKAGE_VERSION::$PACKAGE_VERSION" 43 | env: 44 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 45 | - name: Create commit comment 46 | uses: peter-evans/commit-comment@v1 47 | if: github.repository == 'n1ru4l/react-use-transition' 48 | with: 49 | body: | 50 | The changes have been published to npm. 51 | ```bash 52 | yarn add -D @n1ru4l/react-use-transition@${{ steps.publish-step.outputs.PACKAGE_VERSION }} 53 | ``` 54 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | release: 10 | name: Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout Repo 14 | uses: actions/checkout@master 15 | with: 16 | # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits 17 | fetch-depth: 0 18 | 19 | - name: Setup Node.js 14.x 20 | uses: actions/setup-node@master 21 | with: 22 | node-version: 14.x 23 | 24 | - name: Install Dependencies 25 | run: yarn 26 | 27 | - name: Build 28 | run: yarn build 29 | 30 | - name: Create Release Pull Request or Publish to npm 31 | id: changesets 32 | uses: changesets/action@master 33 | with: 34 | # This expects you to have a script called release which does a build for your packages and calls changeset publish 35 | publish: yarn release 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode" 4 | } 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @n1ru4l/react-use-transition 2 | 3 | ## 0.4.3 4 | 5 | ### Patch Changes 6 | 7 | - 2f1a958: BREAKING: switch out tuple return types. 8 | 9 | ```diff 10 | - const [showLoadingIndicator, cachedData] = useTransition() 11 | + const [cachedData, showLoadingIndicator] = useTransition() 12 | ``` 13 | 14 | ## 0.4.2 15 | 16 | ### Patch Changes 17 | 18 | - 001dc61: fix publish script 19 | 20 | ## 0.4.1 21 | 22 | ### Patch Changes 23 | 24 | - db294c0: Include README.md in published package. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @n1ru4l/react-use-transition 2 | 3 | [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) 4 | [![npm](https://img.shields.io/npm/v/@n1ru4l/react-use-transition.svg)](https://www.npmjs.com/package/@n1ru4l/react-use-transition) 5 | [![npm bundle size](https://img.shields.io/bundlephobia/min/@n1ru4l/react-use-transition)](https://bundlephobia.com/result?p=@n1ru4l/react-use-transition) 6 | [![Dependencies](https://img.shields.io/david/n1ru4l/react-use-transition)](https://www.npmjs.com/package/@n1ru4l/react-use-transition) 7 | [![NPM](https://img.shields.io/npm/dm/@n1ru4l/react-use-transition.svg)](https://www.npmjs.com/package/@n1ru4l/react-use-transition) 8 | 9 | Suspense like transitions without experimental react features today. For any fetching library. 10 | 11 | ## Why? 12 | 13 | Ever experienced flashy transitions where content disappears after triggering navigation, a loading state shows up for like 10 milliseconds, disappears and a new page is rendered? Did you wonder why it was even necessary to show a loading page in the first place? 14 | 15 | That is exactly what this micro library tries to solve. Cache the previous result in case a transition occurs and only show some kind of loading indicator after a certain threshold (by default 300ms) has been reached without the new data arriving. 16 | 17 | The concept is taken from the experimental `React.useTransition` which is not stable as today and only available as an experimental build. This hook however works without React concurrent mode. 18 | 19 | Use it together with your favorite GraphQL client such as `relay` or `urql` or any other data fetching library. 20 | 21 | ## Install 22 | 23 | ```bash 24 | yarn install -E @n1ru4l/react-use-transition 25 | ``` 26 | 27 | ## Usage 28 | 29 | [Check out the code sandbox](https://codesandbox.io/s/usetransition-85v3c?file=/src/index.js) 30 | 31 | ```tsx 32 | import * as React from "react"; 33 | import { unstable_batchedUpdates } from "react-dom"; // or react-native (your react reconciler) 34 | import { createUseTransition } from "@n1ru4l/react-use-transition"; 35 | 36 | import { useQuery } from "your-fetching-library-of-choice"; 37 | 38 | const useTransition = createUseTransition(unstable_batchedUpdates); 39 | 40 | const DataFetchingComponent = ({ postId }) => { 41 | const { data, isLoading } = useQuery("/foo/:postId", { postId }); 42 | const [cachedData, showLoadingIndicator] = useTransition(data, isLoading); 43 | 44 | return ( 45 | <> 46 | {showLoadingIndicator ? : null} 47 | {cachedData ? ( 48 | cachedData.error ? ( 49 | 50 | ) : ( 51 | 52 | ) 53 | ) : null} 54 | 55 | ); 56 | }; 57 | ``` 58 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ["@babel/preset-env", { targets: { node: "current" } }], 4 | "@babel/preset-typescript", 5 | "@babel/preset-react", 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@n1ru4l/react-use-transition", 3 | "version": "0.4.3", 4 | "repository": "https://github.com/n1ru4l/use-transition", 5 | "author": "n1ru4l", 6 | "license": "MIT", 7 | "devDependencies": { 8 | "@babel/core": "7.14.3", 9 | "@babel/preset-env": "7.14.2", 10 | "@babel/preset-react": "7.13.13", 11 | "@babel/preset-typescript": "7.13.0", 12 | "@changesets/cli": "2.16.0", 13 | "@rollup/plugin-typescript": "8.2.1", 14 | "@testing-library/react": "11.2.7", 15 | "@types/react": "17.0.6", 16 | "@types/react-dom": "17.0.5", 17 | "babel-jest": "26.6.3", 18 | "cpy-cli": "3.1.1", 19 | "cross-env": "7.0.3", 20 | "jest": "26.6.3", 21 | "prettier": "2.3.0", 22 | "react": "17.0.2", 23 | "react-dom": "17.0.2", 24 | "rimraf": "3.0.2", 25 | "rollup": "2.48.0", 26 | "tslib": "2.2.0", 27 | "typescript": "4.2.4" 28 | }, 29 | "peerDependencies": { 30 | "react": "17.x.x" 31 | }, 32 | "type": "module", 33 | "types": "./main.d.ts", 34 | "typescript": { 35 | "definition": "./main.d.ts" 36 | }, 37 | "module": "./main.js", 38 | "main": "./cjs/main.js", 39 | "exports": { 40 | ".": { 41 | "import": "./main.js", 42 | "require": "./cjs/main.js" 43 | }, 44 | "./package.json": "./package.json", 45 | "./": "./" 46 | }, 47 | "scripts": { 48 | "build:cjs": "cross-env MODE=cjs rollup -c", 49 | "build:esm": "rollup -c", 50 | "build": "rimraf dist && yarn build:cjs && yarn build:esm && cpy README.md ./dist", 51 | "test": "jest", 52 | "type-check": "tsc", 53 | "release": "changeset publish" 54 | }, 55 | "keywords": [ 56 | "react", 57 | "hook", 58 | "transition", 59 | "query", 60 | "fetch", 61 | "suspense" 62 | ], 63 | "publishConfig": { 64 | "directory": "dist", 65 | "access": "public" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base"], 4 | "postUpdateOptions": ["yarnDedupeFewer"], 5 | "packageRules": [ 6 | { 7 | "groupName": "react", 8 | "packagePatterns": [ 9 | "@types/react", 10 | "@types/react-dom", 11 | "react", 12 | "react-dom" 13 | ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import ts from "@rollup/plugin-typescript"; 2 | import { promises as fs } from "fs"; 3 | 4 | const isCJSBuild = process.env.MODE === "cjs"; 5 | 6 | const commonjsPkgJSONPlugin = () => { 7 | return { 8 | name: "commonjsPkgJSONPlugin", 9 | writeBundle: async () => { 10 | if (isCJSBuild === true) { 11 | await fs.writeFile( 12 | "dist/cjs/package.json", 13 | JSON.stringify({ 14 | type: "commonjs", 15 | }) 16 | ); 17 | } else { 18 | await fs.copyFile("package.json", "dist/package.json"); 19 | } 20 | }, 21 | }; 22 | }; 23 | 24 | export default { 25 | input: ["src/main.ts"], 26 | output: [ 27 | { 28 | dir: isCJSBuild ? "dist/cjs" : "dist", 29 | format: isCJSBuild ? "cjs" : "esm", 30 | }, 31 | ], 32 | plugins: [ 33 | ts({ 34 | tsconfig: isCJSBuild ? "tsconfig.build-cjs.json" : "tsconfig.build.json", 35 | }), 36 | commonjsPkgJSONPlugin(), 37 | ], 38 | external: ["react"], 39 | }; 40 | -------------------------------------------------------------------------------- /src/createUseTransition.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { createUseTransition } from "./createUseTransition"; 3 | import { test, expect, beforeEach, jest } from "@jest/globals"; 4 | import { render, act } from "@testing-library/react"; 5 | 6 | const useTransition = createUseTransition(act); 7 | 8 | let state: ReturnType | null = null; 9 | 10 | beforeEach(() => { 11 | state?.unmount(); 12 | }); 13 | 14 | test("returns the current value for a non-loading object", () => { 15 | expect.assertions(2); 16 | const TestComponent = () => { 17 | const [data, isLoading] = useTransition({ hello: "hi" }, false); 18 | expect(isLoading).toEqual(false); 19 | expect(data).toEqual({ hello: "hi" }); 20 | return null; 21 | }; 22 | state = render(); 23 | }); 24 | 25 | test("returns the loading value for a loading object", () => { 26 | expect.assertions(2); 27 | const TestComponent = () => { 28 | const [data, isLoading] = useTransition({ hello: "hi" }, true); 29 | expect(isLoading).toEqual(true); 30 | expect(data).toEqual({ hello: "hi" }); 31 | return null; 32 | }; 33 | state = render(); 34 | }); 35 | 36 | test("returns the loading value for a loading object", () => { 37 | expect.assertions(2); 38 | const TestComponent = () => { 39 | const [data, isLoading] = useTransition({ hello: "hi" }, true); 40 | expect(isLoading).toEqual(true); 41 | expect(data).toEqual({ hello: "hi" }); 42 | return null; 43 | }; 44 | state = render(); 45 | }); 46 | 47 | test("returns the correct value after loading has finished", () => { 48 | expect.assertions(4); 49 | let renderCount = 0; 50 | const TestComponent = (state: { 51 | isLoading: boolean; 52 | data: string | null; 53 | }) => { 54 | renderCount = renderCount + 1; 55 | const [data, isLoading] = useTransition(state.data, state.isLoading); 56 | if (renderCount === 1) { 57 | expect(isLoading).toEqual(true); 58 | expect(data).toEqual(null); 59 | } 60 | if (renderCount === 2) { 61 | expect(isLoading).toEqual(false); 62 | expect(data).toEqual("foo"); 63 | } 64 | return null; 65 | }; 66 | state = render(); 67 | state.rerender(); 68 | }); 69 | 70 | test("updates isLoading to 'true' after threshold is reached after entering loading state", () => { 71 | jest.useFakeTimers(); 72 | 73 | let renderCount = 0; 74 | const TestComponent = (state: { 75 | isLoading: boolean; 76 | data: string | null; 77 | }) => { 78 | renderCount = renderCount + 1; 79 | const [data, isLoading] = useTransition(state.data, state.isLoading); 80 | if (renderCount === 1) { 81 | expect(isLoading).toEqual(false); 82 | expect(data).toEqual("foo"); 83 | } 84 | if (renderCount === 2) { 85 | expect(isLoading).toEqual(false); 86 | expect(data).toEqual("foo"); 87 | } 88 | if (renderCount === 3) { 89 | expect(isLoading).toEqual(true); 90 | expect(data).toEqual("foo"); 91 | } 92 | return null; 93 | }; 94 | state = render(); 95 | state.rerender(); 96 | jest.runAllTimers(); 97 | }); 98 | 99 | test("updates isLoading to 'false' and data to the latest value after leaving the loading state.", async () => { 100 | expect.assertions(8); 101 | jest.useFakeTimers(); 102 | 103 | let onThirdRender: () => void; 104 | const didRenderForThirdTime = new Promise( 105 | (res) => (onThirdRender = res) 106 | ); 107 | 108 | let renderCount = 0; 109 | const TestComponent = (state: { 110 | isLoading: boolean; 111 | data: string | null; 112 | }) => { 113 | renderCount = renderCount + 1; 114 | const [data, isLoading] = useTransition(state.data, state.isLoading); 115 | if (renderCount === 1) { 116 | expect(isLoading).toEqual(false); 117 | expect(data).toEqual("foo"); 118 | } 119 | if (renderCount === 2) { 120 | expect(isLoading).toEqual(false); 121 | expect(data).toEqual("foo"); 122 | } 123 | if (renderCount === 3) { 124 | expect(isLoading).toEqual(true); 125 | expect(data).toEqual("foo"); 126 | onThirdRender(); 127 | } 128 | if (renderCount === 4) { 129 | expect(isLoading).toEqual(false); 130 | expect(data).toEqual("hi"); 131 | } 132 | return null; 133 | }; 134 | state = render(); 135 | state.rerender(); 136 | jest.runAllTimers(); 137 | // we wait until the third render occurred for our next update to ensure the component is consistently rendered with every value. 138 | await didRenderForThirdTime; 139 | state.rerender(); 140 | }); 141 | -------------------------------------------------------------------------------- /src/createUseTransition.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | /** 4 | * Create a useTransition hook for a specific react reconciler. 5 | */ 6 | export function createUseTransition( 7 | /** 8 | * The unstable_BatchedUpdates function for a specific react-reconciler. 9 | */ 10 | batchedUpdates = (run: () => void) => run(), 11 | /** 12 | * The used setTimeout function for scheduling the timeout after which a loading indicator should be shown. 13 | * Passing this argument might be handy for testing. 14 | */ 15 | _setTimeout = setTimeout 16 | ) { 17 | /** 18 | * Returns a transition state that holds the old value until the loading of the new value has finished. 19 | */ 20 | return function useTransition( 21 | /** 22 | * The latest data that got loaded. 23 | */ 24 | data: TType, 25 | /** 26 | * Whether a new value is currently being loaded. 27 | */ 28 | isLoading: boolean, 29 | /** 30 | * Threshold after which showing a loading indicator might be useful. 31 | */ 32 | shouldShowLoadingIndicatorThreshold = 300 33 | ): [TType, boolean] { 34 | const [, triggerStateUpdate] = React.useState(1); 35 | const ref = React.useRef({ 36 | data, 37 | previousIsLoading: isLoading, 38 | shouldShowLoadingIndicator: isLoading, 39 | }); 40 | 41 | React.useEffect(() => { 42 | if (!isLoading) { 43 | ref.current.data = data; 44 | ref.current.shouldShowLoadingIndicator = false; 45 | } 46 | 47 | let timeout: NodeJS.Timeout | null = null; 48 | if (ref.current.previousIsLoading !== isLoading && isLoading) { 49 | timeout = _setTimeout(() => { 50 | batchedUpdates(() => { 51 | ref.current.shouldShowLoadingIndicator = true; 52 | triggerStateUpdate((i) => i + 1); 53 | }); 54 | }, shouldShowLoadingIndicatorThreshold); 55 | } 56 | 57 | ref.current.previousIsLoading = isLoading; 58 | 59 | return () => { 60 | if (timeout) { 61 | clearTimeout(timeout); 62 | } 63 | }; 64 | }, [isLoading, triggerStateUpdate, shouldShowLoadingIndicatorThreshold]); 65 | 66 | if (!isLoading) { 67 | return [data, isLoading]; 68 | } 69 | 70 | return [ref.current.data, ref.current.shouldShowLoadingIndicator]; 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | export * from "./createUseTransition"; 2 | -------------------------------------------------------------------------------- /tsconfig.build-cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.build.json", 3 | "compilerOptions": { 4 | "outDir": "dist/cjs", 5 | "declaration": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "declaration": true, 6 | "noEmit": false 7 | }, 8 | "exclude": ["src/**/*.spec.tsx"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "jsx": "preserve", 5 | "noEmit": true 6 | }, 7 | "include": ["src"] 8 | } 9 | --------------------------------------------------------------------------------