├── .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 | 3 | 4 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------