├── .babelrc.js
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .jest
└── jest-setup.ts
├── .prettierrc.js
├── .storybook
├── main.js
└── preview.js
├── .vscode
└── settings.json
├── README.md
├── jest.config.js
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── images
│ ├── icon-192.png
│ ├── icon-512.png
│ ├── javascript.png
│ └── typescript.jpeg
├── manifest.json
├── sw.js
├── vercel.svg
└── workbox-c2b5e142.js
├── src
├── components
│ └── TestComponent
│ │ ├── index.tsx
│ │ ├── stories.tsx
│ │ ├── styles.ts
│ │ └── test.tsx
├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ └── index.tsx
├── styles
│ ├── global-styles.ts
│ └── theme.ts
└── utils
│ └── custom-render.tsx
├── tsconfig.json
└── types
├── jest-styled-components.d.ts
└── styled-components.d.ts
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "presets": [
3 | "next/babel",
4 | "@babel/preset-typescript"
5 | ],
6 | "plugins": [
7 | [
8 | "styled-components",
9 | {
10 | "ssr": true,
11 | "displayName": true
12 | }
13 | ]
14 | ],
15 | "env": {
16 | "test": {
17 | "plugins": [
18 | [
19 | "styled-components",
20 | {
21 | "ssr": false,
22 | "displayName": false
23 | }
24 | ]
25 | ]
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | end_of_line = lf
10 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | node: true,
6 | },
7 | extends: [
8 | 'eslint:recommended',
9 | 'plugin:@typescript-eslint/recommended',
10 | 'plugin:react/recommended',
11 | 'plugin:react-hooks/recommended',
12 | 'plugin:prettier/recommended',
13 | ],
14 | parser: '@typescript-eslint/parser',
15 | parserOptions: {
16 | ecmaFeatures: {
17 | jsx: true,
18 | },
19 | ecmaVersion: 12,
20 | sourceType: 'module',
21 | },
22 | plugins: ['react', '@typescript-eslint'],
23 | settings: {
24 | react: {
25 | version: 'detect',
26 | },
27 | },
28 | rules: {
29 | '@typescript-eslint/explicit-module-boundary-types': 'off',
30 | 'react/react-in-jsx-scope': 'off',
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/.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 | *.pem
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 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/.jest/jest-setup.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import 'jest-styled-components';
3 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | trailingComma: 'all',
4 | singleQuote: true,
5 | printWidth: 80,
6 | tabWidth: 2,
7 | }
8 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "stories": [
3 | "../src/**/*.stories.mdx",
4 | "../src/**/*.stories.@(js|jsx|ts|tsx)",
5 | "../src/**/stories.@(js|jsx|ts|tsx)"
6 | ],
7 | "addons": [
8 | "@storybook/addon-links",
9 | "@storybook/addon-essentials"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import { ThemeProvider } from 'styled-components';
2 | import {theme} from '../src/styles/theme';
3 | import {GlobalStyles} from '../src/styles/global-styles';
4 |
5 | export const parameters = {
6 | actions: { argTypesRegex: "^on[A-Z].*" },
7 | }
8 |
9 | const withThemeProvider=(Story,context)=>{
10 | return (
11 |
12 |
13 |
14 |
15 | )
16 | }
17 | export const decorators = [withThemeProvider];
18 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "window.zoomLevel": 0
3 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
16 |
17 | ## Learn More
18 |
19 | To learn more about Next.js, take a look at the following resources:
20 |
21 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
22 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
23 |
24 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
25 |
26 | ## Deploy on Vercel
27 |
28 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
29 |
30 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
31 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | coverageDirectory: 'coverage',
3 | collectCoverageFrom: ['src/**/*.ts(x)?'],
4 | collectCoverage: true,
5 |
6 | testEnvironment: 'jsdom',
7 | testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'],
8 | testPathIgnorePatterns: ['/node_modules/', '/.next/', '/out/', '/public/'],
9 |
10 | setupFilesAfterEnv: ['/.jest/jest-setup.ts'],
11 | };
12 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | const nextPWA = require('next-pwa');
3 | const production = process.env.NODE_ENV === 'production';
4 |
5 | module.exports = nextPWA({
6 | pwa: {
7 | dest: 'public',
8 | disable: !production,
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "blog-next",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "test": "jest",
10 | "test:watch": "jest --watch",
11 | "storybook": "start-storybook -s ./public -p 6006",
12 | "build-storybook": "build-storybook"
13 | },
14 | "dependencies": {
15 | "next": "9.5.3",
16 | "next-pwa": "^3.1.5",
17 | "react": "16.13.1",
18 | "react-dom": "16.13.1",
19 | "styled-components": "^5.2.0"
20 | },
21 | "devDependencies": {
22 | "@babel/core": "^7.11.6",
23 | "@babel/preset-typescript": "^7.10.4",
24 | "@storybook/addon-actions": "^6.0.26",
25 | "@storybook/addon-essentials": "^6.0.26",
26 | "@storybook/addon-links": "^6.0.26",
27 | "@storybook/react": "^6.0.26",
28 | "@testing-library/jest-dom": "^5.11.4",
29 | "@testing-library/react": "^11.0.4",
30 | "@types/jest": "^26.0.14",
31 | "@types/node": "^14.11.2",
32 | "@types/react": "^16.9.50",
33 | "@types/styled-components": "^5.1.3",
34 | "@typescript-eslint/eslint-plugin": "^4.3.0",
35 | "@typescript-eslint/parser": "^4.3.0",
36 | "babel-loader": "^8.1.0",
37 | "babel-plugin-styled-components": "^1.11.1",
38 | "eslint": "^7.10.0",
39 | "eslint-config-prettier": "^6.12.0",
40 | "eslint-plugin-prettier": "^3.1.4",
41 | "eslint-plugin-react": "^7.21.3",
42 | "eslint-plugin-react-hooks": "^4.1.2",
43 | "jest": "^26.5.2",
44 | "jest-styled-components": "^7.0.3",
45 | "prettier": "^2.1.2",
46 | "react-is": "^16.13.1",
47 | "typescript": "^4.0.3"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/next-example-with-typescript-and-styled-components/0c52e42fc38f055c2e44f44999141df9b4979a20/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/next-example-with-typescript-and-styled-components/0c52e42fc38f055c2e44f44999141df9b4979a20/public/images/icon-192.png
--------------------------------------------------------------------------------
/public/images/icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/next-example-with-typescript-and-styled-components/0c52e42fc38f055c2e44f44999141df9b4979a20/public/images/icon-512.png
--------------------------------------------------------------------------------
/public/images/javascript.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/next-example-with-typescript-and-styled-components/0c52e42fc38f055c2e44f44999141df9b4979a20/public/images/javascript.png
--------------------------------------------------------------------------------
/public/images/typescript.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/next-example-with-typescript-and-styled-components/0c52e42fc38f055c2e44f44999141df9b4979a20/public/images/typescript.jpeg
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Otávio Miranda",
3 | "short_name": "OM",
4 | "icons": [
5 | {
6 | "src": "/images/icon-192.png",
7 | "type": "image/png",
8 | "sizes": "192x192"
9 | },
10 | {
11 | "src": "/images/icon-512.png",
12 | "type": "image/png",
13 | "sizes": "512x512"
14 | }
15 | ],
16 | "background_color": "#06092B",
17 | "description": "Essa é a descrição do seu blog.",
18 | "display": "fullscreen",
19 | "start_url": "/",
20 | "theme_color": "#000000"
21 | }
22 |
--------------------------------------------------------------------------------
/public/sw.js:
--------------------------------------------------------------------------------
1 | if(!self.define){const e=e=>{"require"!==e&&(e+=".js");let s=Promise.resolve();return a[e]||(s=new Promise(async s=>{if("document"in self){const a=document.createElement("script");a.src=e,document.head.appendChild(a),a.onload=s}else importScripts(e),s()})),s.then(()=>{if(!a[e])throw new Error(`Module ${e} didn’t register its module`);return a[e]})},s=(s,a)=>{Promise.all(s.map(e)).then(e=>a(1===e.length?e[0]:e))},a={require:Promise.resolve(s)};self.define=(s,n,r)=>{a[s]||(a[s]=Promise.resolve().then(()=>{let a={};const c={uri:location.origin+s.slice(1)};return Promise.all(n.map(s=>{switch(s){case"exports":return a;case"module":return c;default:return e(s)}})).then(e=>{const s=r(...e);return a.default||(a.default=s),a})}))}}define("./sw.js",["./workbox-c2b5e142"],(function(e){"use strict";importScripts(),e.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/static/chunks/f6078781a05fe1bcb0902d23dbbb2662c8d200b3.b1b4058541fcfd6d9ff9.js",revision:"25f7c5fb5ad27bfaa295f09803db0abc"},{url:"/_next/static/chunks/framework.cb05d56be993eb6b088a.js",revision:"18cf5f5d0de16c62614aa6853bc8704e"},{url:"/_next/static/chunks/main-60c8c28b71bafe118f89.js",revision:"8979ced620ea5a0e536b41f6710ddd5c"},{url:"/_next/static/chunks/pages/_app-331183f5e4bfdca2ba96.js",revision:"e2e9b9aefec06c0c3d36d414856c97ba"},{url:"/_next/static/chunks/pages/_error-ed1b06dc12d6382f8fbd.js",revision:"61483c6e9b2433d1b87721995d4f0861"},{url:"/_next/static/chunks/pages/index-ab49952df61379cf58d2.js",revision:"b63d5f9611bd606ea77809744adcad27"},{url:"/_next/static/chunks/polyfills-36bde18dcfde0c144be5.js",revision:"3c5b148baecf222e205dfe1cb1588bd6"},{url:"/_next/static/chunks/webpack-e067438c4cf4ef2ef178.js",revision:"8c19f623e8389f11131a054a7e17ff95"},{url:"/_next/static/s9q0YioLnrrxUc5oC4xyV/_buildManifest.js",revision:"a1920db642d08d99bd622527edcaa27d"},{url:"/_next/static/s9q0YioLnrrxUc5oC4xyV/_ssgManifest.js",revision:"abee47769bf307639ace4945f9cfd4ff"},{url:"/favicon.ico",revision:"3b0a6f832a69b8252ac3cdbaab9787c1"},{url:"/images/icon-192.png",revision:"9705f8af0a2a398de10b84fa23d4ff26"},{url:"/images/icon-512.png",revision:"1de2cb17810638d222e1aabfa7d2e502"},{url:"/images/javascript.png",revision:"da1e5eab6bb6fad79fb0d4d4208b408d"},{url:"/images/typescript.jpeg",revision:"ceac07ce8befe21c5a6bb4016e841432"},{url:"/manifest.json",revision:"534cb642345c2ad93d09456c6a921319"},{url:"/vercel.svg",revision:"4b4f1876502eb6721764637fe5c41702"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[new e.ExpirationPlugin({maxEntries:1,maxAgeSeconds:86400,purgeOnQuotaError:!0})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis|gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3,purgeOnQuotaError:!0})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800,purgeOnQuotaError:!0})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400,purgeOnQuotaError:!0})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400,purgeOnQuotaError:!0})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400,purgeOnQuotaError:!0})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400,purgeOnQuotaError:!0})]}),"GET"),e.registerRoute(/\/api\/.*$/i,new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400,purgeOnQuotaError:!0})]}),"GET"),e.registerRoute(/.*/i,new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400,purgeOnQuotaError:!0})]}),"GET")}));
2 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/workbox-c2b5e142.js:
--------------------------------------------------------------------------------
1 | define("./workbox-c2b5e142.js",["exports"],(function(t){"use strict";try{self["workbox:core:5.1.4"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=" :: "+JSON.stringify(e)),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:5.1.4"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class i{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}}class r extends i{constructor(t,e,s){super(({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)},e,s)}}const a=t=>new URL(String(t),location.href).href.replace(new RegExp("^"+location.origin),"");class c{constructor(){this.t=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)})}addCacheListener(){self.addEventListener("message",t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map(t=>{"string"==typeof t&&(t=[t]);const e=new Request(...t);return this.handleRequest({request:e})}));t.waitUntil(s),t.ports&&t.ports[0]&&s.then(()=>t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const{params:n,route:i}=this.findMatchingRoute({url:s,request:t,event:e});let r,a=i&&i.handler;if(!a&&this.s&&(a=this.s),a){try{r=a.handle({url:s,request:t,event:e,params:n})}catch(t){r=Promise.reject(t)}return r instanceof Promise&&this.i&&(r=r.catch(n=>this.i.handle({url:s,request:t,event:e}))),r}}findMatchingRoute({url:t,request:e,event:s}){const n=this.t.get(e.method)||[];for(const i of n){let n;const r=i.match({url:t,request:e,event:s});if(r)return n=r,(Array.isArray(r)&&0===r.length||r.constructor===Object&&0===Object.keys(r).length||"boolean"==typeof r)&&(n=void 0),{route:i,params:n}}return{}}setDefaultHandler(t){this.s=n(t)}setCatchHandler(t){this.i=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let o;const h=()=>(o||(o=new c,o.addFetchListener(),o.addCacheListener()),o);const u={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},l=t=>[u.prefix,t,u.suffix].filter(t=>t&&t.length>0).join("-"),f=t=>t||l(u.precache),w=t=>t||l(u.runtime);function p(t){t.then(()=>{})}const d=new Set;class y{constructor(t,e,{onupgradeneeded:s,onversionchange:n}={}){this.o=null,this.h=t,this.u=e,this.l=s,this.p=n||(()=>this.close())}get db(){return this.o}async open(){if(!this.o)return this.o=await new Promise((t,e)=>{let s=!1;setTimeout(()=>{s=!0,e(new Error("The open request was blocked and timed out"))},this.OPEN_TIMEOUT);const n=indexedDB.open(this.h,this.u);n.onerror=()=>e(n.error),n.onupgradeneeded=t=>{s?(n.transaction.abort(),n.result.close()):"function"==typeof this.l&&this.l(t)},n.onsuccess=()=>{const e=n.result;s?e.close():(e.onversionchange=this.p.bind(this),t(e))}}),this}async getKey(t,e){return(await this.getAllKeys(t,e,1))[0]}async getAll(t,e,s){return await this.getAllMatching(t,{query:e,count:s})}async getAllKeys(t,e,s){return(await this.getAllMatching(t,{query:e,count:s,includeKeys:!0})).map(t=>t.key)}async getAllMatching(t,{index:e,query:s=null,direction:n="next",count:i,includeKeys:r=!1}={}){return await this.transaction([t],"readonly",(a,c)=>{const o=a.objectStore(t),h=e?o.index(e):o,u=[],l=h.openCursor(s,n);l.onsuccess=()=>{const t=l.result;t?(u.push(r?t:t.value),i&&u.length>=i?c(u):t.continue()):c(u)}})}async transaction(t,e,s){return await this.open(),await new Promise((n,i)=>{const r=this.o.transaction(t,e);r.onabort=()=>i(r.error),r.oncomplete=()=>n(),s(r,t=>n(t))})}async g(t,e,s,...n){return await this.transaction([e],s,(s,i)=>{const r=s.objectStore(e),a=r[t].apply(r,n);a.onsuccess=()=>i(a.result)})}close(){this.o&&(this.o.close(),this.o=null)}}y.prototype.OPEN_TIMEOUT=2e3;const g={readonly:["get","count","getKey","getAll","getAllKeys"],readwrite:["add","put","clear","delete"]};for(const[t,e]of Object.entries(g))for(const s of e)s in IDBObjectStore.prototype&&(y.prototype[s]=async function(e,...n){return await this.g(s,e,t,...n)});try{self["workbox:expiration:5.1.4"]&&_()}catch(t){}const m=t=>{const e=new URL(t,location.href);return e.hash="",e.href};class q{constructor(t){this.m=t,this.o=new y("workbox-expiration",1,{onupgradeneeded:t=>this.q(t)})}q(t){const e=t.target.result.createObjectStore("cache-entries",{keyPath:"id"});e.createIndex("cacheName","cacheName",{unique:!1}),e.createIndex("timestamp","timestamp",{unique:!1}),(async t=>{await new Promise((e,s)=>{const n=indexedDB.deleteDatabase(t);n.onerror=()=>{s(n.error)},n.onblocked=()=>{s(new Error("Delete blocked"))},n.onsuccess=()=>{e()}})})(this.m)}async setTimestamp(t,e){const s={url:t=m(t),timestamp:e,cacheName:this.m,id:this.v(t)};await this.o.put("cache-entries",s)}async getTimestamp(t){return(await this.o.get("cache-entries",this.v(t))).timestamp}async expireEntries(t,e){const s=await this.o.transaction("cache-entries","readwrite",(s,n)=>{const i=s.objectStore("cache-entries").index("timestamp").openCursor(null,"prev"),r=[];let a=0;i.onsuccess=()=>{const s=i.result;if(s){const n=s.value;n.cacheName===this.m&&(t&&n.timestamp=e?r.push(s.value):a++),s.continue()}else n(r)}}),n=[];for(const t of s)await this.o.delete("cache-entries",t.id),n.push(t.url);return n}v(t){return this.m+"|"+m(t)}}class v{constructor(t,e={}){this.R=!1,this.U=!1,this.L=e.maxEntries,this.N=e.maxAgeSeconds,this.m=t,this._=new q(t)}async expireEntries(){if(this.R)return void(this.U=!0);this.R=!0;const t=this.N?Date.now()-1e3*this.N:0,e=await this._.expireEntries(t,this.L),s=await self.caches.open(this.m);for(const t of e)await s.delete(t);this.R=!1,this.U&&(this.U=!1,p(this.expireEntries()))}async updateTimestamp(t){await this._.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.N){return await this._.getTimestamp(t)t.filter(t=>e in t),U=async({request:t,mode:e,plugins:s=[]})=>{const n=R(s,"cacheKeyWillBeUsed");let i=t;for(const t of n)i=await t.cacheKeyWillBeUsed.call(t,{mode:e,request:i}),"string"==typeof i&&(i=new Request(i));return i},L=async({cacheName:t,request:e,event:s,matchOptions:n,plugins:i=[]})=>{const r=await self.caches.open(t),a=await U({plugins:i,request:e,mode:"read"});let c=await r.match(a,n);for(const e of i)if("cachedResponseWillBeUsed"in e){const i=e.cachedResponseWillBeUsed;c=await i.call(e,{cacheName:t,event:s,matchOptions:n,cachedResponse:c,request:a})}return c},x=async({cacheName:t,request:e,response:n,event:i,plugins:r=[],matchOptions:c})=>{const o=await U({plugins:r,request:e,mode:"write"});if(!n)throw new s("cache-put-with-no-response",{url:a(o.url)});const h=await(async({request:t,response:e,event:s,plugins:n=[]})=>{let i=e,r=!1;for(const e of n)if("cacheWillUpdate"in e){r=!0;const n=e.cacheWillUpdate;if(i=await n.call(e,{request:t,response:i,event:s}),!i)break}return r||(i=i&&200===i.status?i:void 0),i||null})({event:i,plugins:r,response:n,request:o});if(!h)return;const u=await self.caches.open(t),l=R(r,"cacheDidUpdate"),f=l.length>0?await L({cacheName:t,matchOptions:c,request:o}):null;try{await u.put(o,h)}catch(t){throw"QuotaExceededError"===t.name&&await async function(){for(const t of d)await t()}(),t}for(const e of l)await e.cacheDidUpdate.call(e,{cacheName:t,event:i,oldResponse:f,newResponse:h,request:o})},N=L,b=async({request:t,fetchOptions:e,event:n,plugins:i=[]})=>{if("string"==typeof t&&(t=new Request(t)),n instanceof FetchEvent&&n.preloadResponse){const t=await n.preloadResponse;if(t)return t}const r=R(i,"fetchDidFail"),a=r.length>0?t.clone():null;try{for(const e of i)if("requestWillFetch"in e){const s=e.requestWillFetch,i=t.clone();t=await s.call(e,{request:i,event:n})}}catch(t){throw new s("plugin-error-request-will-fetch",{thrownError:t})}const c=t.clone();try{let s;s="navigate"===t.mode?await fetch(t):await fetch(t,e);for(const t of i)"fetchDidSucceed"in t&&(s=await t.fetchDidSucceed.call(t,{event:n,request:c,response:s}));return s}catch(t){for(const e of r)await e.fetchDidFail.call(e,{error:t,event:n,originalRequest:a.clone(),request:c.clone()});throw t}};try{self["workbox:strategies:5.1.4"]&&_()}catch(t){}const O={cacheWillUpdate:async({response:t})=>200===t.status||0===t.status?t:null};let E;async function K(t,e){const s=t.clone(),n={headers:new Headers(s.headers),status:s.status,statusText:s.statusText},i=e?e(n):n,r=function(){if(void 0===E){const t=new Response("");if("body"in t)try{new Response(t.body),E=!0}catch(t){E=!1}E=!1}return E}()?s.body:await s.blob();return new Response(r,i)}try{self["workbox:precaching:5.1.4"]&&_()}catch(t){}function M(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const i=new URL(n,location.href),r=new URL(n,location.href);return i.searchParams.set("__WB_REVISION__",e),{cacheKey:i.href,url:r.href}}class P{constructor(t){this.m=f(t),this.O=new Map,this.K=new Map,this.M=new Map}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:i}=M(n),r="string"!=typeof n&&n.revision?"reload":"default";if(this.O.has(i)&&this.O.get(i)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.O.get(i),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.M.has(t)&&this.M.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:i});this.M.set(t,n.integrity)}if(this.O.set(i,t),this.K.set(i,r),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}async install({event:t,plugins:e}={}){const s=[],n=[],i=await self.caches.open(this.m),r=await i.keys(),a=new Set(r.map(t=>t.url));for(const[t,e]of this.O)a.has(e)?n.push(t):s.push({cacheKey:e,url:t});const c=s.map(({cacheKey:s,url:n})=>{const i=this.M.get(s),r=this.K.get(n);return this.P({cacheKey:s,cacheMode:r,event:t,integrity:i,plugins:e,url:n})});await Promise.all(c);return{updatedURLs:s.map(t=>t.url),notUpdatedURLs:n}}async activate(){const t=await self.caches.open(this.m),e=await t.keys(),s=new Set(this.O.values()),n=[];for(const i of e)s.has(i.url)||(await t.delete(i),n.push(i.url));return{deletedURLs:n}}async P({cacheKey:t,url:e,cacheMode:n,event:i,plugins:r,integrity:a}){const c=new Request(e,{integrity:a,cache:n,credentials:"same-origin"});let o,h=await b({event:i,plugins:r,request:c});for(const t of r||[])"cacheWillUpdate"in t&&(o=t);if(!(o?await o.cacheWillUpdate({event:i,request:c,response:h}):h.status<400))throw new s("bad-precaching-response",{url:e,status:h.status});h.redirected&&(h=await K(h)),await x({event:i,plugins:r,response:h,request:t===e?c:new Request(t),cacheName:this.m,matchOptions:{ignoreSearch:!0}})}getURLsToCacheKeys(){return this.O}getCachedURLs(){return[...this.O.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.O.get(e.href)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.m)).match(s)}}createHandler(t=!0){return async({request:e})=>{try{const t=await this.matchPrecache(e);if(t)return t;throw new s("missing-precache-entry",{cacheName:this.m,url:e instanceof Request?e.url:e})}catch(s){if(t)return fetch(e);throw s}}}createHandlerBoundToURL(t,e=!0){if(!this.getCacheKeyForURL(t))throw new s("non-precached-url",{url:t});const n=this.createHandler(e),i=new Request(t);return()=>n({request:i})}}let T;const D=()=>(T||(T=new P),T);const k=(t,e)=>{const s=D().getURLsToCacheKeys();for(const n of function*(t,{ignoreURLParametersMatching:e,directoryIndex:s,cleanURLs:n,urlManipulation:i}={}){const r=new URL(t,location.href);r.hash="",yield r.href;const a=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some(t=>t.test(s))&&t.searchParams.delete(s);return t}(r,e);if(yield a.href,s&&a.pathname.endsWith("/")){const t=new URL(a.href);t.pathname+=s,yield t.href}if(n){const t=new URL(a.href);t.pathname+=".html",yield t.href}if(i){const t=i({url:r});for(const e of t)yield e.href}}(t,e)){const t=s.get(n);if(t)return t}};let C=!1;function A(t){C||((({ignoreURLParametersMatching:t=[/^utm_/],directoryIndex:e="index.html",cleanURLs:s=!0,urlManipulation:n}={})=>{const i=f();self.addEventListener("fetch",r=>{const a=k(r.request.url,{cleanURLs:s,directoryIndex:e,ignoreURLParametersMatching:t,urlManipulation:n});if(!a)return;let c=self.caches.open(i).then(t=>t.match(a)).then(t=>t||fetch(a));r.respondWith(c)})})(t),C=!0)}const S=[],F={get:()=>S,add(t){S.push(...t)}},I=t=>{const e=D(),s=F.get();t.waitUntil(e.install({event:t,plugins:s}).catch(t=>{throw t}))},W=t=>{const e=D();t.waitUntil(e.activate())};t.CacheFirst=class{constructor(t={}){this.m=w(t.cacheName),this.T=t.plugins||[],this.D=t.fetchOptions,this.k=t.matchOptions}async handle({event:t,request:e}){"string"==typeof e&&(e=new Request(e));let n,i=await N({cacheName:this.m,request:e,event:t,matchOptions:this.k,plugins:this.T});if(!i)try{i=await this.C(e,t)}catch(t){n=t}if(!i)throw new s("no-response",{url:e.url,error:n});return i}async C(t,e){const s=await b({request:t,event:e,fetchOptions:this.D,plugins:this.T}),n=s.clone(),i=x({cacheName:this.m,request:t,response:n,event:e,plugins:this.T});if(e)try{e.waitUntil(i)}catch(t){}return s}},t.ExpirationPlugin=class{constructor(t={}){var e;this.cachedResponseWillBeUsed=async({event:t,request:e,cacheName:s,cachedResponse:n})=>{if(!n)return null;const i=this.A(n),r=this.S(s);p(r.expireEntries());const a=r.updateTimestamp(e.url);if(t)try{t.waitUntil(a)}catch(t){}return i?n:null},this.cacheDidUpdate=async({cacheName:t,request:e})=>{const s=this.S(t);await s.updateTimestamp(e.url),await s.expireEntries()},this.F=t,this.N=t.maxAgeSeconds,this.I=new Map,t.purgeOnQuotaError&&(e=()=>this.deleteCacheAndMetadata(),d.add(e))}S(t){if(t===w())throw new s("expire-custom-caches-only");let e=this.I.get(t);return e||(e=new v(t,this.F),this.I.set(t,e)),e}A(t){if(!this.N)return!0;const e=this.W(t);if(null===e)return!0;return e>=Date.now()-1e3*this.N}W(t){if(!t.headers.has("date"))return null;const e=t.headers.get("date"),s=new Date(e).getTime();return isNaN(s)?null:s}async deleteCacheAndMetadata(){for(const[t,e]of this.I)await self.caches.delete(t),await e.delete();this.I=new Map}},t.NetworkFirst=class{constructor(t={}){if(this.m=w(t.cacheName),t.plugins){const e=t.plugins.some(t=>!!t.cacheWillUpdate);this.T=e?t.plugins:[O,...t.plugins]}else this.T=[O];this.B=t.networkTimeoutSeconds||0,this.D=t.fetchOptions,this.k=t.matchOptions}async handle({event:t,request:e}){const n=[];"string"==typeof e&&(e=new Request(e));const i=[];let r;if(this.B){const{id:s,promise:a}=this.H({request:e,event:t,logs:n});r=s,i.push(a)}const a=this.j({timeoutId:r,request:e,event:t,logs:n});i.push(a);let c=await Promise.race(i);if(c||(c=await a),!c)throw new s("no-response",{url:e.url});return c}H({request:t,logs:e,event:s}){let n;return{promise:new Promise(e=>{n=setTimeout(async()=>{e(await this.G({request:t,event:s}))},1e3*this.B)}),id:n}}async j({timeoutId:t,request:e,logs:s,event:n}){let i,r;try{r=await b({request:e,event:n,fetchOptions:this.D,plugins:this.T})}catch(t){i=t}if(t&&clearTimeout(t),i||!r)r=await this.G({request:e,event:n});else{const t=r.clone(),s=x({cacheName:this.m,request:e,response:t,event:n,plugins:this.T});if(n)try{n.waitUntil(s)}catch(t){}}return r}G({event:t,request:e}){return N({cacheName:this.m,request:e,event:t,matchOptions:this.k,plugins:this.T})}},t.StaleWhileRevalidate=class{constructor(t={}){if(this.m=w(t.cacheName),this.T=t.plugins||[],t.plugins){const e=t.plugins.some(t=>!!t.cacheWillUpdate);this.T=e?t.plugins:[O,...t.plugins]}else this.T=[O];this.D=t.fetchOptions,this.k=t.matchOptions}async handle({event:t,request:e}){"string"==typeof e&&(e=new Request(e));const n=this.C({request:e,event:t});let i,r=await N({cacheName:this.m,request:e,event:t,matchOptions:this.k,plugins:this.T});if(r){if(t)try{t.waitUntil(n)}catch(i){}}else try{r=await n}catch(t){i=t}if(!r)throw new s("no-response",{url:e.url,error:i});return r}async C({request:t,event:e}){const s=await b({request:t,event:e,fetchOptions:this.D,plugins:this.T}),n=x({cacheName:this.m,request:t,response:s.clone(),event:e,plugins:this.T});if(e)try{e.waitUntil(n)}catch(t){}return s}},t.cleanupOutdatedCaches=function(){self.addEventListener("activate",t=>{const e=f();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter(s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t);return await Promise.all(s.map(t=>self.caches.delete(t))),s})(e).then(t=>{}))})},t.clientsClaim=function(){self.addEventListener("activate",()=>self.clients.claim())},t.precacheAndRoute=function(t,e){!function(t){D().addToCacheList(t),t.length>0&&(self.addEventListener("install",I),self.addEventListener("activate",W))}(t),A(e)},t.registerRoute=function(t,e,n){let a;if("string"==typeof t){const s=new URL(t,location.href);a=new i(({url:t})=>t.href===s.href,e,n)}else if(t instanceof RegExp)a=new r(t,e,n);else if("function"==typeof t)a=new i(t,e,n);else{if(!(t instanceof i))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});a=t}return h().registerRoute(a),a},t.skipWaiting=function(){self.addEventListener("install",()=>self.skipWaiting())}}));
2 |
--------------------------------------------------------------------------------
/src/components/TestComponent/index.tsx:
--------------------------------------------------------------------------------
1 | import { Container } from './styles';
2 |
3 | export type TestComponentProps = {
4 | children: React.ReactNode;
5 | };
6 |
7 | export const TestComponent = ({ children }: TestComponentProps) => {
8 | return {children};
9 | };
10 |
--------------------------------------------------------------------------------
/src/components/TestComponent/stories.tsx:
--------------------------------------------------------------------------------
1 | import { TestComponent, TestComponentProps } from '.';
2 | import { Meta, Story } from '@storybook/react/types-6-0';
3 |
4 | export default {
5 | title: 'TestComponent',
6 | component: TestComponent,
7 | args: {
8 | children: 'Default Value',
9 | },
10 | argTypes: {
11 | children: {
12 | type: 'string',
13 | },
14 | },
15 | } as Meta;
16 |
17 | export const Template: Story = (args) => (
18 |
19 | );
20 |
--------------------------------------------------------------------------------
/src/components/TestComponent/styles.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.h1`
4 | ${({ theme }) => css`
5 | background: ${theme.colors.primary};
6 | `}
7 | `;
8 |
--------------------------------------------------------------------------------
/src/components/TestComponent/test.tsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/react';
2 | import { TestComponent } from '.';
3 | import { customRender } from '../../utils/custom-render';
4 |
5 | describe('', () => {
6 | it('should render the test component', () => {
7 | customRender(Oi);
8 | expect(screen.getByRole('heading', { name: 'Oi' })).toHaveStyleRule(
9 | 'background',
10 | '#0070f3',
11 | );
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { AppProps } from 'next/app';
2 | import { ThemeProvider } from 'styled-components';
3 | import { GlobalStyles } from '../styles/global-styles';
4 | import { theme } from '../styles/theme';
5 |
6 | function MyApp({ Component, pageProps }: AppProps) {
7 | return (
8 |
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | export default MyApp;
16 |
--------------------------------------------------------------------------------
/src/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import Document, {
2 | DocumentContext,
3 | Head,
4 | Html,
5 | Main,
6 | NextScript,
7 | } from 'next/document';
8 | import { ServerStyleSheet } from 'styled-components';
9 |
10 | export default class MyDocument extends Document {
11 | static async getInitialProps(ctx: DocumentContext) {
12 | const sheet = new ServerStyleSheet();
13 | const originalRenderPage = ctx.renderPage;
14 |
15 | try {
16 | ctx.renderPage = () =>
17 | originalRenderPage({
18 | enhanceApp: (App) => (props) =>
19 | sheet.collectStyles(),
20 | });
21 |
22 | const initialProps = await Document.getInitialProps(ctx);
23 | return {
24 | ...initialProps,
25 | styles: (
26 | <>
27 | {initialProps.styles}
28 | {sheet.getStyleElement()}
29 | >
30 | ),
31 | };
32 | } finally {
33 | sheet.seal();
34 | }
35 | }
36 |
37 | render() {
38 | return (
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | export default function Home() {
2 | return Oi
;
3 | }
4 |
--------------------------------------------------------------------------------
/src/styles/global-styles.ts:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from 'styled-components';
2 |
3 | export const GlobalStyles = createGlobalStyle`
4 | body {
5 | background: red;
6 | }
7 | `;
8 |
--------------------------------------------------------------------------------
/src/styles/theme.ts:
--------------------------------------------------------------------------------
1 | export const theme = {
2 | colors: {
3 | primary: '#0070f3',
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/src/utils/custom-render.tsx:
--------------------------------------------------------------------------------
1 | import { render, RenderResult } from '@testing-library/react';
2 | import { ThemeProvider } from 'styled-components';
3 | import { theme } from '../styles/theme';
4 |
5 | export const customRender = (children: React.ReactNode): RenderResult => {
6 | return render({children});
7 | };
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "strict": false,
12 | "forceConsistentCasingInFileNames": true,
13 | "noEmit": true,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "jsx": "preserve"
20 | },
21 | "include": [
22 | "next-env.d.ts",
23 | "**/*.ts",
24 | "**/*.tsx"
25 | ],
26 | "exclude": [
27 | "node_modules"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/types/jest-styled-components.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import { Plugin, NewPlugin } from 'pretty-format';
3 |
4 | declare global {
5 | namespace jest {
6 | interface AsymmetricMatcher {
7 | $$typeof: symbol;
8 | sample?: string | RegExp | object | Array | Function;
9 | }
10 |
11 | type Value = string | number | RegExp | AsymmetricMatcher | undefined;
12 |
13 | interface Options {
14 | media?: string;
15 | modifier?: string;
16 | supports?: string;
17 | }
18 |
19 | interface Matchers {
20 | toHaveStyleRule(property: string, value?: Value, options?: Options): R;
21 | }
22 | }
23 | }
24 |
25 | export declare const styleSheetSerializer: Exclude;
26 |
--------------------------------------------------------------------------------
/types/styled-components.d.ts:
--------------------------------------------------------------------------------
1 | import { theme } from '../src/styles/theme';
2 |
3 | type Theme = typeof theme;
4 |
5 | declare module 'styled-components' {
6 | // eslint-disable-next-line @typescript-eslint/no-empty-interface
7 | export interface DefaultTheme extends Theme {}
8 | }
9 |
--------------------------------------------------------------------------------