├── .babelrc ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── build-and-test.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .stylelintrc ├── README.md ├── Technologies.png ├── app ├── api │ └── RestClient.ts ├── components │ ├── elements │ │ └── Icon │ │ │ └── Icon.tsx │ ├── layouts │ │ └── Default │ │ │ ├── Default.module.scss │ │ │ └── Default.tsx │ ├── modules │ │ └── Header │ │ │ ├── Header.module.scss │ │ │ ├── Header.test.tsx │ │ │ ├── Header.tsx │ │ │ └── __snapshots__ │ │ │ └── Header.test.tsx.snap │ └── templates │ │ └── HomePage │ │ ├── HomePage.module.scss │ │ └── HomePage.tsx ├── constants │ └── .gitkeep ├── context │ └── .gitkeep ├── hooks │ └── .gitkeep ├── styles │ ├── abstracts │ │ ├── _functions.scss │ │ ├── _mixins.scss │ │ └── _variables.scss │ ├── app.scss │ └── base │ │ ├── _base.scss │ │ └── _typography.scss ├── types │ ├── api │ │ └── .gitkeep │ ├── context │ │ └── .gitkeep │ └── elements │ │ └── .gitkeep └── utils │ └── .gitkeep ├── cypress.json ├── jest.config.ts ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── 404.tsx ├── _app.tsx ├── _document.tsx ├── _error.tsx └── index.tsx ├── public ├── favicon.ico ├── images │ └── post.jpg └── robots.txt ├── setupTests.ts ├── test ├── fixtures │ └── .gitkeep ├── spec │ ├── e2e │ │ └── SocialMedia.ts │ └── integration │ │ └── .gitkeep └── support │ ├── commands │ └── getElement.ts │ └── index.ts ├── tsconfig.json └── typings.ts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | typings.ts 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint"], 4 | "extends": [ 5 | "next", 6 | "eslint:recommended", 7 | "plugin:@typescript-eslint/eslint-recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "plugin:react/recommended" 10 | ], 11 | "rules": { 12 | "react/prop-types": 0, 13 | "react/react-in-jsx-scope": 0 14 | }, 15 | "env": { 16 | "jest": true, 17 | "browser": true, 18 | "amd": true, 19 | "node": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | buildAndTest: 7 | name: Build and test 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout source code 12 | uses: actions/checkout@v2 13 | 14 | - name: Use Node.js 12.x 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: 12.x 18 | 19 | - name: Set timezone to Zurich 20 | run: sudo timedatectl set-timezone Europe/Zurich 21 | 22 | - uses: actions/cache@v2 23 | with: 24 | path: ~/.npm 25 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 26 | restore-keys: | 27 | ${{ runner.os }}-node- 28 | 29 | - name: Run npm install 30 | run: npm install 31 | 32 | - name: Run linter 33 | run: npm run lint 34 | 35 | - name: Run Typescript check 36 | run: npm run type:check 37 | 38 | - name: Run unit tests 39 | run: npm run test:unit 40 | 41 | - name: Run integration tests 42 | uses: cypress-io/github-action@v1 43 | with: 44 | build: npm run build 45 | start: npm run start 46 | wait-on: 'http://localhost:3000' 47 | browser: chrome 48 | headless: true 49 | spec: test/integration/**/* 50 | 51 | - name: Run e2e tests 52 | uses: cypress-io/github-action@v1 53 | with: 54 | build: npm run build 55 | start: npm run start 56 | wait-on: 'http://localhost:3000' 57 | browser: chrome 58 | headless: true 59 | spec: test/e2e/**/* 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | /.idea 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Always-ignore extensions 2 | *.diff 3 | *.err 4 | *.orig 5 | *.log 6 | *.rej 7 | *.swo 8 | *.swp 9 | *.zip 10 | *.vi 11 | *~ 12 | *.sass-cache 13 | *.ruby-version 14 | *.ini 15 | *.exe 16 | *.iml 17 | *.md 18 | *.txt 19 | *.snap 20 | 21 | # OS / Editors 22 | .DS_Store 23 | ._* 24 | Thumbs.db 25 | .cache 26 | .project 27 | .settings 28 | .tmproj 29 | *.esproj 30 | nbproject 31 | *.sublime-project 32 | *.sublime-workspace 33 | .vscode 34 | 35 | # Project specific 36 | .idea 37 | node_modules 38 | .next 39 | .tmp 40 | .sass-cache 41 | package-lock.json 42 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "printWidth": 80 7 | } 8 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-idiomatic-order" 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js Boilerplate 2 | Feel free to use this Boilerplate as a starting point for your own Next.js Projects. 3 | 4 | ## 👶 Getting Started 5 | Here you will find all the available npm commands. 6 | 7 | ### 👨‍💻 Develop 8 | Use this command to develop local. 9 | 10 | ``` 11 | npm run dev 12 | ``` 13 | 14 | ### 🏗 Build 15 | This will create an optimized production build. 16 | 17 | ``` 18 | npm run build 19 | ``` 20 | 21 | ### 🏃Run 22 | Use this command to run your Application on the Server. 23 | 24 | ``` 25 | npm run start 26 | ``` 27 | 28 | ### 🔎 Well structured code 29 | You can run one of the following commands to lint your code. 30 | 31 | ``` 32 | npm run lint // JS & CSS 33 | npm run lint:scss 34 | npm run lint:js 35 | ``` 36 | 37 | ### 🪲 Test 38 | Run this command to execute your jest tests. 39 | 40 | ``` 41 | npm run test:unit 42 | npm run test:unit:update // Update Snapshots 43 | ``` 44 | 45 | Run one of these commands to run your integration & e2e tests. 46 | 47 | ``` 48 | npm run cypress // Command Line 49 | npm run cypress:open // GUI 50 | ``` 51 | 52 | ### 🥷 Typescript 53 | To verify your static typing run the following command. 54 | 55 | ``` 56 | npm run type:check 57 | ``` 58 | 59 | ## 🧱 Tech Stack 60 | ![Technologies](Technologies.png) 61 | 62 | -------------------------------------------------------------------------------- /Technologies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/Technologies.png -------------------------------------------------------------------------------- /app/api/RestClient.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; 2 | 3 | axios.defaults.headers.common.Accept = 'application/json'; 4 | axios.defaults.timeout = 12000; 5 | 6 | const getHttpHeaders = (isAuthenticated = false): AxiosRequestConfig => { 7 | // Add your custom logic here, for example add a Token to the Headers 8 | if (isAuthenticated) { 9 | return { 10 | headers: { 11 | Authorization: 'Bearer YOUR_TOKEN', 12 | }, 13 | }; 14 | } 15 | 16 | return {}; 17 | }; 18 | 19 | const get = (path: string): Promise => axios.get(path, getHttpHeaders()); 20 | 21 | const del = (path: string): Promise => axios.delete(path, getHttpHeaders()); 22 | 23 | const post = (path: string, data: any): Promise => axios.post(path, data, getHttpHeaders()); 24 | 25 | const put = (path: string, data: any): Promise => axios.post(path, data, getHttpHeaders()); 26 | 27 | const patch = (path: string, data: any): Promise => axios.post(path, data, getHttpHeaders()); 28 | 29 | export default { 30 | get, 31 | del, 32 | post, 33 | put, 34 | patch, 35 | }; 36 | -------------------------------------------------------------------------------- /app/components/elements/Icon/Icon.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | export enum Icons { 3 | GITHUB = 'github', 4 | MEDIUM = 'medium', 5 | TWITTER = 'twitter', 6 | } 7 | 8 | const icons = { 9 | github: ( 10 | 11 | ), 12 | medium: ( 13 | 14 | ), 15 | twitter: ( 16 | <> 17 | 18 | 22 | 23 | ), 24 | }; 25 | 26 | type Props = { 27 | icon: Icons; 28 | alt?: string; 29 | }; 30 | 31 | const Icon = ({ icon, alt }: Props) => ( 32 | 39 | {alt && {alt}} 40 | {icons[icon]} 41 | 42 | ); 43 | 44 | export default Icon; 45 | -------------------------------------------------------------------------------- /app/components/layouts/Default/Default.module.scss: -------------------------------------------------------------------------------- 1 | @import 'app.scss'; 2 | 3 | .default { 4 | display: grid; 5 | min-height: 100vh; 6 | grid-template-rows: 14rem 1fr; 7 | } 8 | 9 | .content { 10 | display: grid; 11 | margin: 0 auto; 12 | grid-column-gap: $grid-gutter-width; 13 | grid-template-columns: repeat($grid-columns, 1fr); 14 | @include content-width; 15 | } 16 | -------------------------------------------------------------------------------- /app/components/layouts/Default/Default.tsx: -------------------------------------------------------------------------------- 1 | import Header from '@module/Header/Header'; 2 | import styles from './Default.module.scss'; 3 | 4 | type Props = { 5 | children: React.ReactNode; 6 | }; 7 | 8 | const Default = ({ children }: Props) => ( 9 |
10 |
11 |
{children}
12 |
13 | ); 14 | 15 | export default Default; 16 | -------------------------------------------------------------------------------- /app/components/modules/Header/Header.module.scss: -------------------------------------------------------------------------------- 1 | @import 'app.scss'; 2 | 3 | .header { 4 | display: flex; 5 | width: 100%; 6 | align-items: center; 7 | justify-content: center; 8 | } 9 | 10 | .socials { 11 | display: flex; 12 | 13 | a { 14 | color: $color-black; 15 | text-decoration: none; 16 | transition: transform 0.2s ease-out; 17 | 18 | &:hover { 19 | transform: translateY(-4px); 20 | transition: transform 0.2s ease-in; 21 | } 22 | 23 | &:not(:last-child) { 24 | margin-right: 2.5rem; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/components/modules/Header/Header.test.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | import Header from './Header'; 3 | 4 | describe('Module Header', () => { 5 | it('should render component', () => { 6 | const header = render(
); 7 | expect(header).toMatchSnapshot(); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /app/components/modules/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import Icon, { Icons } from '@element/Icon/Icon'; 2 | import styles from './Header.module.scss'; 3 | 4 | const Header = () => ( 5 |
6 | 7 | 12 | 13 | 14 | 21 | 22 | 23 | 28 | 29 | 30 | 31 |
32 | ); 33 | 34 | export default Header; 35 | -------------------------------------------------------------------------------- /app/components/modules/Header/__snapshots__/Header.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Module Header should render component 1`] = ` 4 | Object { 5 | "asFragment": [Function], 6 | "baseElement": 7 | 84 | , 85 | "container": , 162 | "debug": [Function], 163 | "findAllByAltText": [Function], 164 | "findAllByDisplayValue": [Function], 165 | "findAllByLabelText": [Function], 166 | "findAllByPlaceholderText": [Function], 167 | "findAllByRole": [Function], 168 | "findAllByTestId": [Function], 169 | "findAllByText": [Function], 170 | "findAllByTitle": [Function], 171 | "findByAltText": [Function], 172 | "findByDisplayValue": [Function], 173 | "findByLabelText": [Function], 174 | "findByPlaceholderText": [Function], 175 | "findByRole": [Function], 176 | "findByTestId": [Function], 177 | "findByText": [Function], 178 | "findByTitle": [Function], 179 | "getAllByAltText": [Function], 180 | "getAllByDisplayValue": [Function], 181 | "getAllByLabelText": [Function], 182 | "getAllByPlaceholderText": [Function], 183 | "getAllByRole": [Function], 184 | "getAllByTestId": [Function], 185 | "getAllByText": [Function], 186 | "getAllByTitle": [Function], 187 | "getByAltText": [Function], 188 | "getByDisplayValue": [Function], 189 | "getByLabelText": [Function], 190 | "getByPlaceholderText": [Function], 191 | "getByRole": [Function], 192 | "getByTestId": [Function], 193 | "getByText": [Function], 194 | "getByTitle": [Function], 195 | "queryAllByAltText": [Function], 196 | "queryAllByDisplayValue": [Function], 197 | "queryAllByLabelText": [Function], 198 | "queryAllByPlaceholderText": [Function], 199 | "queryAllByRole": [Function], 200 | "queryAllByTestId": [Function], 201 | "queryAllByText": [Function], 202 | "queryAllByTitle": [Function], 203 | "queryByAltText": [Function], 204 | "queryByDisplayValue": [Function], 205 | "queryByLabelText": [Function], 206 | "queryByPlaceholderText": [Function], 207 | "queryByRole": [Function], 208 | "queryByTestId": [Function], 209 | "queryByText": [Function], 210 | "queryByTitle": [Function], 211 | "rerender": [Function], 212 | "unmount": [Function], 213 | } 214 | `; 215 | -------------------------------------------------------------------------------- /app/components/templates/HomePage/HomePage.module.scss: -------------------------------------------------------------------------------- 1 | @import 'app.scss'; 2 | 3 | .content { 4 | grid-column: span 12; 5 | text-align: center; 6 | 7 | @include breakpoint('md') { 8 | grid-column: 3 / 11; 9 | } 10 | 11 | @include breakpoint('xl') { 12 | grid-column: 4 / 10; 13 | } 14 | } 15 | 16 | .author { 17 | font-size: 2.2rem; 18 | font-weight: lighter; 19 | letter-spacing: .05rem; 20 | text-transform: lowercase; 21 | 22 | a { 23 | color: $color-black; 24 | font-style: italic; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/components/templates/HomePage/HomePage.tsx: -------------------------------------------------------------------------------- 1 | import styles from './HomePage.module.scss'; 2 | 3 | const HomePage = () => ( 4 |
5 | 6 | Made by{' '} 7 | 8 | yannickwittwer.dev 9 | 10 | 11 |
12 | ); 13 | 14 | export default HomePage; 15 | -------------------------------------------------------------------------------- /app/constants/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/app/constants/.gitkeep -------------------------------------------------------------------------------- /app/context/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/app/context/.gitkeep -------------------------------------------------------------------------------- /app/hooks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/app/hooks/.gitkeep -------------------------------------------------------------------------------- /app/styles/abstracts/_functions.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/app/styles/abstracts/_functions.scss -------------------------------------------------------------------------------- /app/styles/abstracts/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin breakpoint($breakpoint) { 2 | @if map-has-key($breakpoints, $breakpoint) { 3 | @media (min-width: #{map-get($breakpoints, $breakpoint)}) { 4 | @content; 5 | } 6 | } @else { 7 | @warn 'Unfortunately, no value could be retrieved from `#{$breakpoint}`. ' 8 | + 'Please make sure it is defined in `$grid-breakpoints` map.'; 9 | } 10 | } 11 | 12 | @mixin content-width { 13 | width: 100%; 14 | max-width: map_get($container-max-widths, xs); 15 | padding: 0 1rem; 16 | 17 | @include breakpoint('sm') { 18 | max-width: map_get($container-max-widths, sm); 19 | padding: 0; 20 | } 21 | 22 | @include breakpoint('md') { 23 | max-width: map_get($container-max-widths, md); 24 | } 25 | 26 | @include breakpoint('lg') { 27 | max-width: map_get($container-max-widths, lg); 28 | } 29 | 30 | @include breakpoint('xl') { 31 | max-width: map_get($container-max-widths, xl); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/styles/abstracts/_variables.scss: -------------------------------------------------------------------------------- 1 | $color-primary: #faa916; 2 | $color-black: #050517; 3 | $color-white: #fff; 4 | 5 | $grid-columns: 12; 6 | $grid-gutter-width: 3rem; 7 | 8 | $breakpoints: ( 9 | xs: 0, 10 | sm: 576px, 11 | md: 768px, 12 | lg: 992px, 13 | xl: 1200px, 14 | ); 15 | 16 | $container-max-widths: ( 17 | sm: 540px, 18 | md: 720px, 19 | lg: 960px, 20 | xl: 1140px, 21 | ); 22 | -------------------------------------------------------------------------------- /app/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import 'abstracts/variables'; 2 | @import 'abstracts/mixins'; 3 | @import 'abstracts/functions'; 4 | 5 | @import 'base/typography'; 6 | -------------------------------------------------------------------------------- /app/styles/base/_base.scss: -------------------------------------------------------------------------------- 1 | *, 2 | *:after, 3 | *:before { 4 | box-sizing: inherit; 5 | padding: 0; 6 | margin: 0; 7 | } 8 | 9 | html { 10 | // This defines what 1rem is 11 | font-size: 62.5%; 12 | } 13 | 14 | body { 15 | box-sizing: border-box; 16 | font-family: 'Montserrat', sans-serif; 17 | font-size: 1.6rem; 18 | } 19 | 20 | p { 21 | line-height: 2.4rem; 22 | 23 | &:not(:last-child) { 24 | margin-bottom: 2rem; 25 | } 26 | } 27 | 28 | a { 29 | text-decoration: none; 30 | } 31 | -------------------------------------------------------------------------------- /app/styles/base/_typography.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/app/styles/base/_typography.scss -------------------------------------------------------------------------------- /app/types/api/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/app/types/api/.gitkeep -------------------------------------------------------------------------------- /app/types/context/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/app/types/context/.gitkeep -------------------------------------------------------------------------------- /app/types/elements/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/app/types/elements/.gitkeep -------------------------------------------------------------------------------- /app/utils/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/app/utils/.gitkeep -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "fixturesFolder": "test/fixtures", 3 | "integrationFolder": "test/spec", 4 | "supportFile": "test/support/index.ts", 5 | "pluginsFile": false, 6 | "screenshotOnRunFailure": false 7 | } 8 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | clearMocks: true, 3 | coverageDirectory: 'coverage', 4 | testPathIgnorePatterns: ['/.next/', '/node_modules/'], 5 | coverageProvider: 'v8', 6 | testEnvironment: 'jsdom', 7 | setupFilesAfterEnv: ['/setupTests.ts'], 8 | transform: { 9 | '^.+\\.(js|jsx|ts|tsx)$': '/node_modules/babel-jest', 10 | }, 11 | moduleNameMapper: { 12 | '^.+\\.(css|less|scss)$': 'identity-obj-proxy', 13 | '@element/(.*)/(.*)$': '/app/components/elements/$1/$2', 14 | '@layout/(.*)/(.*)$': '/app/components/layouts/$1/$2', 15 | '@module/(.*)/(.*)$': '/app/components/modules/$1/$2', 16 | '@template/(.*)/(.*)$': '/app/components/templates/$1/$2', 17 | '@constant/(.*)/(.*)$': '/app/constants/$1/$2', 18 | '@context/(.*)/(.*)$': '/app/context/$1/$2', 19 | '@hook/(.*)/(.*)$': '/app/hooks/$1/$2', 20 | '@util/(.*)/(.*)$': '/app/utils/$1/$2', 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | sassOptions: { 6 | includePaths: [path.join(__dirname, 'app/styles')], 7 | }, 8 | i18n: { 9 | locales: ['en-US'], 10 | defaultLocale: 'en-US', 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-js-boilerplate", 3 | "version": "0.1.0", 4 | "author": "Yannick Wittwer", 5 | "license": "MIT", 6 | "scripts": { 7 | "dev": "next dev", 8 | "build": "next build", 9 | "start": "next start", 10 | "lint": "next lint && npm run lint:scss", 11 | "lint:scss": "stylelint 'app/**/*.scss' --syntax scss --fix ; exit 0", 12 | "lint:js": "next lint", 13 | "test:unit": "jest --passWithNoTests", 14 | "test:unit:update": "jest --passWithNoTests --updateSnapshot", 15 | "type:check": "tsc", 16 | "cypress": "cypress", 17 | "cypress:open": "cypress open" 18 | }, 19 | "dependencies": { 20 | "next": "11.0.1", 21 | "react": "17.0.2", 22 | "react-dom": "17.0.2", 23 | "sass": "^1.35.1" 24 | }, 25 | "devDependencies": { 26 | "@babel/preset-react": "^7.14.5", 27 | "@testing-library/dom": "^7.31.2", 28 | "@testing-library/jest-dom": "^5.14.1", 29 | "@testing-library/react": "^11.2.7", 30 | "@types/jest": "^26.0.23", 31 | "@types/node": "^14.17.3", 32 | "@types/react": "^16.14.8", 33 | "@typescript-eslint/eslint-plugin": "^4.27.0", 34 | "@typescript-eslint/parser": "^4.27.0", 35 | "axios": "^0.21.1", 36 | "babel-jest": "^26.6.3", 37 | "cypress": "^5.6.0", 38 | "eslint": "^7.29.0", 39 | "eslint-config-next": "^11.0.0", 40 | "eslint-plugin-react": "^7.24.0", 41 | "husky": "^4.3.8", 42 | "identity-obj-proxy": "^3.0.0", 43 | "jest": "^26.6.3", 44 | "jest-dom": "^4.0.0", 45 | "lint-staged": "^10.5.4", 46 | "prettier": "2.1.2", 47 | "stylelint": "^13.13.1", 48 | "stylelint-config-idiomatic-order": "^8.1.0", 49 | "ts-node": "^9.1.1", 50 | "typescript": "^4.3.4" 51 | }, 52 | "husky": { 53 | "hooks": { 54 | "pre-commit": "lint-staged" 55 | } 56 | }, 57 | "lint-staged": { 58 | "*": "prettier --write --ignore-unknown", 59 | "*.{js,jsx,ts,tsx}": "eslint", 60 | "*.{css,scss}": "stylelint" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pages/404.tsx: -------------------------------------------------------------------------------- 1 | const Custom404 = () =>
404 Page not Found
; 2 | 3 | export default Custom404; 4 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { AppProps } from 'next/app'; 2 | import 'styles/base/_base.scss'; 3 | 4 | const App = ({ Component, pageProps }: AppProps) => ( 5 | 6 | ); 7 | 8 | export default App; 9 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from 'next/document'; 2 | 3 | class CustomDocument extends Document { 4 | return = (): JSX.Element => ( 5 | 6 | 7 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | ); 18 | } 19 | 20 | export default CustomDocument; 21 | -------------------------------------------------------------------------------- /pages/_error.tsx: -------------------------------------------------------------------------------- 1 | const Error = ({ statusCode }: { statusCode?: number }) => ( 2 |

3 | {statusCode 4 | ? `An error ${statusCode} occurred on server` 5 | : 'An error occurred on client'} 6 |

7 | ); 8 | 9 | Error.getInitialProps = ({ res, err }) => { 10 | const statusCode = res ? res.statusCode : err ? err.statusCode : 404; 11 | return { statusCode }; 12 | }; 13 | 14 | export default Error; 15 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Default from '@layout/Default/Default'; 2 | import HomePage from '@template/HomePage/HomePage'; 3 | 4 | const App = () => ( 5 | 6 | 7 | 8 | ); 9 | 10 | export default App; 11 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/public/favicon.ico -------------------------------------------------------------------------------- /public/images/post.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/public/images/post.jpg -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | -------------------------------------------------------------------------------- /setupTests.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom/extend-expect'; 2 | -------------------------------------------------------------------------------- /test/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/test/fixtures/.gitkeep -------------------------------------------------------------------------------- /test/spec/e2e/SocialMedia.ts: -------------------------------------------------------------------------------- 1 | describe('Homepage', () => { 2 | it('should display three social media icons', () => { 3 | cy.visit('http://localhost:3000'); 4 | cy.getElement('header-socials').children().should('have.length', 3); 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /test/spec/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wityan/next-js-boilerplate/4186a797bb12a1cd43a48764f1214fa869188b01/test/spec/integration/.gitkeep -------------------------------------------------------------------------------- /test/support/commands/getElement.ts: -------------------------------------------------------------------------------- 1 | Cypress.Commands.add('getElement', (id, options) => 2 | cy.get(`[data-cy=${id}]`, { 3 | timeout: 5000, 4 | ...options, 5 | }) 6 | ); 7 | -------------------------------------------------------------------------------- /test/support/index.ts: -------------------------------------------------------------------------------- 1 | import './commands/getElement'; 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "baseUrl": "app", 5 | "paths": { 6 | "@element/*": ["components/elements/*"], 7 | "@layout/*": ["components/layouts/*"], 8 | "@module/*": ["components/modules/*"], 9 | "@template/*": ["components/templates/*"], 10 | "@constant/*": ["constants/*"], 11 | "@context/*": ["context/*"], 12 | "@hook/*": ["hooks/*"], 13 | "@util/*": ["utils/*"], 14 | "@api/*": ["api/*"] 15 | }, 16 | "lib": ["dom", "dom.iterable", "esnext"], 17 | "allowJs": true, 18 | "skipLibCheck": true, 19 | "strict": false, 20 | "forceConsistentCasingInFileNames": true, 21 | "noEmit": true, 22 | "esModuleInterop": true, 23 | "module": "esnext", 24 | "moduleResolution": "node", 25 | "resolveJsonModule": true, 26 | "isolatedModules": true, 27 | "jsx": "preserve", 28 | "types": ["cypress"] 29 | }, 30 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 31 | "exclude": ["node_modules"] 32 | } 33 | -------------------------------------------------------------------------------- /typings.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | declare module '*.scss' { 3 | interface IClassNames { 4 | [className: string]: string; 5 | } 6 | const classNames: IClassNames; 7 | export = classNames; 8 | } 9 | 10 | declare namespace Cypress { 11 | interface Chainable { 12 | getElement(id: string, options?: Partial): Chainable; 13 | } 14 | } 15 | --------------------------------------------------------------------------------