This is my solution to Coodesh Front-End Challenge 2021. A TypeScript/React application. Built with Babel and Webpack. From a template wich is from my athoring. With a lot of configurations for development and production environments.
-
Recommended IDE VS Code
- How to run this app
- Node Package Manager
- Node Version Manager
- Install packages
- Run on Development
- Build Production
- Run Tests
- Linting
- Built with
- Node Package Manager
- Node Version Manager
- Node
- React
- Typescript
- Webpack
- Babel
- SASS
- React Router DOM -router-dom
- Axios
- ES Lint
- Jest
- Husky
- Folder structure
- Atomic Design
- Adapting from Atomic Design
- Suggested folder structure
- Root and src folders
- Tests folder
- Questions and answers
- Why TypeScript
- What is Webpack
- What is Babel
- What is ES Lint
- What is Prettier
- What is Stylelint
- What is .editorconfig
- Why SASS
- Why index.ejs
- Why
__tests__
folder - Why Husky
- Troubleshooting
- To do
- Recommended IDE extensions
- Recommended VS CODE settings.json
In order to install packages and run this application, you will need Node Package Manager v6.14.15 or higher
This application is running on Node version 14.18.0 LTS
- To change the Node version on IDE, open a new terminal and:
nvm use
- If you don't have the LTS version, install it with Node Version Manager:
nvm install --lts
- To set the LTS version as default on your terminal:
nvm alias default v14.18.0
- To check if the Node versions is the correct:
node --version
And you should see v14.18.0
To install all the node_modules packages:
npm install
To run on Development mode:
npm start
Build production mode:
npm run build
Build and Run within a ๐ Docker Container:
sudo docker build --tag react . && sudo docker run --publish 5010:5010 react
To run tests:
npm test
Tests coverage:
npm run test:coverage
To run tests on watch mode:
npm test --watchAll <fileName>
SCSS files:
npx stylelint **/*.scss
styles.ts files:
npx stylelint **/*.styles.ts
TypeScript files:
npm run eslint --ext .tsx,.ts src
All files:
npm run lint
Husky is already configured and commited. All you have to do is commit your changes.
Check the docs for more details Husky if you need to install it locally.
package.json
"husky": {
"hooks": {
"pre-commit": "npm lint",
"pre-push": "npm test"
}
},
-
- How to install
-
-
How to install with Webpack
-
Useful sources:
-
-
-
How to install
-
How to init tesconfig
-
Useful sources:
- Typescript for new programmers
- Typescript for Javascript programmers
- Typescript handbook
- Typescript declaration files
- Typescript projects configurations
- Typescript from zero to hero - PT-BR ๐ง๐ท
-
@Types and Plugins:
-
-
-
How to install
-
Webpack getting started
-
Webpack asset management
-
Webpack output management
-
Webpack loaders
-
Webpack Typescript
-
Webpack concepts
-
Modules:
- webpack
- webpack-cli command line interface
- webpack-dev-server see also devServer
- webpack-merge
-
Plugins:
-
Officials:
- html-webpack-plugin basic for HTML files
- mini-css-extract-plugin extracts CSS into separate files
- compression-webpack-plugin compressed version of assets
- terser-webpack-plugin minify Javascript
-
Third Parties:
-
-
Loaders:
-
Transpiling:
-
Styling:
-
-
-
-
Required for Webpack:
-
-
To import Bootstrap SCSS and normalize CSS
/App.scss
@import '~bootstrap/scss/bootstrap';
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
height: 100vh;
width: 100vw;
}
...
/Routes.tsx
import React from 'react'
import { Route, Switch } from 'react-router-dom'
const HomePage = React.lazy(() => import('@pages/HomePage'))
const Routes: React.FC = () => {
return (
<Switch>
<Route exact path="/">
<React.Suspense fallback={<div>Carregando...</div>}>
<HomePage />
</React.Suspense>
</Route>
</Switch>
)
}
export default Routes
- /App.tsx
import { ConnectedRouter } from 'connected-react-router'
import React from 'react'
import { Provider as ReduxStoreProvider } from 'react-redux'
import styled, { ThemeProvider } from 'styled-components'
import goodContrastTheme from '@common/themes/goodContrastTheme'
import Routes from './Routes'
import store, { history } from './store'
import './App.scss'
const App: React.FC = () => {
return (
<ReduxStoreProvider store={store}>
<AppProviders>
<Routes />
</AppProviders>
</ReduxStoreProvider>
)
}
export default App
export const RootThemeWrapper = styled.div`
background: ${({ theme }) => theme.colors.background.light};
color: ${({ theme }) => theme.colors.text.light};
`
export const AppProviders: React.FC = ({ children }) => {
return (
<ThemeProvider theme={goodContrastTheme}>
<ConnectedRouter history={history}>
<RootThemeWrapper>{children}</RootThemeWrapper>
</ConnectedRouter>
</ThemeProvider>
)
}
/src/services/api/getQuote.ts
import axios, { AxiosResponse } from 'axios'
export const getQuote = async (): Promise<AxiosResponse<never>> => {
const url = 'https://type.fit/api/quotes'
const result = await axios({
url,
method: 'GET',
headers: {
'Content-type': 'application/json',
},
})
return result
}
-
-
Required for Jest with TypeScript and Webpack:
-
Useful sources:
- Tutorial Jest for React
- Jest Webpack Guide
-
-
-
Required for Redux with TypeScript and React Router DOM:
-
Useful sources:
- React Router integration with Redux
- Redux Thunk typing actions, store, reducers, and thunk
- Redux DevTools Chrome Extension
-
Atomic Design โข๏ธ
โโโ atoms/
โ
โโโ molecules/
โ
โโโ organisms/
โ
โโโ templates/
โ
โโโ pages/
src/
โโโ components/
โ โโโ atoms/
โ โโโ molecules/
โ โโโ organisms/
โ
โโโ layouts|templates/
โ
โโโ pages/
For components, layouts, and pages
โโโ HeaderComponent/
โ โโโ HeaderComponent.tsx
โ โโโ index.(scss|styles.ts)
โ โโโ index.ts
...
โโโ HomeLayout/
โ โโโ HomeLayout.tsx
โ โโโ index.(scss|styles.ts)
โ โโโ index.ts
...
โโโ HomePage/
โ โโโ HomePage.tsx
โ โโโ index.(scss|styles.ts)
โ โโโ index.ts
/
โโโ .husky/ <!-- generated afer run `npx husky install` -->
โโโ build/ <!-- generated afer run `npm start` -->
โโโ dist/ <!-- generated afer run `npm build` -->
โโโ node_modules/ <!-- generated afer run `npm install` -->
โโโ public/
โโโ src/
โ โโโ __tests__/
โ โโโ common/
โ โโโ components/
โ โโโ layouts/
โ โโโ pages/
โ โโโ services/
โ โโโ App.scss
โ โโโ App.tsx
โ โโโ index.ejs
โ โโโ index.tsx
โ โโโ Routes.tsx
โโโ .editorconfig
โโโ .eslintignore
โโโ .eslintrc.json
โโโ .gitignore
โโโ .node-version
โโโ .nvmrc
โโโ .prettierignore
โโโ .prettierrc
โโโ .stylelintignore
โโโ .stylelintrc.json
โโโ babel.config.js
โโโ docker-compose.yml
โโโ Dockerfile
โโโ package-lock.json
โโโ package.json
โโโ postcss.config.js
โโโ README.md
โโโ tsconfig.json
โโโ webpack.config.js
โโโ webpack.development.js
โโโ webpack.production.js
For components, layouts, and pages
pages/
โโโ HomePage/
โ โโโ HomePage.tsx
โ โโโ index.(scss|styles.ts)
โ โโโ index.ts
...
__tests__/
โโโ app/
โโโ coverage/ <!-- generated afer run `npm test` -->
โโโ helpers/
โโโ mocks/
__tests__/app/
โโโ components/
โโโ layouts/
โโโ pages/
__tests__/app/components/
โโโ atoms/
โโโ molecules/
โโโ organisms/
__tests__/app/components/atoms/InnerLink.(test|spec).tsx
__tests__/app/components/atoms/ListItem.(test|spec).tsx
__tests__/app/components/atoms/OuterLink.(test|spec).tsx
__tests__/app/components/molecules/TechList.(test|spec).tsx
__tests__/app/components/organisms/HomeNav.(test|spec).tsx
__tests__/app/layouts/HomeMain.(test|spec).tsx
__tests__/app/pages/HomePage.(test|spec).tsx
- Improves a lot the development experience. Has a lot of features that JavaScript does not have. Having a strong sinergy with Webpack, Babel, and ES Lint. Types makes the code basis easier to read and mantain. And it also boosts your knowledge about React and JavaScript. Since we have to care about every type of JSX Elements, DOM Events, functions, objects, and so on.
- Customize our projects configurations connecting plugins with Webpack, Babel, and ES Lint. Imports management, folder and file paths alias, and
others environments options. Are all available on
tsconfig.json
file.
- Webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph from one or more entry points and then combines every module your project needs into one or more bundles, which are static assets to serve your content from. Concepts
- Babel is a JavaScript compiler
- See also don-t-use-create-react-app-how-you-can-set-up-your-own-reactjs-boilerplate
- ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code, with the goal of making code more consistent and avoiding bugs.
- Ensures that every person that is working on this code basis is writing on the same pattern. Following the same linting rules and avoiding anti-patterns from being commited and pushed. Combined with Husky.
- See also eslint-plugin
- An opinionated code formatter. You press save and code is formatted.
- Need to install Prettier extension for yor IDE in order to automate foarmat on save and/or format on paste options.
- Same as ES Lint but for styles files like .scss, .css, .less, etc.
- To ensure all environments that are running this application are also running the same Node version.
- EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.
- CSS is hard to maintain. SASS is more porwerfull ๐ฅ. And there are plenty of UI libraries that supports SASS.
- UI libraries that supports SASS:
- Webpack relative or absolute path to the template. By default it will use src/index.ejs if it exists. Please see the docs for details.
- EJS is just a way to warite some JS on HTML. Since we need to capture values from HtmlWebpackPlugin.
- See html-webpack-plugin
- See also template-option
- The main idea here is to keep all files related to tests separated from others
src
folder files. By doing this, we prevent Webpack to load files with .test and .spec extensions. And other files that are only useful on test environment. Like mocks, Provider and jest-setup from utils. coverage
is a special folder that comes in when any test is runned. This is an automatic configuration placed onjest.config.js
on keycoverageDirectory
- Unit tests will be applied to components, layouts, and pages.
- End-to-End tests will be runned with Cypress further.
- Husky improves your commits and more ๐ถ woof!
- To avoid having unlinted code in our repository, what we can do is add ESLint at one point of our process using Git Hooks. Like Husky!
-
Supposing this application will scale and grow larger, Redux is more recommended.
-
Even though we have to write more code, Redux is easier to understand. Because it organises better the state management.
-
A lot of people know how to use Redux. Specially if this person cames from another language/framework. Context is only for React.
-
Context is a very rencent API. Only full available from React 16.8 to above.
-
Useful resourcers:
- Common is a place for
assets
,functions
,hooks
,themes
,types
, and wahtever you may need to share with the rest of the application. - Is also useful to place third party libraries configurations and setups. Keeping it on a place where you know where to find.
- Good place for reusable code. In order to keep the application simple and dry. And reducing responsabilities from components.
- On deploy, you must set build production folder to
dist/
instead of/
.
- To Do
settings.json
{
"auto-close-tag.activationOnLanguage": ["*"],
"code-runner.clearPreviousOutput": true,
"code-runner.runInTerminal": true,
"editor.autoClosingBrackets": "always",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
},
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.suggestSelection": "first",
"editor.tabCompletion": "on",
"editor.tabSize": 4,
"emmet.showSuggestionsAsSnippets": true,
"emmet.includeLanguages": {
"javascript": "javascriptreact",
"ejs": "html"
},
"eslint.useESLintClass": true,
"eslint.workingDirectories": ["./"],
"files.exclude": {
"**/.classpath": true,
"**/.project": true,
"**/.settings": true,
"**/.factorypath": true
},
"files.associations": {
"**/*.html": "html",
"*.md": "mdx"
},
"git.autoRepositoryDetection": "openEditors",
"gitlens.gitCommands.search.showResultsInSideBar": true,
"gitlens.hovers.annotations.over": "annotation",
"gitlens.hovers.annotations.changes": false,
"gitlens.hovers.annotations.details": false,
"gitlens.hovers.autolinks.enabled": false,
"gitlens.hovers.autolinks.enhanced": false,
"gitlens.hovers.avatars": false,
"gitlens.hovers.currentLine.changes": false,
"gitlens.hovers.currentLine.enabled": false,
"gitlens.hovers.currentLine.details": false,
"gitlens.hovers.pullRequests.enabled": false,
"importCost.smallPackageSize": 10,
"importCost.mediumPackageSize": 20,
"importCost.mediumPackageColor": "#CCBC00",
"javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": true,
"javascript.updateImportsOnFileMove.enabled": "always",
"liveServer.settings.donotVerifyTags": true,
"liveServer.settings.donotShowInfoMsg": true,
"material-icon-theme.folders.associations": {
"widgets": "components",
"front-angular": "font",
"front-react": "React-Components",
"front-vue": "vue",
"store": "Redux-store",
"actions": "redux-actions",
"reducers": "redux-reducer",
"atoms": "React-components",
"molecules": "Node",
"organisms": "Other",
"favicons": "Admin",
"modules": "Home",
"http": "Global",
"typeorm": "Database",
"migrations": "Expo",
"entities": "Class",
"repositories": "Log",
"requirements": "Rules"
},
"material-icon-theme.files.associations": {
"ormconfig.json": "Database"
},
"stylelint.validate": ["css", "scss"],
"typescript.updateImportsOnFileMove.enabled": "always",
"vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue",
"workbench.colorTheme": "Dracula",
"workbench.iconTheme": "material-icon-theme",
"workbench.tree.indent": 20,
"window.zoomLevel": 0,
}