├── .eslintignore
├── .eslintrc.json
├── .github
└── workflows
│ └── pr-checks.yml
├── .gitignore
├── .prettierrc
├── README.md
├── babel.config.js
├── jest.config.js
├── jest.setup.js
├── package.json
├── src
├── index.test.js
└── index.tsx
├── tsconfig.json
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | .next/*
2 | coverage/*
3 | locales/*
4 | deploy/*
5 | mock/*
6 | cypress/*
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "airbnb",
4 | "prettier",
5 | "prettier/react",
6 | "prettier/@typescript-eslint",
7 | "plugin:@typescript-eslint/recommended"
8 | ],
9 | "parserOptions": {
10 | "ecmaVersion": 2018, // Allows for the parsing of modern ECMAScript features
11 | "sourceType": "module" // Allows for the use of imports
12 | },
13 | "parser": "@typescript-eslint/parser",
14 | "rules": {
15 | "import/extensions": [
16 | "error",
17 | "ignorePackages",
18 | {
19 | "js": "never",
20 | "jsx": "never",
21 | "ts": "never",
22 | "tsx": "never"
23 | }
24 | ],
25 | "react/jsx-props-no-spreading": "off",
26 | "no-console": "off",
27 | "react/prop-types": "off",
28 | "react/default-props-match-prop-types": "off",
29 | "react/jsx-no-duplicate-props": ["error", { "ignoreCase": false }],
30 | "react/no-did-mount-set-state": "off",
31 | "react/jsx-filename-extension": [
32 | 1,
33 | {
34 | "extensions": [".js", ".tsx"]
35 | }
36 | ],
37 | "@typescript-eslint/no-var-requires": "warn"
38 | },
39 | "plugins": [
40 | "react",
41 | "prettier",
42 | "import",
43 | "@typescript-eslint",
44 | "react-hooks"
45 | ],
46 | "env": {
47 | "browser": true,
48 | "jest": true
49 | },
50 | "overrides": [
51 | {
52 | "files": ["*.test.js"],
53 | "rules": {
54 | "global-require": 0,
55 | "no-underscore-dangle": 0
56 | }
57 | }
58 | ],
59 | "settings": {
60 | "react": {
61 | "version": "detect"
62 | },
63 | "import/resolver": {
64 | "node": {
65 | "extensions": [".js", ".jsx", ".ts", ".tsx", ".json"]
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/.github/workflows/pr-checks.yml:
--------------------------------------------------------------------------------
1 | name: PR checks
2 |
3 | env:
4 | GPR_TOKEN: ${{ secrets.GPR_TOKEN }}
5 | HUSKY_SKIP_INSTALL: true
6 |
7 | on: pull_request
8 |
9 | jobs:
10 | eslint:
11 | runs-on: ubuntu-18.04
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: actions/setup-node@v1
15 | with:
16 | node-version: 12
17 | - name: Restore node_modules cache
18 | uses: actions/cache@v2
19 | with:
20 | path: '**/node_modules'
21 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
22 | - name: Install packages
23 | run: yarn --frozen-lockfile
24 | - name: 'ESLint'
25 | run: yarn eslint
26 | typecheck:
27 | runs-on: ubuntu-18.04
28 | steps:
29 | - uses: actions/checkout@v2
30 | - uses: actions/setup-node@v1
31 | with:
32 | node-version: 12
33 | - name: Restore node_modules cache
34 | uses: actions/cache@v2
35 | with:
36 | path: '**/node_modules'
37 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
38 | - name: Install packages
39 | run: yarn --frozen-lockfile
40 | - name: 'Type check'
41 | run: yarn typecheck
42 | tests:
43 | runs-on: ubuntu-18.04
44 | steps:
45 | - uses: actions/checkout@v2
46 | - uses: actions/setup-node@v1
47 | with:
48 | node-version: 12
49 | - name: Restore node_modules cache
50 | uses: actions/cache@v2
51 | with:
52 | path: '**/node_modules'
53 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
54 | - name: Install packages
55 | run: yarn --frozen-lockfile
56 | - name: 'Tests'
57 | run: yarn test
58 | build:
59 | runs-on: ubuntu-18.04
60 | steps:
61 | - uses: actions/checkout@v2
62 | - uses: actions/setup-node@v1
63 | with:
64 | node-version: 12
65 | - name: Restore node_modules cache
66 | uses: actions/cache@v2
67 | with:
68 | path: '**/node_modules'
69 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
70 | - name: Install packages
71 | run: yarn --frozen-lockfile
72 | - name: 'Build'
73 | run: yarn prepublishOnly
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | dist/
4 | coverage/
5 | .npmrc
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 120,
3 | "useTabs": false,
4 | "semi": true,
5 | "singleQuote": true,
6 | "jsxSingleQuote":false,
7 | "trailingComma": "none",
8 | "bracketSpacing": true,
9 | "jsxBracketSameLine": false,
10 | "arrowParens": "avoid"
11 | }
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # next-app-builder
2 |
3 | [![Version Badge][npm-version-svg]][package-url]
4 | [![GZipped size][npm-minzip-svg]][bundlephobia-url]
5 | [![dependency status][deps-svg]][deps-url]
6 | [![dev dependency status][dev-deps-svg]][dev-deps-url]
7 | [![License][license-image]][license-url]
8 | [![Downloads][downloads-image]][downloads-url]
9 |
10 | Custom App builder for Next.js.
11 |
12 | ## What is a Custom `App`?
13 |
14 | Next.js uses the App component to initialize pages. You can override it and control the page initialization. Which allows you to do amazing things like:
15 |
16 | - Persisting layout between page changes
17 | - Keeping state when navigating pages
18 | - Custom error handling using componentDidCatch
19 | - Inject additional data into pages
20 | - Add global CSS
21 |
22 | For more details, see [offical documentation](https://nextjs.org/docs/advanced-features/custom-app).
23 |
24 | ## Why a builder?
25 |
26 | Generates a custom next App using middleware.
27 |
28 | Before:
29 |
30 | ```javascript
31 | class CustomNextApp extends App {
32 | static async getInitialProps({ Component, ctx, router }) {
33 | const initialPageProps = await (Component.getInitialProps ? Component.getInitialProps : {});
34 | const data = await fetch(getDataForPage(router.pathname));
35 | return {
36 | pageProps: {
37 | ...initialPageProps,
38 | data
39 | }
40 | };
41 | }
42 |
43 | render() {
44 | const { Component, pageProps } = this.props;
45 | return (
46 |
47 |
48 |
49 |
50 |
51 | );
52 | }
53 | }
54 | ```
55 |
56 | After:
57 |
58 | ```javascript
59 | const ssrDataMiddleware = {
60 | Component: SsrDataProvider,
61 | getInitialProps: ({ router }) => {
62 | const data = await fetch(getDataForPage(router.pathname));
63 | return { data };
64 | }
65 | };
66 |
67 | const layoutMiddleware = { Component: LayoutComponent };
68 |
69 | nextAppBuilder({
70 | middleware: [ssrDataMiddleware, layoutMiddleware]
71 | });
72 | ```
73 |
74 | ## Installation
75 |
76 | Install using Yarn:
77 |
78 | ```sh
79 | yarn add @scacap/next-app-builder
80 | ```
81 |
82 | or NPM:
83 |
84 | ```sh
85 | npm install @scacap/next-app-builder --save
86 | ```
87 |
88 | ## Usage
89 |
90 | ```javascript
91 | // pages/_app.js
92 | import nextAppBuilder from '@scacap/next-app-builder';
93 | import materialUiMiddleware from '../middlewares/material-ui';
94 | import theme from '../theme';
95 |
96 | export default nextAppBuilder({
97 | middleware: [materialUiMiddleware(theme)]
98 | });
99 | ```
100 |
101 | [](https://codesandbox.io/s/custom-next-app-15s4p?fontsize=14&hidenavigation=1&file=/pages/_app.tsx)
102 |
103 | ## API
104 |
105 | ### nextAppBuilder
106 |
107 | Creates a custom app that should be the default export of `pages/_app.js`.
108 |
109 | ```javascript
110 | const CustomApp = appBuilder({
111 | middleware: [
112 | // middlewares here
113 | ]
114 | });
115 | ```
116 |
117 | ### middleware
118 |
119 | An object containing the following fields:
120 |
121 | - **name**: a human readable identifier of middleware. Optional.
122 | - **getInitialProps**: a function which is executed before rendering. Used for blocking data requirements for every single page in your application, e.g. server side data fetching. Optional.
123 | - **Component**: the React component which is rendered in custom App. It's a wrapper component that will receive each page as children. Typically used for adding providers in the App level, e.g. css theme provider. Optional.
124 | - **componentDidCatch**: invoked when a descendant component throws an error. See more details in the [React docs](https://reactjs.org/docs/react-component.html#componentdidcatch). Optional.
125 |
126 | ## Caveats
127 |
128 | Internally, you will be adding a custom getInitialProps in your App. This will disable Automatic Static Optimization in pages without Static Generation.
129 |
130 | For more details, see [offical documentation](https://nextjs.org/docs/advanced-features/custom-app#caveats).
131 |
132 | ## Contributing
133 |
134 | Let's build together our v1! Pull-requests and issue reports are welcome.
135 |
136 | [npm-version-svg]: https://img.shields.io/npm/v/@scacap/next-app-builder.svg
137 | [package-url]: https://www.npmjs.com/package/@scacap/next-app-builder
138 | [bundlephobia-url]: https://bundlephobia.com/result?p=@scacap/next-app-builder
139 | [npm-minzip-svg]: https://img.shields.io/bundlephobia/minzip/@scacap/next-app-builder
140 | [deps-url]: https://david-dm.org/scacap/next-app-builder
141 | [deps-svg]: https://david-dm.org/scacap/next-app-builder.svg
142 | [dev-deps-url]: https://david-dm.org/scacap/next-app-builder?type=dev
143 | [dev-deps-svg]: https://david-dm.org/scacap/next-app-builder/dev-status.svg
144 | [license-url]: https://www.apache.org/licenses/LICENSE-2.0
145 | [license-image]: https://img.shields.io/npm/l/@scacap/next-app-builder.svg
146 | [downloads-url]: https://npm-stat.com/charts.html?package=@scacap/next-app-builder
147 | [downloads-image]: https://img.shields.io/npm/dm/@scacap/next-app-builder.svg
148 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | module.exports = api => {
3 | api.cache(true);
4 | return {
5 | presets: ['@babel/env', '@babel/preset-react', '@babel/typescript'],
6 | plugins: [
7 | '@babel/proposal-object-rest-spread',
8 | '@babel/plugin-proposal-nullish-coalescing-operator',
9 | '@babel/plugin-proposal-class-properties',
10 | '@babel/plugin-proposal-optional-chaining',
11 | '@babel/plugin-transform-runtime'
12 | ]
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | setupFiles: ['./jest.setup.js'],
5 | testPathIgnorePatterns: ['/node_modules/'],
6 | collectCoverage: true,
7 | collectCoverageFrom: ['src/**/*.{ts,tsx,js}'],
8 | coverageReporters: ['lcov', 'text', 'json-summary', 'json'],
9 | testMatch: ['**/?(*.)+(test).{ts,tsx,js}'],
10 | testURL: 'http://localhost'
11 | }
12 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | const { configure } = require('enzyme');
2 | const Adapter = require('enzyme-adapter-react-16');
3 |
4 | configure({ adapter: new Adapter() });
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@scacap/next-app-builder",
3 | "version": "0.0.1",
4 | "description": "Middleware pipeline to create next.js App.",
5 | "main": "./dist/index.js",
6 | "author": "frontend@sclable.capital",
7 | "peerDependencies": {
8 | "next": "^11.1.1",
9 | "react": "^16.13.1"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/ScaCap/next-app-builder.git"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/ScaCap/next-app-builder/issues"
17 | },
18 | "license": "Apache-2.0",
19 | "scripts": {
20 | "eslint-fix": "eslint './src/**/*.{ts,tsx}' --fix",
21 | "eslint": "eslint './src/**/*.{ts,tsx}'",
22 | "cleanup": "rm -rf dist",
23 | "build:types": "tsc --emitDeclarationOnly",
24 | "build:transpile": "babel src --extensions '.js,.ts,.tsx' --out-dir dist",
25 | "prepublishOnly": "yarn cleanup && yarn build:transpile && yarn build:types",
26 | "postpublish": "yarn cleanup",
27 | "typecheck": "tsc --noEmit",
28 | "test": "jest"
29 | },
30 | "private": false,
31 | "devDependencies": {
32 | "@babel/cli": "7.16.0",
33 | "@babel/core": "7.16.0",
34 | "@babel/plugin-proposal-class-properties": "7.16.0",
35 | "@babel/plugin-proposal-nullish-coalescing-operator": "7.16.0",
36 | "@babel/plugin-proposal-object-rest-spread": "7.16.0",
37 | "@babel/plugin-proposal-optional-chaining": "7.16.0",
38 | "@babel/plugin-transform-runtime": "7.16.4",
39 | "@babel/preset-env": "7.16.4",
40 | "@babel/preset-react": "7.16.0",
41 | "@babel/preset-typescript": "7.16.0",
42 | "@types/react": "16.9.35",
43 | "@types/react-dom": "16.9.8",
44 | "@typescript-eslint/eslint-plugin": "2.33.0",
45 | "@typescript-eslint/parser": "2.33.0",
46 | "babel-eslint": "10.0.3",
47 | "babel-loader": "8.1.0",
48 | "enzyme": "3.11.0",
49 | "enzyme-adapter-react-16": "1.15.5",
50 | "eslint": "6.8.0",
51 | "eslint-config-airbnb": "18.0.1",
52 | "eslint-config-prettier": "6.9.0",
53 | "eslint-plugin-import": "2.20.0",
54 | "eslint-plugin-jsx-a11y": "6.2.3",
55 | "eslint-plugin-prettier": "3.1.2",
56 | "eslint-plugin-react": "7.18.0",
57 | "eslint-plugin-react-hooks": "2.3.0",
58 | "jest": "26.6.3",
59 | "next": "12.3.1",
60 | "prettier": "2.1.2",
61 | "react": "16.13.1",
62 | "react-dom": "16.12.0",
63 | "ts-loader": "7.0.4",
64 | "typescript": "3.9.2"
65 | },
66 | "files": [
67 | "dist"
68 | ]
69 | }
70 |
--------------------------------------------------------------------------------
/src/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, mount } from 'enzyme';
3 | import appBuilder from '.';
4 |
5 | describe('App Builder', () => {
6 | const Page = ({ name }) => {`Hello ${name}!`}
;
7 | Page.getInitialProps = () => ({ name: 'Maria' });
8 |
9 | const shallowPage = async middleware => {
10 | const App = appBuilder({ middleware });
11 | const props = await App.getInitialProps({ Component: Page, ctx: { AppTree: () => null } });
12 | return shallow();
13 | };
14 |
15 | it('should render Page correctly', async () => {
16 | const wrapper = await shallowPage([]);
17 | const page = wrapper.find(Page);
18 | expect(page.exists()).toBe(true);
19 | expect(page.html()).toContain('Hello Maria!');
20 | expect(page.props()).toEqual(expect.objectContaining({ name: 'Maria' }));
21 | });
22 |
23 | it('should render middleware Components & page in correct order', async () => {
24 | const middleware1 = { Component: ({ children }) => {children}
};
25 | middleware1.Component.displayName = 'Component1';
26 | const middleware2 = { Component: ({ children }) => {children}
};
27 | middleware2.Component.displayName = 'Component2';
28 |
29 | const wrapper = await shallowPage([middleware1, middleware2]);
30 |
31 | const firstElement = wrapper.find(middleware1.Component);
32 | const secondElement = firstElement.find(middleware2.Component);
33 | expect(secondElement.find(Page).exists()).toBe(true);
34 | });
35 |
36 | it('should pass getInitialProps only to same middleware Component', async () => {
37 | const middleware1 = {
38 | Component: ({ children }) => {children}
,
39 | getInitialProps: () => ({ value: 7 })
40 | };
41 | const middleware2 = {
42 | Component: ({ children }) => {children}
,
43 | getInitialProps: () => ({ value: 42, name: 'Max' })
44 | };
45 | const wrapper = await shallowPage([middleware1, middleware2]);
46 | expect(wrapper.find(Page).props()).toEqual(expect.objectContaining({ name: 'Maria' }));
47 | expect(wrapper.find(middleware1.Component).props()).toEqual(
48 | // partial comparison because it contains children
49 | expect.objectContaining({ value: 7, name: 'Maria' })
50 | );
51 | expect(wrapper.find(middleware2.Component).props()).toEqual(
52 | // partial comparison because it contains children
53 | expect.objectContaining({ value: 42, name: 'Max' })
54 | );
55 | });
56 |
57 | it('should pass the props correctly to the Component, when the internalRenderPage is called', async () => {
58 | let render;
59 | const middleware = {
60 | Component: jest.fn(({ children }) => {children}
),
61 | getInitialProps: ({ AppTree }) => {
62 | // we are "rendering" the component
63 | render = shallow();
64 | return { internal: false };
65 | }
66 | };
67 |
68 | const App = appBuilder({ middleware: [middleware] });
69 | const AppTree = jest.fn(() => null);
70 | const props = await App.getInitialProps({ Component: Page, ctx: { AppTree } });
71 | expect(render.find(AppTree).exists()).toBe(true);
72 | expect(render.find(AppTree).prop('pageProps').internal).toBe(true);
73 |
74 | mount();
75 |
76 | expect(middleware.Component).toHaveBeenCalledTimes(1);
77 |
78 | expect(middleware.Component.mock.calls[0][0]).toEqual(
79 | expect.objectContaining({ internal: false })
80 | );
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { ErrorInfo, FunctionComponent } from 'react';
2 | import App from 'next/app';
3 | import { AppContext } from 'next/dist/pages/_app';
4 |
5 | export type NextAppMiddleware> = {
6 | /**
7 | * human readable identifier of middleware
8 | */
9 | name?: string;
10 | /**
11 | * Static function which is executed before rendering.
12 | * Used for blocking data requirements for every single page in your application, e.g. server side data fetching.
13 | */
14 | getInitialProps?(appContext: AppContext): T | Promise;
15 | /**
16 | * Component which is rendered in custom App.
17 | */
18 | Component?: FunctionComponent;
19 | componentDidCatch?(error: Error, errorInfo: ErrorInfo): App['componentDidCatch'];
20 | };
21 |
22 | type NextAppBuilderOptions = {
23 | middleware: NextAppMiddleware[];
24 | };
25 |
26 | type NextAppMiddlewareBuilder = (options: NextAppBuilderOptions) => typeof App;
27 |
28 | type ExecuteComponentDidCatchMiddleware = (allMiddlewarec, error: Error, _errorInfo: ErrorInfo) => void;
29 |
30 | const executeComponentDidCatchMiddleware: ExecuteComponentDidCatchMiddleware = (allMiddleware, error, errorInfo) =>
31 | allMiddleware.forEach(({ componentDidCatch }) => {
32 | if (componentDidCatch) {
33 | componentDidCatch(error, errorInfo);
34 | }
35 | });
36 |
37 | const renderPage = (allMiddleware, { Component: PageComponent, pageProps: { middlewareProps, ...props } }) =>
38 | allMiddleware
39 | .filter(({ Component: MiddlewareComponent }) => !!MiddlewareComponent)
40 | .reduceRight(
41 | (nestedElement, { Component: MiddlewareComponent, id }) => (
42 |
43 | {nestedElement}
44 |
45 | ),
46 |
47 | );
48 |
49 | /**
50 | * Generates a custom next App using middleware.
51 | *
52 | * Usage
53 | *
54 | * ```
55 | *
56 | * const getInitialProps = ({ router }) => {
57 | * const data = await fetch(getDataForPage(router.pathname));
58 | * return { data };
59 | * }
60 | *
61 | * const ssrDataMiddleware = {
62 | * Component: SsrDataProvider,
63 | * getInitialProps
64 | * };
65 | * const layoutMiddleware = { Component: LayoutComponent };
66 | *
67 | * nextAppBuilder({
68 | * middleware: [
69 | * ssrDataMiddleware,
70 | * layoutMiddleware
71 | * ]
72 | * })
73 | *
74 | * ```
75 | *
76 | * @param middleware
77 | */
78 | const nextAppBuilder: NextAppMiddlewareBuilder = ({ middleware = [] }) => {
79 | const allMiddleware = middleware.map((singleMiddleware, index) => ({
80 | ...singleMiddleware,
81 | id: `nextAppMiddleware-${index}`
82 | }));
83 |
84 | class NextAppMiddlewareComponent extends App {
85 | static async getInitialProps({ Component, ctx, router }): Promise<{ pageProps: any }> {
86 | let pageProps = {};
87 | const { AppTree } = ctx;
88 | const extendPageProps = props => {
89 | pageProps = {
90 | ...pageProps,
91 | ...props
92 | };
93 | };
94 | if (Component.getInitialProps) {
95 | extendPageProps(await Component.getInitialProps(ctx));
96 | }
97 |
98 | let middlewareProps;
99 |
100 | const InternalAppTree = props => {
101 | const enhancedPageProps = { ...pageProps, middlewareProps, ...props };
102 | return ;
103 | };
104 |
105 | const allInitialProps = await Promise.all(
106 | allMiddleware.map(async ({ getInitialProps, id, name }) => {
107 | let initialProps = {};
108 | if (getInitialProps) {
109 | try {
110 | initialProps = await getInitialProps({
111 | Component,
112 | router,
113 | ctx,
114 | AppTree: InternalAppTree
115 | });
116 | } catch (error) {
117 | console.warn(`getInitialProps failed for middleware with name ${name || 'unnamed'}`, error);
118 | }
119 | }
120 | return { initialProps, id };
121 | })
122 | );
123 |
124 | middlewareProps = allInitialProps.reduce(
125 | (props, { id, initialProps }) => ({
126 | ...props,
127 | [id]: initialProps
128 | }),
129 | {}
130 | );
131 |
132 | extendPageProps({ middlewareProps });
133 | return { pageProps };
134 | }
135 |
136 | componentDidCatch(error, errorInfo): void {
137 | executeComponentDidCatchMiddleware(allMiddleware, error, errorInfo);
138 | // This is needed to render errors correctly in development / production
139 | // eslint-disable-next-line
140 | // @ts-ignore
141 | super.componentDidCatch(error, errorInfo);
142 | }
143 |
144 | render(): JSX.Element {
145 | const { Component, pageProps, ...otherProps } = this.props;
146 |
147 | return renderPage(allMiddleware, {
148 | Component,
149 | // eslint-disable-next-line
150 | // @ts-ignore
151 | pageProps: { ...pageProps, ...otherProps }
152 | });
153 | }
154 | }
155 | return NextAppMiddlewareComponent;
156 | };
157 |
158 | export default nextAppBuilder;
159 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "outDir": "./dist",
6 | "declaration": true,
7 | "skipLibCheck": true,
8 | "strict": false,
9 | "strictNullChecks": true,
10 | "noImplicitAny": false,
11 | "noImplicitThis": true,
12 | "alwaysStrict": true,
13 | "strictBindCallApply": true,
14 | "strictFunctionTypes": true,
15 | "strictPropertyInitialization": true,
16 | "jsx": "react",
17 | "moduleResolution": "node",
18 | "noEmit": false,
19 | "forceConsistentCasingInFileNames": true,
20 | "noErrorTruncation": true,
21 | "resolveJsonModule": true,
22 | "lib": ["es6", "dom"],
23 | "esModuleInterop": true,
24 | "listEmittedFiles":true
25 | },
26 | "exclude": ["node_modules", "**/*.test.js", "dist"],
27 | "include": ["src"]
28 | }
29 |
--------------------------------------------------------------------------------