├── .all-contributorsrc
├── .eslintignore
├── .eslintrc
├── .github
├── FUNDING.yml
├── labeler.yml
└── workflows
│ ├── codeql.yml
│ ├── label.yml
│ └── node.js.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .storybook
└── main.js
├── .stylelintignore
├── .stylelintrc
├── .travis.yml
├── LICENSE
├── README.md
├── SECURITY.md
├── babel.config.js
├── jest.config.js
├── package.json
├── postcss.config.js
├── public
├── favicon.ico
├── manifest.json
├── react.png
├── robots.txt
└── template.html
├── src
├── actionTypes
│ └── CounterActionTypes.js
├── actions
│ └── CounterAction.js
├── assets
│ └── images
│ │ ├── React.webp
│ │ └── wavesOpacity.svg
├── components
│ ├── App.jsx
│ ├── App.test.jsx
│ ├── home
│ │ ├── Home.jsx
│ │ ├── Home.scss
│ │ ├── Home.story.jsx
│ │ └── Home.test.jsx
│ ├── loader
│ │ ├── Loader.jsx
│ │ ├── Loader.scss
│ │ ├── Loader.story.jsx
│ │ └── Loader.test.jsx
│ └── not-found
│ │ ├── NotFound.jsx
│ │ ├── NotFound.scss
│ │ ├── NotFound.story.jsx
│ │ └── NotFound.test.jsx
├── constants
│ ├── 404Constants.js
│ ├── AppConstants.js
│ ├── CommonConstants.js
│ └── HomeConstants.js
├── index.css
├── index.jsx
├── pure-components
│ └── Button
│ │ ├── Button.jsx
│ │ ├── Button.story.jsx
│ │ └── Button.test.jsx
├── reducers
│ ├── CounterReducer.js
│ └── index.js
├── redux
│ └── store.js
├── routes
│ ├── Routes.jsx
│ └── Routes.lazy.jsx
├── sagas
│ ├── DecrCountSaga.js
│ ├── IncrCountSaga.js
│ └── index.js
├── server
│ └── server.jsx
├── service-worker.js
├── tests
│ └── __mocks__
│ │ ├── FileMock.js
│ │ └── SetupTests.js
└── utils
│ └── RestClient.js
├── tailwind.config.js
└── webpack
├── client
├── webpack.common.config.js
├── webpack.dev.config.js
└── webpack.prod.config.js
└── server
├── webpack.common.config.js
├── webpack.dev.config.js
└── webpack.prod.config.js
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "anandgupta193",
10 | "name": "Anand gupta",
11 | "avatar_url": "https://avatars2.githubusercontent.com/u/24511864?v=4",
12 | "profile": "https://github.com/anandgupta193",
13 | "contributions": [
14 | "code"
15 | ]
16 | }
17 | ],
18 | "contributorsPerLine": 7,
19 | "projectName": "react-enterprise-starter-kit",
20 | "projectOwner": "anandgupta193",
21 | "repoType": "github",
22 | "repoHost": "https://github.com",
23 | "skipCi": true
24 | }
25 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | dist/*
3 | src/assets/*
4 | coverage/*
5 | server-build/*
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "browser": true,
5 | "es6": true,
6 | "node": true,
7 | "jest": true
8 | },
9 | "globals": {
10 | "__DEVELOPMENT__": true,
11 | "__SERVER__": true
12 | },
13 | "parserOptions": {
14 | "ecmaVersion": 2020,
15 | "ecmaFeatures": {
16 | "impliedStrict": true,
17 | "jsx": true
18 | },
19 | "sourceType": "module"
20 | },
21 | "plugins": ["react", "react-hooks", "prettier"],
22 | "extends": [
23 | "eslint:recommended",
24 | "plugin:react/recommended",
25 | "plugin:react-hooks/recommended",
26 | "airbnb"
27 | ],
28 | "settings": {
29 | "react": {
30 | "version": "detect"
31 | }
32 | },
33 | "rules": {
34 | "no-console": "error",
35 | "max-len": ["error", 140, { "code": 80 }],
36 | "import/no-extraneous-dependencies": "off",
37 | "quotes": ["error", "single", { "allowTemplateLiterals": true }]
38 | },
39 | "ignorePatterns": ["dist"]
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: anandgupta193
2 |
--------------------------------------------------------------------------------
/.github/labeler.yml:
--------------------------------------------------------------------------------
1 | # Add 'repo' label to any root file changes
2 | repo:
3 | - any: ['./*']
4 |
5 | # Add 'test' label to any change to *.spec.js files within the source dir
6 | test:
7 | - any: ['src/**/*.test.jsx']
8 |
9 | # Add 'source' label to any change to src files within the source dir EXCEPT for the docs sub-folder
10 | source:
11 | - any: ['src/**/*', '!src/docs/*']
12 |
13 | # Add 'github-workflows' label to any change to workflows files within the .github dir
14 | github-workflows:
15 | - any: ['.github/**/*']
16 |
17 | # Add 'webpack' label to any change to workflows files within the webpack dir
18 | webpack:
19 | - any: ['webpack/**/*']
20 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 | schedule:
9 | - cron: "28 13 * * 0"
10 |
11 | jobs:
12 | analyze:
13 | name: Analyze
14 | runs-on: ubuntu-latest
15 | permissions:
16 | actions: read
17 | contents: read
18 | security-events: write
19 |
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | language: [ javascript ]
24 |
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v3
28 |
29 | - name: Initialize CodeQL
30 | uses: github/codeql-action/init@v2
31 | with:
32 | languages: ${{ matrix.language }}
33 | queries: +security-and-quality
34 |
35 | - name: Autobuild
36 | uses: github/codeql-action/autobuild@v2
37 |
38 | - name: Perform CodeQL Analysis
39 | uses: github/codeql-action/analyze@v2
40 | with:
41 | category: "/language:${{ matrix.language }}"
42 |
--------------------------------------------------------------------------------
/.github/workflows/label.yml:
--------------------------------------------------------------------------------
1 | # This workflow will triage pull requests and apply a label based on the
2 | # paths that are modified in the pull request.
3 | #
4 | # To use this workflow, you will need to set up a .github/labeler.yml
5 | # file with configuration. For more information, see:
6 | # https://github.com/actions/labeler
7 |
8 | name: "Pull Request Labeler"
9 | on:
10 | - pull_request
11 |
12 | jobs:
13 | triage:
14 | runs-on: macos-latest
15 | steps:
16 | - uses: actions/labeler@master
17 | with:
18 | repo-token: "${{ secrets.GITHUB_TOKEN }}"
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node CI Build
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: macos-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [18.x]
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 | - name: Use Node.js ${{ matrix.node-version }}
24 | uses: actions/setup-node@v1
25 | with:
26 | node-version: ${{ matrix.node-version }}
27 | - run: npm i
28 | - run: npm run build-prod
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | coverage
4 | .vscode
5 | server-build
6 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist/
2 | package-lock.json
3 | package.json
4 | node_modules/
5 | coverage/
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": true,
6 | "endOfLine": "lf",
7 | "printWidth": 140
8 | }
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | // Export a function. Accept the base config as the only param.
4 | module.exports = {
5 | stories: ["../src/**/*.story.jsx"],
6 | webpackFinal: async (config, { configType }) => {
7 | // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
8 | // You can change the configuration based on that.
9 | // 'PRODUCTION' is used when building the static version of storybook.
10 |
11 | // Make whatever fine-grained changes you need
12 | config.module.rules.push({
13 | test: /\.scss$/,
14 | use: ['style-loader', 'css-loader', 'sass-loader'],
15 | include: path.resolve(__dirname, '../src'),
16 | });
17 |
18 | // Return the altered config
19 | return config;
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | dist/*
3 | src/assets/*
4 | src/**/*.jsx
5 | src/**/*.js
6 | coverage/*
7 | server-build/*
8 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | 'string-quotes': 'single',
4 | 'no-duplicate-at-import-rules' : true,
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 18
4 | cache:
5 | yarn: false
6 | directories:
7 | - node_modules
8 | env:
9 | matrix:
10 | - CI=true
11 | jobs:
12 | include:
13 | - stage: deployment
14 | name: Deploy artifacts to Netlify
15 | before_deploy:
16 | - npm run build-prod
17 | - npm install netlify-cli -g
18 | deploy:
19 | provider: script
20 | script: netlify deploy -s $NETLIFY_SITE_ID --auth $NETLIFY_ACCESS_TOKEN -p --dir
21 | ./dist
22 | skip_cleanup: true
23 | on:
24 | push:
25 | branches:
26 | only:
27 | - master
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Anand gupta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | [](https://github.com/anandgupta193/react-enterprise-starter-kit/blob/master/LICENSE)
3 | 
4 | [](https://travis-ci.com/anandgupta193/react-enterprise-starter-kit)
5 | [](https://app.netlify.com/sites/distracted-knuth-6fa18c/deploys)
6 |
7 | 
8 |
9 | # React Enterprise Starter Kit :fire:
10 |
11 | Introducing the Enterprise Starter Kit 🚀, the perfect React boilerplate for web developers and industries looking to improve their web building capabilities. Our Starter Kit is not just highly scalable and performant 🚀💪, it's also incredibly flexible, making it perfect for use in multiple projects 💪🌟.
12 |
13 | Our Enterprise Starter Kit has been specifically designed to give developers a solid foundation for building high-quality, feature-rich web applications 🌟👨💻. With a carefully curated set of tools, frameworks, and libraries, we've created a starter kit that provides a streamlined development experience, allowing you to focus on building your application rather than worrying about the underlying infrastructure 🤓.
14 |
15 | Here are some of the key features of our Enterprise Starter Kit:
16 |
17 | * 🔥 Highly scalable architecture: Our starter kit is built with scalability in mind, allowing you to easily scale your application as your user base grows.
18 |
19 | * ⚡ Performant: Our starter kit uses the latest performance optimization techniques to ensure your application is fast and responsive.
20 |
21 | * 💪 Flexibility: Our starter kit is highly customizable, allowing you to modify and enhance it to suit your specific needs.
22 |
23 | * 📖 Comprehensive documentation: Our starter kit comes with detailed documentation, making it easy for developers to get up and running quickly.
24 |
25 | * 👨💻 Built with industry best practices: Our starter kit is built using the latest industry best practices, ensuring your application is secure, maintainable, and easy to deploy.
26 |
27 | * 💼 Ready for enterprise use: Our Starter Kit is designed for enterprise use, making it the perfect solution for businesses and organizations that need to build scalable, performant, and reliable web applications.
28 |
29 | * 🔍 SEO-friendly: Our Starter Kit is optimized for search engines, making it easier for your web application to rank higher on search engine results pages (SERPs).
30 |
31 | * 💻🌟🚀 Whether you're a seasoned web developer or just getting started with React, our Enterprise Starter Kit has everything you need to build high-quality web applications quickly and efficiently. So why wait? Download our Starter Kit today and start building amazing web applications! 💻🌟🚀
32 |
33 | ## Key Points:
34 |
35 | * Completely based on latest react hooks using React version ^18.2.0.
36 |
37 | * Redux store is in place with saga as middleware.
38 |
39 | * Complete control over webpack for dev and production builds.
40 |
41 | * Comes with all eslint standard rules for consistent codebase.
42 |
43 | * Highly Scalable due to its atomic design.
44 |
45 | * React router with lazy loding features for better performance and route based chunking.
46 |
47 | * Server side rendering for faster page load.
48 |
49 | * Progressive web app with customizable service worker.
50 |
51 | * Google Lighthouse score is 100 for performance, accessibility, best practices, SEO and PWA.
52 |
53 | * Configurable Test cases setup using latest react testing library with complete HTML coverage report.
54 |
55 | * Continious Integration and Deployment is setup using Travis and Netlify.
56 |
57 | * Components documentation using React Storybook.
58 |
59 | * Github actions for labeling your PR's and build passing status checks using Node CI.
60 |
61 |
62 | ### Demo - https://reactenterprisestarterkit.netlify.app/ (Deployed on Netlify)
63 |
64 |
65 |
66 | Run the following commands to run project in local machine
67 |
68 | To start the project
69 |
70 | ```bash
71 | npm run start-dev
72 | ```
73 |
74 | To Create development build
75 |
76 | ```bash
77 | npm run build-dev
78 | ```
79 |
80 | To Create production build
81 |
82 | ```bash
83 | npm run build-prod
84 | ```
85 |
86 | To run test cases
87 |
88 | ```bash
89 | npm run test
90 | ```
91 |
92 | To run storybook
93 |
94 | ```bash
95 | npm run storybook
96 | ```
97 |
98 | To run Bundle Analyzer
99 |
100 | ```bash
101 | npm run build-analyze
102 | ```
103 |
104 |
105 | ## Contributors ✨
106 |
107 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
108 |
109 |
110 |
111 |
112 |
122 |
123 |
124 |
125 |
126 |
127 | Contributions of any kind welcome!
128 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | | ------- | ------------------ |
10 | | 5.1.x | :white_check_mark: |
11 | | 5.0.x | :x: |
12 | | 4.0.x | :white_check_mark: |
13 | | < 4.0 | :x: |
14 |
15 | ## Reporting a Vulnerability
16 |
17 | Use this section to tell people how to report a vulnerability.
18 |
19 | Tell them where to go, how often they can expect to get an update on a
20 | reported vulnerability, what to expect if the vulnerability is accepted or
21 | declined, etc.
22 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | esmodules: true,
8 | },
9 | },
10 | ],
11 | '@babel/preset-react',
12 | ],
13 | };
14 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | verbose: true,
3 | testEnvironment: 'jest-environment-jsdom',
4 | moduleNameMapper: {
5 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|webp)$':
6 | '/src/tests/__mocks__/FileMock.js',
7 | '\\.(css|scss|less)$': 'identity-obj-proxy',
8 | },
9 | setupFilesAfterEnv: ['/src/tests/__mocks__/SetupTests.js'],
10 | coverageReporters: ['json', ['lcov', { projectRoot: './' }]],
11 | collectCoverage: true,
12 | collectCoverageFrom: [
13 | 'src/**/*.{js,jsx}',
14 | '!src/tests/**/*',
15 | '!src/index.jsx',
16 | '!src/**/*.story.{js,jsx}',
17 | ],
18 | coverageThreshold: {
19 | global: {
20 | branches: 0,
21 | functions: 20,
22 | lines: 20,
23 | statements: 20,
24 | },
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-boilerplate",
3 | "version": "2.0.0",
4 | "description": "Awesome React Starter Kit that can scale for an enterprise application with the very easy maintainable codebase.",
5 | "main": "index.jsx",
6 | "scripts": {
7 | "test": "jest",
8 | "start-dev": "better-npm-run start-dev",
9 | "build-dev": "better-npm-run build-dev",
10 | "build-prod": "better-npm-run build-prod",
11 | "build-analyze": "better-npm-run build-dev-analyze",
12 | "eslint": "eslint --config ./.eslintrc --ext .js,jsx ./src --fix",
13 | "storybook": "start-storybook",
14 | "prettify": "prettier --config ./.prettierrc --ext .js,jsx,.scss ./src --write",
15 | "stylelint": "stylelint --config ./.stylelintrc --aei .scss ./src",
16 | "build-dev-ssr": "npm run build-dev && better-npm-run build-dev-ssr",
17 | "build-prod-ssr": "npm run build-prod && better-npm-run build-prod-ssr",
18 | "start-dev-ssr": "npm run build-dev-ssr && node ./server-build/server.js --inspect",
19 | "start-prod-ssr": "npm run build-prod-ssr && node ./server-build/server.js"
20 | },
21 | "betterScripts": {
22 | "build-dev-ssr": {
23 | "command": "webpack --progress --config ./webpack/server/webpack.dev.config.js",
24 | "env": {
25 | "NODE_ENV": "development",
26 | "PORT": 3000
27 | }
28 | },
29 | "build-prod-ssr": {
30 | "command": "webpack --progress --config ./webpack/server/webpack.prod.config.js",
31 | "env": {
32 | "NODE_ENV": "production",
33 | "PORT": 3000
34 | }
35 | },
36 | "build-dev": {
37 | "command": "webpack --progress --config ./webpack/client/webpack.dev.config.js",
38 | "env": {
39 | "NODE_ENV": "development",
40 | "PORT": 3000
41 | }
42 | },
43 | "build-dev-analyze": {
44 | "command": "webpack -w --progress --config ./webpack/client/webpack.dev.config.js",
45 | "env": {
46 | "NODE_ENV": "production",
47 | "PORT": 3000,
48 | "analyze": true
49 | }
50 | },
51 | "build-prod": {
52 | "command": "webpack --progress --config ./webpack/client/webpack.prod.config.js",
53 | "env": {
54 | "NODE_ENV": "production",
55 | "PORT": 3000
56 | }
57 | },
58 | "start-dev": {
59 | "command": "webpack-dev-server --config ./webpack/client/webpack.dev.config.js --open",
60 | "env": {
61 | "NODE_ENV": "development",
62 | "PORT": 3000
63 | }
64 | }
65 | },
66 | "keywords": [
67 | "react",
68 | "javascript",
69 | "webpack",
70 | "babel",
71 | "jsx",
72 | "html",
73 | "css",
74 | "tailwind",
75 | "server side rendering",
76 | "eslint",
77 | "storybook",
78 | "redux",
79 | "saga"
80 | ],
81 | "author": "Anand (@aanandgupta193) & Shubham (@shubhamsWEB)",
82 | "license": "ISC",
83 | "dependencies": {
84 | "@babel/core": "^7.21.4",
85 | "@babel/node": "^7.20.7",
86 | "@storybook/react": "^7.0.6",
87 | "axios": "^1.3.6",
88 | "prop-types": "^15.7.2",
89 | "react": "^18.2.0",
90 | "react-dom": "^18.2.0",
91 | "react-redux": "^8.0.5",
92 | "react-router-dom": "^6.10.0",
93 | "redux": "^4.0.5",
94 | "redux-devtools-extension": "^2.13.9",
95 | "redux-saga": "^1.1.3"
96 | },
97 | "devDependencies": {
98 | "@babel/polyfill": "^7.10.4",
99 | "@babel/preset-env": "^7.10.3",
100 | "@babel/preset-react": "^7.10.1",
101 | "@testing-library/jest-dom": "^5.11.1",
102 | "@testing-library/react": "^14.0.0",
103 | "autoprefixer": "^10.4.14",
104 | "babel-eslint": "^10.1.0",
105 | "babel-jest": "^29.5.0",
106 | "babel-loader": "^9.1.2",
107 | "better-npm-run": "^0.1.1",
108 | "clean-webpack-plugin": "^4.0.0",
109 | "compression-webpack-plugin": "^10.0.0",
110 | "copy-webpack-plugin": "^11.0.0",
111 | "css-loader": "^6.7.3",
112 | "css-minimizer-webpack-plugin": "^5.0.0",
113 | "eslint": "^8.39.0",
114 | "eslint-config-airbnb": "^19.0.4",
115 | "eslint-plugin-import": "^2.22.0",
116 | "eslint-plugin-jsx-a11y": "^6.3.1",
117 | "eslint-plugin-prettier": "^4.2.1",
118 | "eslint-plugin-react": "^7.20.1",
119 | "eslint-plugin-react-hooks": "^4.0.4",
120 | "extract-css-chunks-webpack-plugin": "^4.7.5",
121 | "file-loader": "^6.0.0",
122 | "html-webpack-plugin": "^5.5.1",
123 | "husky": "^8.0.3",
124 | "identity-obj-proxy": "^3.0.0",
125 | "ignore-styles": "^5.0.1",
126 | "jest": "^29.5.0",
127 | "jest-environment-jsdom": "^29.5.0",
128 | "lint-staged": "^13.2.1",
129 | "mini-css-extract-plugin": "^2.7.5",
130 | "nodemon": "^2.0.4",
131 | "postcss": "^8.4.23",
132 | "postcss-loader": "^7.2.4",
133 | "prettier": "^2.0.5",
134 | "react-test-renderer": "^18.2.0",
135 | "sass": "^1.62.0",
136 | "sass-loader": "^13.2.2",
137 | "style-loader": "^3.3.2",
138 | "stylelint": "^15.6.0",
139 | "stylelint-webpack-plugin": "^4.1.1",
140 | "tailwindcss": "^3.3.1",
141 | "webpack": "^5.80.0",
142 | "webpack-bundle-analyzer": "^4.6.1",
143 | "webpack-cli": "^5.0.2",
144 | "webpack-dev-server": "^4.11.0",
145 | "webpack-manifest-plugin": "^5.0.0",
146 | "webpack-merge": "^5.0.9",
147 | "webpack-node-externals": "^3.0.0",
148 | "workbox-precaching": "^6.5.4",
149 | "workbox-webpack-plugin": "^6.5.4"
150 | },
151 | "lint-staged": {
152 | "*.{js,jsx}": "npm run eslint"
153 | },
154 | "husky": {
155 | "hooks": {
156 | "pre-commit": "lint-staged && npm run build-prod"
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const autoprefixer = require('autoprefixer');
2 | const tailwindcss = require('tailwindcss');
3 |
4 | module.exports = {
5 | plugins: [
6 | tailwindcss('./tailwind.config.js'),
7 | autoprefixer,
8 | ],
9 | };
10 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/anandgupta193/react-enterprise-starter-kit/70c476938a8d68d06515b95a94ff34cabeb15117/public/favicon.ico
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "icons": [
3 | {
4 | "src": "react.png",
5 | "sizes": "384x384",
6 | "type": "image/png"
7 | },
8 | {
9 | "src": "react.png",
10 | "sizes": "256x256",
11 | "type": "image/png"
12 | },
13 | {
14 | "src": "react.png",
15 | "sizes": "192x192",
16 | "type": "image/png"
17 | },
18 | {
19 | "src": "react.png",
20 | "sizes": "128x128",
21 | "type": "image/png"
22 | },
23 | {
24 | "src": "react.png",
25 | "sizes": "96x96",
26 | "type": "image/png"
27 | },
28 | {
29 | "src": "react.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "any maskable"
33 | }
34 | ],
35 | "name": "React Enterprise Starter Kit",
36 | "short_name": "Application",
37 | "orientation": "portrait",
38 | "display": "standalone",
39 | "start_url": "/",
40 | "description": "Description!",
41 | "background_color": "#01579b",
42 | "theme_color": "#01579b",
43 | "prefer_related_applications": true
44 | }
--------------------------------------------------------------------------------
/public/react.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/anandgupta193/react-enterprise-starter-kit/70c476938a8d68d06515b95a94ff34cabeb15117/public/react.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
--------------------------------------------------------------------------------
/public/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React Enterprise Starter Kit
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/actionTypes/CounterActionTypes.js:
--------------------------------------------------------------------------------
1 | export const INCREMENT = 'INCREMENT';
2 | export const DECREMENT = 'DECREMENT';
3 | export const INCREMENT_SUCCESS = 'INCREMENT_SUCCESS';
4 | export const DECREMENT_SUCCESS = 'DECREMENT_SUCCESS';
5 |
--------------------------------------------------------------------------------
/src/actions/CounterAction.js:
--------------------------------------------------------------------------------
1 | import { INCREMENT, DECREMENT } from '../actionTypes/CounterActionTypes';
2 |
3 | export const addCount = () => ({
4 | type: INCREMENT,
5 | });
6 |
7 | export const substractCount = () => ({
8 | type: DECREMENT,
9 | });
10 |
--------------------------------------------------------------------------------
/src/assets/images/React.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/anandgupta193/react-enterprise-starter-kit/70c476938a8d68d06515b95a94ff34cabeb15117/src/assets/images/React.webp
--------------------------------------------------------------------------------
/src/assets/images/wavesOpacity.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Home from './home/Home';
3 |
4 | const App = () => ;
5 |
6 | export default App;
7 |
--------------------------------------------------------------------------------
/src/components/App.test.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | describe('[App Component Test Suite]', () => {
6 | test('should pass if app component is able to render successfully', () => {
7 | render();
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/home/Home.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import HomeStyles from './Home.scss';
3 | import {
4 | WELCOME_HEADING,
5 | IMG_ALT,
6 | REPO_URL,
7 | GITHUB_FORK_TEXT,
8 | LICENCE_BADGE,
9 | FORK_BADGE,
10 | STARS_BADGE,
11 | } from '../../constants/HomeConstants';
12 | import ReactLogo from '../../assets/images/React.webp';
13 | import CloudsImage from '../../assets/images/wavesOpacity.svg';
14 |
15 | const Home = () => (
16 | <>
17 |
18 |
19 | {GITHUB_FORK_TEXT}
20 |
21 |
22 |

23 |
{WELCOME_HEADING}
24 |
29 |
30 | >
31 | );
32 | export default Home;
33 |
--------------------------------------------------------------------------------
/src/components/home/Home.scss:
--------------------------------------------------------------------------------
1 | .logo {
2 | display: block;
3 | margin-left: auto;
4 | margin-right: auto;
5 | width: 220px;
6 | height: 220px;
7 | animation: spin 12s infinite linear;
8 | }
9 | @keyframes spin {
10 | from {
11 | transform: rotate(0deg);
12 | }
13 | to {
14 | transform: rotate(360deg);
15 | }
16 | }
17 | .heading {
18 | display: block;
19 | margin-left: auto;
20 | margin-right: auto;
21 | color: #ffffff;
22 | font-size: 3rem;
23 | font-family: 'Montserrat', sans-serif;
24 | text-align: center;
25 | }
26 | .badges {
27 | display: block;
28 | margin-left: auto;
29 | margin-right: auto;
30 | text-align: center;
31 | img {
32 | margin-left: 10px;
33 | }
34 | }
35 | .forkonGithub a {
36 | background: #101010;
37 | color: #fff;
38 | text-decoration: none;
39 | font-family: arial, sans-serif;
40 | text-align: center;
41 | font-weight: bold;
42 | padding: 5px 40px;
43 | font-size: 1rem;
44 | line-height: 2rem;
45 | position: relative;
46 | transition: 0.5s;
47 | }
48 | .forkongithub a {
49 | background: #101010;
50 | color: #fff;
51 | text-decoration: none;
52 | font-family: arial, sans-serif;
53 | text-align: center;
54 | font-weight: bold;
55 | padding: 5px 40px;
56 | font-size: 1rem;
57 | line-height: 2rem;
58 | position: relative;
59 | transition: 0.5s;
60 | }
61 | .forkonGithub a:hover {
62 | background: #fff;
63 | color: #000;
64 | }
65 | .forkonGithub a::before,
66 | .forkonGithub a::after {
67 | content: '';
68 | width: 100%;
69 | display: block;
70 | position: absolute;
71 | top: 1px;
72 | left: 0;
73 | height: 1px;
74 | background: #fff;
75 | }
76 | .forkonGithub a::after {
77 | bottom: 1px;
78 | top: auto;
79 | }
80 | @media screen and (min-width: 300px) {
81 | .forkonGithub {
82 | position: fixed;
83 | display: block;
84 | top: 0;
85 | right: 0;
86 | width: 200px;
87 | overflow: hidden;
88 | height: 200px;
89 | z-index: 9999;
90 | }
91 | .forkonGithub a {
92 | width: 200px;
93 | position: absolute;
94 | top: 60px;
95 | right: -60px;
96 | transform: rotate(45deg);
97 | -webkit-transform: rotate(45deg);
98 | -ms-transform: rotate(45deg);
99 | -moz-transform: rotate(45deg);
100 | -o-transform: rotate(45deg);
101 | box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.8);
102 | }
103 | }
104 | body {
105 | background: #101010 !important;
106 | margin: 0;
107 | }
108 | .app {
109 | position: absolute;
110 | top: 50%;
111 | left: 50%;
112 | transform: translate(-50%,-50%);
113 | }
114 |
--------------------------------------------------------------------------------
/src/components/home/Home.story.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '../../pure-components/Button/Button';
3 |
4 | export default { title: 'Home' };
5 |
6 | export const withText = () => ;
7 |
8 | export const withEmoji = () => ;
9 |
--------------------------------------------------------------------------------
/src/components/home/Home.test.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import { WELCOME_HEADING } from '../../constants/HomeConstants';
4 | import Home from './Home';
5 |
6 | describe('[Home Component Test Suite]', () => {
7 | test('should pass if "React Enterprise Starter Kit" is found in the document', () => {
8 | render();
9 | expect(screen.getByText(WELCOME_HEADING)).toBeInTheDocument();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/components/loader/Loader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Styles from './Loader.scss';
3 |
4 | const Loader = () => ;
5 | export default Loader;
6 |
--------------------------------------------------------------------------------
/src/components/loader/Loader.scss:
--------------------------------------------------------------------------------
1 | .loader {
2 | display: block;
3 | margin-left: auto;
4 | margin-right: auto;
5 | margin-top: 50%;
6 | border: 16px solid #f3f3f3; /* Light grey */
7 | border-top: 16px solid gray; /* Blue */
8 | border-radius: 50%;
9 | width: 100px;
10 | height: 100px;
11 | animation: spin 2s linear infinite;
12 | }
13 | @keyframes spin {
14 | 0% {
15 | transform: rotate(0deg);
16 | }
17 | 100% {
18 | transform: rotate(360deg);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/loader/Loader.story.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Loader from './Loader';
3 |
4 | export default { title: 'Loader' };
5 |
6 | export const Default = () => ;
7 |
--------------------------------------------------------------------------------
/src/components/loader/Loader.test.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import Loader from './Loader';
4 |
5 | describe('[Loader Component Test Suite]', () => {
6 | test('should pass if loader component can be loaded', () => {
7 | render();
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/not-found/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Styles from './NotFound.scss';
3 | import { ERROR_CODE, ERROR_TEXT } from '../../constants/404Constants';
4 |
5 | const NotFound = () => (
6 |
7 |
{ERROR_CODE}
8 | {ERROR_TEXT}
9 |
10 | );
11 |
12 | export default NotFound;
13 |
--------------------------------------------------------------------------------
/src/components/not-found/NotFound.scss:
--------------------------------------------------------------------------------
1 | .bold {
2 | color: red;
3 | }
4 | .container {
5 | position: absolute;
6 | top: 50%;
7 | left: 50%;
8 | transform: translate(-50%,-50%);
9 | text-align: center;
10 | }
--------------------------------------------------------------------------------
/src/components/not-found/NotFound.story.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '../../pure-components/Button/Button';
3 |
4 | export default { title: 'Not Found' };
5 |
6 | export const withText = () => ;
7 |
8 | export const withEmoji = () => ;
9 |
--------------------------------------------------------------------------------
/src/components/not-found/NotFound.test.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import { ERROR_CODE, ERROR_TEXT } from '../../constants/404Constants';
4 | import NotFound from './NotFound';
5 |
6 | describe('[NotFound Component Test Suite]', () => {
7 | test('should pass if "404" is found in the document', () => {
8 | render();
9 | expect(screen.getByText(ERROR_CODE)).toBeInTheDocument();
10 | });
11 |
12 | test('should pass if "PAGE NOT FOUND" is found in the document', () => {
13 | render();
14 | expect(screen.getByText(ERROR_TEXT)).toBeInTheDocument();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/constants/404Constants.js:
--------------------------------------------------------------------------------
1 | export const ERROR_CODE = '404';
2 | export const ERROR_TEXT = 'PAGE NOT FOUND';
3 |
--------------------------------------------------------------------------------
/src/constants/AppConstants.js:
--------------------------------------------------------------------------------
1 | export const INCREASE_COUNTER = 'Increase Counter';
2 | export const DECREASE_COUNTER = 'Decrease Counter';
3 |
4 | export const requestObject = {
5 | url: '',
6 | params: {},
7 | data: {},
8 | config: {},
9 | };
10 |
--------------------------------------------------------------------------------
/src/constants/CommonConstants.js:
--------------------------------------------------------------------------------
1 | // TBD
2 |
--------------------------------------------------------------------------------
/src/constants/HomeConstants.js:
--------------------------------------------------------------------------------
1 | export const WELCOME_HEADING = 'React Enterprise Starter Kit';
2 | export const IMG_ALT = 'React-Enterprise-Starter-Kit';
3 | export const REPO_URL = 'https://github.com/anandgupta193/react-enterprise-starter-kit';
4 | export const GITHUB_FORK_TEXT = 'Fork on Github';
5 | export const LICENCE_BADGE = 'https://img.shields.io/github/license/anandgupta193/react-enterprise-starter-kit?style=for-the-badge';
6 | export const STARS_BADGE = 'https://img.shields.io/github/stars/anandgupta193/react-enterprise-starter-kit?style=for-the-badge';
7 | export const FORK_BADGE = 'https://img.shields.io/github/forks/anandgupta193/react-enterprise-starter-kit?style=for-the-badge';
8 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | import React from 'react';
3 | import { createRoot } from 'react-dom/client';
4 | import { Provider } from 'react-redux';
5 | import Routes from './routes/Routes';
6 | import Store from './redux/store';
7 | import './index.css';
8 |
9 | const container = document.getElementById('root');
10 | const root = createRoot(container);
11 | root.render(
12 |
13 |
14 | ,
15 | );
16 |
17 | if (!__DEVELOPMENT__ && !__SERVER__) {
18 | const registerServiceWorker = () => {
19 | if ('serviceWorker' in navigator) {
20 | window.addEventListener('load', () => {
21 | navigator.serviceWorker.register('/service-worker.js').then((registration) => {
22 | console.log('SW registered: ', registration);
23 | }).catch((registrationError) => {
24 | console.log('SW registration failed: ', registrationError);
25 | });
26 | });
27 | }
28 | };
29 | registerServiceWorker();
30 | }
31 |
--------------------------------------------------------------------------------
/src/pure-components/Button/Button.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | const Button = ({ value }) => ;
5 |
6 | Button.defaultProps = {
7 | value: '',
8 | };
9 |
10 | Button.propTypes = {
11 | value: PropTypes.string,
12 | };
13 |
14 | export default Button;
15 |
--------------------------------------------------------------------------------
/src/pure-components/Button/Button.story.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // eslint-disable-next-line import/no-unresolved
3 | import Button from './Button';
4 |
5 | export default { title: 'Button' };
6 |
7 | export const withText = () => ;
8 |
9 | export const withEmoji = () => ;
10 |
--------------------------------------------------------------------------------
/src/pure-components/Button/Button.test.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import Button from './Button';
4 |
5 | describe('[Button Component Test Suite]', () => {
6 | test('should pass if button component renders successfully', () => {
7 | render();
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/src/reducers/CounterReducer.js:
--------------------------------------------------------------------------------
1 | import { INCREMENT_SUCCESS, DECREMENT_SUCCESS } from '../actionTypes/CounterActionTypes';
2 |
3 | const defaultState = 1;
4 |
5 | const counter = (state = defaultState, action) => {
6 | switch (action.type) {
7 | case INCREMENT_SUCCESS:
8 | return state + 1;
9 | case DECREMENT_SUCCESS:
10 | return state - 1;
11 | default:
12 | return state;
13 | }
14 | };
15 |
16 | export default counter;
17 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import counter from './CounterReducer';
3 |
4 | const rootReducer = combineReducers({
5 | counter,
6 | });
7 |
8 | export default rootReducer;
9 |
--------------------------------------------------------------------------------
/src/redux/store.js:
--------------------------------------------------------------------------------
1 | import createSagaMiddleware from 'redux-saga';
2 | import { createStore, applyMiddleware } from 'redux';
3 | import { composeWithDevTools } from 'redux-devtools-extension';
4 | import reducers from '../reducers';
5 | import rootSagas from '../sagas';
6 |
7 | const sagaMiddleware = createSagaMiddleware();
8 | let middleware = applyMiddleware(sagaMiddleware);
9 |
10 | // adding redux devtools for developmennt mode
11 | if (__DEVELOPMENT__) {
12 | middleware = composeWithDevTools(middleware);
13 | }
14 |
15 | // eslint-disable-next-line no-underscore-dangle
16 | const initialState = !__SERVER__ ? window.__PRELOADED__STATE__ : {};
17 |
18 | const store = createStore(reducers, initialState, middleware);
19 |
20 | if (__SERVER__) {
21 | store.runSaga = sagaMiddleware.run;
22 | } else {
23 | sagaMiddleware.run(rootSagas);
24 | }
25 |
26 | export default store;
27 |
--------------------------------------------------------------------------------
/src/routes/Routes.jsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from 'react';
2 | import { BrowserRouter, Routes, Route } from 'react-router-dom';
3 | import Loader from '../components/loader/Loader';
4 | import NotFound from './Routes.lazy';
5 | import App from '../components/App';
6 |
7 | const RoutePaths = () => (
8 | <>
9 |
10 | }>
11 |
12 | } end />
13 | } end />
14 |
15 |
16 |
17 | >
18 | );
19 | export default RoutePaths;
20 |
--------------------------------------------------------------------------------
/src/routes/Routes.lazy.jsx:
--------------------------------------------------------------------------------
1 | import { lazy } from 'react';
2 |
3 | const NotFound = lazy(() => import('../components/not-found/NotFound'));
4 |
5 | export default NotFound;
6 |
--------------------------------------------------------------------------------
/src/sagas/DecrCountSaga.js:
--------------------------------------------------------------------------------
1 | import { takeLatest, put } from 'redux-saga/effects';
2 | import { DECREMENT, DECREMENT_SUCCESS } from '../actionTypes/CounterActionTypes';
3 | // import RestClient from '../Utils/RestClient';
4 |
5 | function* decrementUserCountSaga() {
6 | // const requestObject = {
7 | // url: `base-url/${action.payload.queryParam}`,
8 | // };
9 | // const response = yield call(RestClient.get, requestObject);
10 | yield put({ type: DECREMENT_SUCCESS, payload: {} });
11 | }
12 |
13 | export default function* watchDecrementCountSaga() {
14 | yield takeLatest(DECREMENT, decrementUserCountSaga);
15 | }
16 |
--------------------------------------------------------------------------------
/src/sagas/IncrCountSaga.js:
--------------------------------------------------------------------------------
1 | import { takeLatest, put } from 'redux-saga/effects';
2 | import { INCREMENT, INCREMENT_SUCCESS } from '../actionTypes/CounterActionTypes';
3 | // import RestClient from '../Utils/RestClient';
4 |
5 | function* incrementUserCountSaga() {
6 | try {
7 | // const response = yield call(RestClient.get, requestObject);
8 | // console.log(response.data);
9 | } catch (error) {
10 | // console.log(error.message);
11 | }
12 | yield put({ type: INCREMENT_SUCCESS, payload: {} });
13 | }
14 |
15 | export default function* watchIncrementCountSaga() {
16 | yield takeLatest(INCREMENT, incrementUserCountSaga);
17 | }
18 |
--------------------------------------------------------------------------------
/src/sagas/index.js:
--------------------------------------------------------------------------------
1 | import { fork } from 'redux-saga/effects';
2 | import IncrCounterSaga from './IncrCountSaga';
3 | import DecrCounterSaga from './DecrCountSaga';
4 |
5 | // List of all sagas combined as root sagas
6 | export default function* rootSagas() {
7 | yield* [fork(IncrCounterSaga), fork(DecrCounterSaga)];
8 | }
9 |
--------------------------------------------------------------------------------
/src/server/server.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | import React from 'react';
3 | import path from 'path';
4 | import fs from 'fs';
5 | import express from 'express';
6 | import { renderToString } from 'react-dom/server';
7 | import { Provider } from 'react-redux';
8 | import store from '../redux/store';
9 | // import rootSagas from '../sagas';
10 | import App from '../components/App';
11 |
12 | const server = express();
13 |
14 | server.get('/', (req, res) => {
15 | console.log('Middle Ware Called');
16 | console.log(path.resolve('./dist/index.html'));
17 | const data = fs.readFileSync(path.resolve('./dist/index.html'), 'utf8');
18 | const content = renderToString(
19 |
20 |
21 | ,
22 | );
23 | const preloadedState = ``;
24 | const serverRenderedHTML = data.replace(
25 | '',
26 | `${content}
${preloadedState}`,
27 | );
28 |
29 | res.status(200).send(serverRenderedHTML);
30 | // store.runSaga(rootSagas).toPromise()
31 | // .then(() => {
32 | // console.log('Saga Run Successfully');
33 | // // res.status(200).send(serverRenderedHTML);
34 | // })
35 | // .catch((e) => {
36 | // console.log('Saga Run Failed', e.message);
37 | // // res.status(500).send(e.message);
38 | // });
39 | });
40 |
41 | // serving compressed version if production mode
42 | if (!__DEVELOPMENT__) {
43 | const encodeResToGzip = (contentType) => (req, res, next) => {
44 | req.url += '.gz';
45 | console.log(req.url);
46 | res.set('Content-Encoding', 'gzip');
47 | res.set('Content-Type', contentType);
48 | console.log(`Serving gzipped JS, ${req.url}`);
49 | next();
50 | };
51 | server.get('*.js', encodeResToGzip('text/javascript'));
52 | server.get('*.css', encodeResToGzip('text/css'));
53 | }
54 |
55 | server.use(express.static('./dist'));
56 |
57 | server.listen(3000, () => {
58 | console.log(`SSR running on port ${3000}`);
59 | });
60 |
--------------------------------------------------------------------------------
/src/service-worker.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-underscore-dangle */
2 | /* eslint-disable no-restricted-globals */
3 | import { precacheAndRoute } from 'workbox-precaching';
4 | // Your other import statements go here.
5 |
6 | precacheAndRoute(self.__WB_MANIFEST);
7 | // Your other SW code goes here.
8 |
--------------------------------------------------------------------------------
/src/tests/__mocks__/FileMock.js:
--------------------------------------------------------------------------------
1 | module.exports = 'test-file-stub';
2 |
--------------------------------------------------------------------------------
/src/tests/__mocks__/SetupTests.js:
--------------------------------------------------------------------------------
1 | // importing jest-dom to use some utility functions with expect assertion lib
2 | import '@testing-library/jest-dom';
3 |
--------------------------------------------------------------------------------
/src/utils/RestClient.js:
--------------------------------------------------------------------------------
1 | // TODO: Need to configure axios and interceptor
2 | import axios from 'axios';
3 |
4 | const RestClient = {
5 | get: (reqObj) => axios.get(reqObj.url, {
6 | params: reqObj.params,
7 | }),
8 | post: (reqObj) => axios.post(reqObj.url, reqObj.data, {
9 | config: reqObj.config,
10 | }),
11 | put: (reqObj) => axios.put(reqObj.url, reqObj.data, {
12 | config: reqObj.config,
13 | }),
14 | patch: (reqObj) => axios.patch(reqObj.url, reqObj.data, {
15 | config: reqObj.config,
16 | }),
17 | delete: (reqObj) => axios.delete(reqObj.url, {
18 | config: reqObj.config,
19 | }),
20 | };
21 |
22 | export default RestClient;
23 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['./src/**/*.{js,jsx}', './public/index.html'],
3 | theme: {
4 | extend: {
5 | colors: {
6 | // define all the base colors
7 | primary: '#1B73E8',
8 | },
9 | },
10 | },
11 | plugins: [],
12 | };
--------------------------------------------------------------------------------
/webpack/client/webpack.common.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
3 | const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const CopyPlugin = require('copy-webpack-plugin');
6 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
7 | const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
8 | const StylelintPlugin = require('stylelint-webpack-plugin');
9 |
10 | const env = process?.env?.NODE_ENV || 'development';
11 | const isDevEnv = env === 'development'
12 |
13 | const plugins = [
14 | new StylelintPlugin(),
15 | new CleanWebpackPlugin(),
16 | new WebpackManifestPlugin({
17 | fileName: 'asset-manifest.json',
18 | }),
19 | new HtmlWebpackPlugin({
20 | template: path.resolve(__dirname, '../../public/template.html'),
21 | filename: 'index.html'
22 | }),
23 | new MiniCssExtractPlugin({
24 | filename: isDevEnv ? 'css/[name].css' : 'css/[name].[contenthash].css',
25 | chunkFilename: isDevEnv ? 'css/[id].css' : 'css/[id].[contenthash].css',
26 | }),
27 | new CopyPlugin({
28 | patterns: [
29 | {
30 | from: path.resolve(__dirname, '../../public'),
31 | },
32 | ],
33 | }),
34 | ];
35 |
36 | module.exports = {
37 | entry: {
38 | app: [path.resolve(__dirname, '../../src/index.jsx')],
39 | },
40 | module: {
41 | rules: [
42 | {
43 | test: /\.(js|jsx)$/,
44 | exclude: /node_modules/,
45 | use: {
46 | loader: 'babel-loader',
47 | },
48 | },
49 | {
50 | test: /\.css$/i,
51 | use: [
52 | 'style-loader',
53 | 'css-loader',
54 | {
55 | loader: 'postcss-loader',
56 | options: {
57 | sourceMap: isDevEnv,
58 | },
59 | }
60 | ],
61 | },
62 | {
63 | test: /\.scss$/,
64 | use: [
65 | MiniCssExtractPlugin.loader,
66 | {
67 | loader: 'css-loader',
68 | options: {
69 | sourceMap: isDevEnv,
70 | modules: {
71 | mode: 'local',
72 | exportGlobals: true,
73 | localIdentName: isDevEnv ? '[name]__[local]' : '[hash:base64:5]',
74 | },
75 | },
76 | },
77 | {
78 | loader: 'postcss-loader',
79 | options: {
80 | sourceMap: isDevEnv,
81 | },
82 | },
83 | {
84 | loader: 'sass-loader',
85 | options: {
86 | sourceMap: isDevEnv,
87 | },
88 | },
89 | ],
90 | },
91 | {
92 | test: /\.(png|jpe?g|gif|webp|svg)$/i,
93 | loader: 'file-loader',
94 | options: {
95 | name: '[name].[ext]',
96 | outputPath: 'assets/images',
97 | },
98 | },
99 | ],
100 | },
101 | resolve: {
102 | extensions: ['.js', '.jsx'],
103 | },
104 | mode: env,
105 | plugins,
106 | optimization: {
107 | splitChunks: {
108 | name: 'vendor',
109 | chunks: 'all',
110 | minSize: 20000,
111 | maxAsyncRequests: 30,
112 | maxInitialRequests: 30,
113 | automaticNameDelimiter: '~',
114 | enforceSizeThreshold: 50000,
115 | maxSize: 250000,
116 | minChunks: 1,
117 | cacheGroups: {
118 | defaultVendors: {
119 | test: /[\\/]node_modules[\\/]/,
120 | priority: -10,
121 | filename: '[name].[contenthash].js',
122 | },
123 | },
124 | },
125 | moduleIds: 'deterministic',
126 | runtimeChunk: 'single',
127 | minimizer: [
128 | new CssMinimizerPlugin()
129 | ]
130 | },
131 | output: {
132 | filename: isDevEnv ? 'scripts/[name].js' : 'scripts/[name].[hash].js',
133 | path: path.resolve(__dirname, '../../dist'),
134 | chunkFilename: isDevEnv ? 'scripts/[name].js' : 'scripts/[name].[hash].js'
135 | },
136 | };
137 |
--------------------------------------------------------------------------------
/webpack/client/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const path = require('path');
3 | const Webpack = require('webpack');
4 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
5 | const common = require('./webpack.common.config');
6 |
7 | const plugins = [
8 | new Webpack.DefinePlugin({
9 | __CLIENT__: true,
10 | __SERVER__: false,
11 | __DEVELOPMENT__: true,
12 | __DEVTOOLS__: true,
13 | }),
14 | ];
15 |
16 | if (process.env.analyze) {
17 | plugins.push(
18 | new BundleAnalyzerPlugin({
19 | openAnalyzer: true,
20 | reportFilename: 'bundleReport.html',
21 | analyzerMode: 'static',
22 | token: 'c3e980d3ec23ddd626fecc110501e76a9a469461',
23 | }),
24 | );
25 | }
26 |
27 | module.exports = merge(common, {
28 | devtool: 'inline-source-map',
29 | devServer: {
30 | static: {
31 | directory: path.join(__dirname, '../../dist'),
32 | },
33 | port: process?.env?.PORT || 3000,
34 | hot: true,
35 | },
36 | plugins,
37 | });
38 |
--------------------------------------------------------------------------------
/webpack/client/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { merge } = require('webpack-merge');
3 | const Webpack = require('webpack');
4 | const CompressionPlugin = require('compression-webpack-plugin');
5 | const { InjectManifest } = require('workbox-webpack-plugin');
6 | const common = require('./webpack.common.config');
7 |
8 | const plugins = [
9 |
10 | new CompressionPlugin({
11 | filename: '[path][base].gz',
12 | algorithm: 'gzip',
13 | test: /\.(js|jsx)$|\.css$|\.html$/,
14 | }),
15 | new Webpack.DefinePlugin({
16 | __CLIENT__: true,
17 | __SERVER__: false,
18 | __DEVELOPMENT__: false,
19 | __DEVTOOLS__: false, // For disabling redux devtools on Production mode
20 | }),
21 | new InjectManifest({
22 | swSrc: path.resolve(__dirname, '../../src/service-worker.js'),
23 | maximumFileSizeToCacheInBytes: 5000000,
24 | }),
25 | ];
26 |
27 | module.exports = merge(common, {
28 | plugins,
29 | });
30 |
--------------------------------------------------------------------------------
/webpack/server/webpack.common.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
4 | const nodeExternals = require('webpack-node-externals');
5 |
6 | const env = process.env.NODE_ENV;
7 | const isDevEnv = env === 'development';
8 |
9 | const plugins = [
10 | new MiniCssExtractPlugin({
11 | filename: isDevEnv ? 'css/[name].css' : 'css/[name].[contenthash].css',
12 | chunkFilename: isDevEnv ? 'css/[id].css' : 'css/[id].[contenthash].css',
13 | }),
14 | new CleanWebpackPlugin(),
15 | ];
16 |
17 | module.exports = {
18 | entry: {
19 | server: [path.resolve(__dirname, '../../src/server/server.jsx')],
20 | },
21 | module: {
22 | rules: [
23 | {
24 | test: /\.(js|jsx)$/,
25 | exclude: /node_modules/,
26 | use: {
27 | loader: 'babel-loader',
28 | },
29 | },
30 | {
31 | test: /\.css$/i,
32 | use: [
33 | 'style-loader',
34 | 'css-loader',
35 | {
36 | loader: 'postcss-loader',
37 | options: {
38 | sourceMap: isDevEnv,
39 | },
40 | }
41 | ],
42 | },
43 | {
44 | test: /\.scss$/,
45 | use: [
46 | MiniCssExtractPlugin.loader,
47 | {
48 | loader: 'css-loader',
49 | options: {
50 | sourceMap: isDevEnv,
51 | modules: {
52 | mode: 'local',
53 | exportGlobals: true,
54 | localIdentName: isDevEnv ? '[name]__[local]' : '[hash:base64:5]',
55 | },
56 | },
57 | },
58 | {
59 | loader: 'postcss-loader',
60 | options: {
61 | sourceMap: isDevEnv,
62 | },
63 | },
64 | {
65 | loader: 'sass-loader',
66 | options: {
67 | sourceMap: isDevEnv,
68 | },
69 | },
70 | ],
71 | },
72 | {
73 | test: /\.(png|jpe?g|gif|webp|svg)$/i,
74 | loader: 'file-loader',
75 | options: {
76 | name: '[name].[ext]',
77 | outputPath: 'assets/images',
78 | },
79 | },
80 | ],
81 | },
82 | resolve: {
83 | extensions: ['.js', '.jsx'],
84 | },
85 | mode: process.env.NODE_ENV,
86 | plugins,
87 | externals: [nodeExternals()],
88 | target: 'node',
89 | output: {
90 | filename: '[name].js',
91 | path: path.resolve(__dirname, '../../server-build'),
92 | },
93 | };
94 |
--------------------------------------------------------------------------------
/webpack/server/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const Webpack = require('webpack');
3 | const common = require('./webpack.common.config');
4 |
5 | const plugins = [
6 | new Webpack.DefinePlugin({
7 | __CLIENT__: false,
8 | __SERVER__: true,
9 | __DEVELOPMENT__: true,
10 | __DEVTOOLS__: true,
11 | }),
12 | ];
13 |
14 | module.exports = merge(common, {
15 | plugins,
16 | });
17 |
--------------------------------------------------------------------------------
/webpack/server/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const Webpack = require('webpack');
3 | const common = require('./webpack.common.config');
4 |
5 | const plugins = [
6 |
7 | new Webpack.DefinePlugin({
8 | __CLIENT__: false,
9 | __SERVER__: true,
10 | __DEVELOPMENT__: false,
11 | __DEVTOOLS__: false, // For disabling redux devtools on Production mode
12 | }),
13 | ];
14 |
15 | module.exports = merge(common, {
16 | plugins,
17 | });
18 |
--------------------------------------------------------------------------------