├── .prettierrc ├── packages └── web │ ├── .eslintignore │ ├── .gitignore │ ├── public │ ├── favicon.ico │ ├── favicon_old.ico │ ├── images │ │ ├── nonce.JPG │ │ ├── agi house.jpeg │ │ ├── kumar usa.jpeg │ │ ├── YC Speaking.jpg │ │ ├── YCombinator.jpg │ │ ├── korea_dinner.jpeg │ │ ├── sam_hinkie.jpeg │ │ ├── arcadia dinner.jpeg │ │ ├── bryan_johnson.jpeg │ │ ├── eric_jorgenson.jpeg │ │ ├── walk_with_naval.jpg │ │ ├── arcadia dinner 2.jpeg │ │ ├── eric_jorgenson_2.JPG │ │ ├── stanford treehacks.jpeg │ │ ├── SF Tech Week 2024 (1).jpg │ │ ├── SF Tech Week 2024 (2).jpg │ │ └── mark rachapoom founders.JPG │ └── favicon_old_2.ico │ ├── .env.example │ ├── .eslintrc.js │ ├── hooks │ ├── useMobile.js │ └── useTwitchIsLive.js │ ├── components │ ├── grid-background.jsx │ ├── TextTransitionAnimation.js │ ├── Subscribe.js │ ├── Layout.jsx │ └── CodeBlock.js │ ├── pages │ ├── subscribe │ │ ├── thanks.js │ │ └── index.js │ ├── live.js │ ├── _document.js │ ├── _app.js │ ├── writings.js │ ├── writings │ │ └── [slug] │ │ │ └── index.js │ └── index.js │ ├── constants │ ├── metadata.js │ ├── Uses.js │ ├── Meta.jsx │ └── Stack.js │ ├── scripts │ └── generateSitemap.js │ ├── archived-writings │ ├── my-first-post.md │ ├── async-await-crash-course.md │ ├── how-to-rate-limit-your-express-api-endpoints.md │ ├── pkgreview-dev-ratings-review-website-npm-packages.md │ ├── starter-guide-create-command-line-app-node-js.md │ ├── five-package-json-scripts-you-dont-use.md │ ├── what-keeps-founders-motivated-not-quit.md │ ├── google-recaptcha-react-node-js.md │ ├── build-publish-react-components.md │ └── next-js-docker-made-easy.md │ ├── package.json │ ├── next.config.js │ ├── writings │ └── what-keeps-founders-motivated-not-quit.md │ └── styles │ └── base.css ├── .gitignore ├── lerna.json ├── .travis.yml ├── .vscode └── settings.json ├── LICENSE ├── package.json └── README.md /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false 3 | } 4 | -------------------------------------------------------------------------------- /packages/web/.eslintignore: -------------------------------------------------------------------------------- 1 | # add files to ignore here 2 | .next 3 | build 4 | export -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | yarn.lock 3 | package-lock.json 4 | .DS_Store 5 | *.key 6 | og -------------------------------------------------------------------------------- /packages/web/.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | out 3 | .env 4 | package.lock 5 | yarn.lock 6 | drafts 7 | sitemap.xml -------------------------------------------------------------------------------- /packages/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/favicon.ico -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmClient": "yarn", 3 | "useWorkspaces": true, 4 | "packages": ["packages/*"], 5 | "version": "0.0.0" 6 | } -------------------------------------------------------------------------------- /packages/web/public/favicon_old.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/favicon_old.ico -------------------------------------------------------------------------------- /packages/web/public/images/nonce.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/nonce.JPG -------------------------------------------------------------------------------- /packages/web/public/favicon_old_2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/favicon_old_2.ico -------------------------------------------------------------------------------- /packages/web/public/images/agi house.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/agi house.jpeg -------------------------------------------------------------------------------- /packages/web/public/images/kumar usa.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/kumar usa.jpeg -------------------------------------------------------------------------------- /packages/web/public/images/YC Speaking.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/YC Speaking.jpg -------------------------------------------------------------------------------- /packages/web/public/images/YCombinator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/YCombinator.jpg -------------------------------------------------------------------------------- /packages/web/public/images/korea_dinner.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/korea_dinner.jpeg -------------------------------------------------------------------------------- /packages/web/public/images/sam_hinkie.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/sam_hinkie.jpeg -------------------------------------------------------------------------------- /packages/web/public/images/arcadia dinner.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/arcadia dinner.jpeg -------------------------------------------------------------------------------- /packages/web/public/images/bryan_johnson.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/bryan_johnson.jpeg -------------------------------------------------------------------------------- /packages/web/public/images/eric_jorgenson.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/eric_jorgenson.jpeg -------------------------------------------------------------------------------- /packages/web/public/images/walk_with_naval.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/walk_with_naval.jpg -------------------------------------------------------------------------------- /packages/web/public/images/arcadia dinner 2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/arcadia dinner 2.jpeg -------------------------------------------------------------------------------- /packages/web/public/images/eric_jorgenson_2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/eric_jorgenson_2.JPG -------------------------------------------------------------------------------- /packages/web/public/images/stanford treehacks.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/stanford treehacks.jpeg -------------------------------------------------------------------------------- /packages/web/public/images/SF Tech Week 2024 (1).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/SF Tech Week 2024 (1).jpg -------------------------------------------------------------------------------- /packages/web/public/images/SF Tech Week 2024 (2).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/SF Tech Week 2024 (2).jpg -------------------------------------------------------------------------------- /packages/web/public/images/mark rachapoom founders.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumarabhirup/personal-website/HEAD/packages/web/public/images/mark rachapoom founders.JPG -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" 4 | 5 | env: 6 | - NODE_ENV=test 7 | script: 8 | - npm run test:ci 9 | - travis_wait 30 npm run lint 10 | -------------------------------------------------------------------------------- /packages/web/.env.example: -------------------------------------------------------------------------------- 1 | # Rename me to .env 2 | RECAPTCHA_CLIENT_KEY="6....UA............jKm" 3 | GA_TRACKING_ID="U.......21-6" 4 | TWITCH_CLIENT_ID="U.......21-6" 5 | TWITCH_OAUTH_ID="siugfhwi8723eshh........fvsvwrgw" 6 | KUMAR_BACKEND_URL="http://localhost:4000" -------------------------------------------------------------------------------- /packages/web/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ 3 | "wesbos" 4 | ], 5 | "rules": { 6 | "semi": [2, "never"], 7 | "react/prop-types": [1] 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 2017 11 | }, 12 | "env": { 13 | "es6": true, 14 | "browser": true, 15 | "node": true 16 | }, 17 | "plugins": [ 18 | "html" 19 | ], 20 | "settings": { 21 | "html/html-extensions": [".html"] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/web/hooks/useMobile.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const useMobile = () => { 4 | const [isMobile, setIsMobile] = React.useState(false) 5 | 6 | React.useEffect(() => { 7 | const handleResize = () => { 8 | setIsMobile(window.innerWidth <= 768) 9 | } 10 | window.addEventListener('resize', handleResize) 11 | handleResize() 12 | return () => window.removeEventListener('resize', handleResize) 13 | }, []) 14 | 15 | return isMobile 16 | } 17 | 18 | export default useMobile 19 | -------------------------------------------------------------------------------- /packages/web/components/grid-background.jsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React from 'react' 4 | 5 | const GridBackground = () => ( 6 |
17 |
28 |
29 | ) 30 | 31 | export default GridBackground 32 | -------------------------------------------------------------------------------- /packages/web/components/TextTransitionAnimation.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import dynamic from 'next/dynamic' 3 | import { config } from 'react-spring' 4 | 5 | import { SKILLS } from '../constants/Stack' 6 | 7 | const TextTransition = dynamic(() => import('react-text-transition'), { 8 | ssr: false, 9 | }) 10 | 11 | export default function TextTransitionAnimation() { 12 | const [index, setIndex] = useState(0) 13 | 14 | useEffect(() => { 15 | const intervalId = setInterval( 16 | // eslint-disable-next-line no-shadow 17 | () => setIndex(index => index + 1), 18 | 3000 // every 3 seconds 19 | ) 20 | }, [index]) 21 | 22 | return ( 23 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /packages/web/pages/subscribe/thanks.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Row, Col } from 'react-flexbox-grid' 3 | 4 | import Layout from '../../components/Layout' 5 | import Subscribe from '../../components/Subscribe' 6 | import { DiscordInviteBox } from '.' 7 | 8 | function ThanksSubscribePage() { 9 | return ( 10 | <> 11 | 12 | 13 | 14 |
15 |

You are just one step away from joining our club!

16 |

Confirmation link is in your inbox, go check it out!

17 |
18 | 19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 | ) 28 | } 29 | 30 | export default ThanksSubscribePage 31 | -------------------------------------------------------------------------------- /packages/web/pages/live.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import dynamic from 'next/dynamic' 3 | 4 | import { META } from '../constants/metadata' 5 | import SubscribePage from './subscribe' 6 | 7 | const ReactTwitchEmbedVideo = dynamic( 8 | () => import('react-twitch-embed-video'), 9 | { ssr: false } 10 | ) 11 | 12 | function LivePage() { 13 | return ( 14 | <> 15 | 23 | 24 | 25 | 26 | ) 27 | } 28 | 29 | LivePage.getInitialProps = () => ({ 30 | data: { 31 | og: { 32 | description: `${META.fname} is LIVE NOW. Join the live stream!`, 33 | image: META.pageOgs.live, 34 | }, 35 | }, 36 | }) 37 | 38 | export default LivePage 39 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | // turn VSCode internal formatter off for JS and JSX, ESLint will do it. 4 | "[javascript]": { 5 | "editor.formatOnSave": false 6 | }, 7 | "[typescript]": { 8 | "editor.formatOnSave": false 9 | }, 10 | "[javascriptreact]": { 11 | "editor.formatOnSave": false 12 | }, 13 | "[typescriptreact]": { 14 | "editor.formatOnSave": false 15 | }, 16 | "editor.codeActionsOnSave": { 17 | "source.fixAll": "explicit" 18 | }, 19 | // Optional BUT IMPORTANT: If you have the prettier extension enabled for other languages like CSS and HTML, turn it off for JS since we are doing it through Eslint already 20 | "prettier.disableLanguages": [ 21 | "javascript", 22 | "javascriptreact", 23 | "typescript", 24 | "typescriptreact" 25 | ], 26 | "eslint.workingDirectories": [ 27 | "packages/web", 28 | "packages/backend" 29 | ], 30 | "editor.autoSave": "off" 31 | } 32 | -------------------------------------------------------------------------------- /packages/web/hooks/useTwitchIsLive.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | import { META } from '../constants/metadata' 4 | 5 | function useTwitchIsLive() { 6 | const [isLive, setIsLive] = useState(false) 7 | 8 | useEffect(() => { 9 | const fetchData = async () => { 10 | const data = await fetch( 11 | `https://api.twitch.tv/helix/streams?user_login=${META.social.twitch}`, 12 | { 13 | headers: { 14 | 'Client-ID': process.env.TWITCH_CLIENT_ID, 15 | Authorization: `Bearer ${process.env.TWITCH_OAUTH_ID}`, 16 | }, 17 | } 18 | ) 19 | .then(res => res.json()) 20 | .catch(e => {}) 21 | 22 | if (data && data?.data && data?.data[0]) { 23 | setIsLive(true) 24 | } else { 25 | setIsLive(false) 26 | } 27 | } 28 | 29 | fetchData() 30 | }, [isLive]) 31 | 32 | return isLive 33 | } 34 | 35 | export default useTwitchIsLive 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kumar Abhirup 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "personal-website", 3 | "version": "1.0.0", 4 | "private": true, 5 | "workspaces": [ 6 | "packages/*" 7 | ], 8 | "scripts": { 9 | "build-web": "(cd web && npm run build)", 10 | "build": "NODE_ENV=production lerna run build --parallel", 11 | "build:watch": "NODE_ENV=production lerna run build:watch --parallel", 12 | "dev": "NODE_ENV=development lerna run dev --parallel", 13 | "start": "NODE_ENV=production lerna run start --parallel", 14 | "deploy:now": "now", 15 | "lint": "lerna run lint --parallel", 16 | "lint:fix": "lerna run lint:fix --parallel", 17 | "test": "lerna run test --parallel", 18 | "test:ci": "lerna run test:ci --parallel", 19 | "test-postinstall": "lerna bootstrap" 20 | }, 21 | "main": "index.js", 22 | "repository": "git@github.com:KumarAbhirup/personal-website.git", 23 | "author": "Kumar Abhirup", 24 | "license": "MIT", 25 | "devDependencies": { 26 | "lerna": "^3.22.1" 27 | }, 28 | "engines": { 29 | "node": ">=12.x" 30 | }, 31 | "dependencies": { 32 | "imagemin": "^8.0.1", 33 | "imagemin-jpegtran": "^7.0.0", 34 | "imagemin-pngquant": "^9.0.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/web/constants/metadata.js: -------------------------------------------------------------------------------- 1 | const username = 'kumareth' 2 | 3 | const META = { 4 | title: 'Kumar Abhirup', 5 | name: 'Kumar Abhirup', 6 | fname: 'Kumar', 7 | lname: 'Abhirup', 8 | website: 'https://kumareth.com', 9 | username, 10 | social: { 11 | twitter: username, 12 | linkedin: username, 13 | github: 'kumarabhirup', 14 | youtube: `c/${username}`, 15 | dev: username, 16 | facebook: username, 17 | instagram: `kumarethz`, 18 | tinyletter: 'kumar_abhirup', 19 | revue: username, 20 | twitch: username, 21 | }, 22 | description: '', 23 | thumbnail: 'https://i.ibb.co/NLZ8f5d/Kumar-Abhirup-Cover.png', // 'https://i.ibb.co/0JfTxD2/Kumar-Abhirup-kumareth.jpg', // 'https://i.ibb.co/MPWCPr1/Kumar-Abhirup.jpg' 24 | pageOgs: { 25 | uses: 'https://i.ibb.co/NLZ8f5d/Kumar-Abhirup-Cover.png', // 'https://i.ibb.co/xKvXWq3/Kumar-Abhirup-kumareth-uses.jpg', 26 | writings: 'https://i.ibb.co/NLZ8f5d/Kumar-Abhirup-Cover.png', // 'https://i.ibb.co/jhg75vm/Kumar-Abhirup-kumareth-writings-essays-blog.jpg', 27 | subscribe: 'https://i.ibb.co/NLZ8f5d/Kumar-Abhirup-Cover.png', // 'https://i.ibb.co/GVS6QBx/Kumar-Abhirup-kumareth-subscribe-to-newsletter.jpg', 28 | live: 'https://i.ibb.co/NLZ8f5d/Kumar-Abhirup-Cover.png', // 'https://i.ibb.co/sK71r4p/Kumar-Abhirup-kumareth-live.jpg', 29 | }, 30 | email: 'hey@kumareth.com', 31 | resume: 32 | 'https://github.com/kumarabhirup/resume/blob/master/Kumar%20Abhirup%20CV.pdf', 33 | githubUrl: 'kumarabhirup/personal-website', 34 | discordLink: 'https://discord.gg/CCjJ2Ndh4S', 35 | } 36 | 37 | module.exports = { 38 | META, 39 | } 40 | -------------------------------------------------------------------------------- /packages/web/constants/Uses.js: -------------------------------------------------------------------------------- 1 | export const USES = [ 2 | { 3 | title: 'Coding', 4 | stack: [ 5 | { 6 | name: 'VSCode', 7 | description: 'Best IDE in my opinion!', 8 | }, 9 | { 10 | name: 'VSCode Theme', 11 | description: `Shades of Purple`, 12 | }, 13 | { 14 | name: 'Hyper Terminal', 15 | description: `I use Shades Of Purple Hyper Theme`, 16 | }, 17 | { 18 | name: 'OhMyZsh, Zsh', 19 | description: `I have configured them to my needs`, 20 | }, 21 | { 22 | name: 'Google Chrome', 23 | description: 'Sorry Firefox, you left no choice :/', 24 | link: 'https://www.google.com/chrome/', 25 | }, 26 | { 27 | name: 'Carbon', 28 | description: 'To create code snippets', 29 | link: 'https://carbon.now.sh', 30 | }, 31 | ], 32 | }, 33 | { 34 | title: 'Gear', 35 | stack: [ 36 | { 37 | name: "MacBook Air '19", 38 | description: 'Love you Apple', 39 | link: 'https://www.apple.com/macbook-air', 40 | }, 41 | { 42 | name: 'Redmi Note 8 Pro', 43 | description: "No, I don't use iPhone.", 44 | }, 45 | { 46 | name: 'Lenovo Tablet', 47 | description: 'I mostly watch courses with it', 48 | }, 49 | ], 50 | }, 51 | { 52 | title: 'Extras', 53 | stack: [ 54 | { 55 | name: 'Kap', 56 | description: 'To record my screen', 57 | link: 'http://getkap.co/', 58 | }, 59 | { 60 | name: 'Keycastr', 61 | description: 'To log key bindings', 62 | link: 'https://github.com/keycastr/keycastr', 63 | }, 64 | ], 65 | }, 66 | ] 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kumar Abhirup 2 | 3 | [![Type](https://img.shields.io/badge/type-monorepo-yellow.svg?style=flat-square)](https://github.com/KumarAbhirup/personal-website) 4 | [![emoji-log](https://cdn.jsdelivr.net/gh/ahmadawais/stuff@ca978741836412b5e33ce8561f5f95c933177067/emoji-log/flat.svg)](https://github.com/KumarAbhirup/Emoji-Log/) 5 | [![Twitter](https://img.shields.io/twitter/follow/kumar_abhirup.svg?style=social&label=@kumar_abhirup)](https://twitter.com/kumar_abhirup/) 6 | 7 | Open Sourced 8 | 9 | ## 📦 Setup 10 | 11 | ### 🖥️ Development environment 12 | 13 | - Run 14 | 15 | ```bash 16 | $ git clone https://github.com/KumarAbhirup/personal-website # to clone project 17 | $ cd personal-website # enter in the project 18 | $ yarn # install modules 19 | $ yarn dev # run development server 20 | ``` 21 | 22 | - Rename `packages/web/.env.example` and `packages/backend/.env.example` to `.env`. 23 | 24 | - Visit `http://localhost:3001/` 25 | 26 | ### ⚒️ Linting 27 | 28 | #### In VSCode 29 | 30 | - Install ESLint and Prettier VSCode extensions. 31 | - **Done! Now you have live linting and autofixing setup!** 32 | 33 | #### In Any other IDE 34 | 35 | - Run `yarn lint` to check for linting errors. 36 | - Run `yarn lint:fix` to fix the linting errors. 37 | 38 | ## 🦄 Info 39 | 40 | - The Lerna monorepo setup was put up by [Harshit Pant](https://twitter.com/pantharshit00). 41 | - To customize the linter, use `.eslintrc` and `.prettierrc` file. [Learn more](https://eslint.org) 42 | 43 | ## 📝 License 44 | 45 | **MIT - Source code by [Kumar Abhirup](https://kumar.now.sh)** 46 | 47 | _Follow me 👋 **on Twitter**_ → [![Twitter](https://img.shields.io/twitter/follow/kumar_abhirup.svg?style=social&label=@kumar_abhirup)](https://twitter.com/kumar_abhirup/) 48 | -------------------------------------------------------------------------------- /packages/web/pages/subscribe/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Row, Col } from 'react-flexbox-grid' 3 | 4 | import Layout from '../../components/Layout' 5 | import Subscribe from '../../components/Subscribe' 6 | import { META } from '../../constants/metadata' 7 | 8 | export function DiscordInviteBox() { 9 | return ( 10 |
21 | I have a Discord server called{' '} 22 | 27 | kumar & friends{' '} 28 | 29 | .It 's a fun community of learners, that I am building ✌️ It' s super 30 | small as of now, glad to have you in ! 31 |
32 | ) 33 | } 34 | 35 | function SubscribePage({ noColorModeChange, noLiveShow }) { 36 | return ( 37 | <> 38 | 39 | 40 | 41 | {' '} 42 | {/*
*/} {/*
*/} 43 |
44 |
45 | 46 | {' '} 47 |
{' '} 48 |
{' '} 49 | 50 | ) 51 | } 52 | 53 | SubscribePage.getInitialProps = () => ({ 54 | data: { 55 | title: `Subscribe to Kumar Abhirup's Newsletter`, 56 | og: { 57 | image: META.pageOgs.subscribe, 58 | }, 59 | }, 60 | }) 61 | 62 | export default SubscribePage 63 | -------------------------------------------------------------------------------- /packages/web/scripts/generateSitemap.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | // eslint-disable-next-line import/no-extraneous-dependencies 4 | const globby = require('globby') 5 | 6 | // eslint-disable-next-line import/no-extraneous-dependencies 7 | const prettier = require('prettier') 8 | 9 | // eslint-disable-next-line import/newline-after-import 10 | ;(async () => { 11 | const prettierConfig = await prettier.resolveConfig('./.prettierrc.js') 12 | 13 | // Ignore Next.js specific files (e.g., _app.js) and API routes. 14 | const pages = await globby([ 15 | 'pages/**/*{.js,.mdx,.jsx}', 16 | 'writings/**/*{.mdx,.md}', 17 | '!pages/subscribe/thanks{.js,.jsx}', 18 | '!writings/drafts', 19 | '!pages/_*.js', 20 | '!pages/api', 21 | '!pages/writings/[slug]{.js,.jsx}', 22 | ]) 23 | 24 | const sitemap = ` 25 | 26 | 27 | ${pages 28 | .map(page => { 29 | const path = page 30 | .replace('pages/', '') 31 | .replace('.js', '') 32 | .replace('.jsx', '') 33 | .replace('.mdx', '') 34 | .replace('.md', '') 35 | 36 | const route = path.endsWith('index') 37 | ? path.replace('index', '') 38 | : path 39 | 40 | return ` 41 | 42 | ${`https://kumareth.com/${route}`} 43 | 44 | ` 45 | }) 46 | .join('')} 47 | 48 | ` 49 | 50 | // If you're not using Prettier, you can remove this. 51 | const formatted = prettier.format(sitemap, { 52 | ...prettierConfig, 53 | parser: 'html', 54 | }) 55 | 56 | fs.writeFileSync('public/sitemap.xml', formatted) 57 | })() 58 | -------------------------------------------------------------------------------- /packages/web/archived-writings/my-first-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "My first post, on my new site!" 3 | date: "2020-05-03" 4 | og: 5 | description: "I rebuilt my website. No, not from scratch. Here's what I did." 6 | image: "https://i.ibb.co/QF3Hhv5/my-first-post.jpg" 7 | author: 8 | twitter: "kumar_abhirup" 9 | name: "Kumar Abhirup" 10 | --- 11 | 12 | My old portfolio website looked like this 👇 13 | 14 | ![Kumar's Old Website](https://camo.githubusercontent.com/8754a779546e3c97109c4d15b0c2ca0fcf42a537/68747470733a2f2f692e6962622e636f2f6d53536b64674b2f4b756d61722d416268697275702e6a7067) 15 | 16 | Yea, I see it looks nice and cool, but I had to change it all. 17 | 18 | That website was bloated with a combination of jQuery and React 😂 Can you believe it? 19 | 20 | Also, it didn't have a blog. I tried adding it there, but it was a failed attempt. 21 | 22 | I faced weird bugs like background image not loading and stuff. 23 | 24 | > **Confession:** The previous website's code was created using mbrise page builder. I just took the code and React-ified it. And so, fixing stuff was hard. I didn't even know jQuery. 25 | 26 | I didn't have the time to rebuild my portfolio website. I was creating a side-project. 27 | 28 | I was looking for open source code already available. 29 | 30 | # One fine day 31 | 32 | [Telmo](http://telmo.online/) open sourced his awesome website. I am really thankful he did it. 33 | 34 | He used the below stack. 35 | 36 | - Next.js 37 | - Markdown 38 | 39 | That's it. It was easy, minimal, supported dark-mode, and was more content focused. Exactly what I wanted. 40 | 41 | I used that open source project in my website. You'll see many similarities in his and my website. 42 | 43 | --- 44 | 45 | # Final Thoughts 46 | 47 | From now on, I am gonna post my content on this fine good-looking website. 48 | 49 | If you like what I tweet, you'll surely like what I email you. So, make sure you subscribe for updates [here](https://tinyletter.com/kumar_abhirup/subscribe). 50 | -------------------------------------------------------------------------------- /packages/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "web", 4 | "version": "1.0.0", 5 | "license": "MIT", 6 | "main": "index.js", 7 | "scripts": { 8 | "dev": "NODE_OPTIONS='--openssl-legacy-provider' NODE_ENV=development next -p 3002", 9 | "start": "NODE_ENV=production next start", 10 | "build": "NODE_ENV=production next build", 11 | "lint": "eslint . --ext .js", 12 | "lint:fix": "eslint --ext .js --fix", 13 | "test": "NODE_ENV=test jest --passWithNoTests --watch", 14 | "test:ci": "NODE_ENV=test jest --passWithNoTests" 15 | }, 16 | "dependencies": { 17 | "@zeit/next-css": "^1.0.1", 18 | "axios": "^0.19.2", 19 | "dotenv": "^8.2.0", 20 | "gray-matter": "^4.0.2", 21 | "next": "^10.0.5", 22 | "next-transpile-modules": "^2.3.1", 23 | "raw-loader": "^4.0.1", 24 | "react": "^17.0.1", 25 | "react-copy-to-clipboard": "^5.0.2", 26 | "react-dom": "^17.0.1", 27 | "react-feather": "^2.0.4", 28 | "react-flexbox-grid": "^2.1.2", 29 | "react-grid-gallery": "^1.0.1-alpha.0", 30 | "react-markdown": "^4.3.1", 31 | "react-scroll-progress-bar": "^1.1.12", 32 | "react-spring": "^8.0.27", 33 | "react-syntax-highlighter": "^12.2.1", 34 | "react-text-transition": "^1.0.2", 35 | "react-twitch-embed-video": "^2.0.2", 36 | "reading-time": "^1.2.0", 37 | "simple-icons": "2.9.0", 38 | "styled-components": "^5.1.0" 39 | }, 40 | "devDependencies": { 41 | "babel-eslint": "^10.0.3", 42 | "eslint": "^6.3.0", 43 | "eslint-config-airbnb": "^18.0.1", 44 | "eslint-config-prettier": "^6.2.0", 45 | "eslint-config-wesbos": "0.0.19", 46 | "eslint-plugin-html": "^6.0.0", 47 | "eslint-plugin-import": "^2.16.0", 48 | "eslint-plugin-jsx-a11y": "^6.2.1", 49 | "eslint-plugin-prettier": "^3.0.1", 50 | "eslint-plugin-react": "^7.12.4", 51 | "eslint-plugin-react-hooks": "^2.0.1", 52 | "globby": "^11.0.0", 53 | "html-webpack-plugin": "^5.2.0", 54 | "jest": "^24.9.0", 55 | "prettier": "^1.16.4", 56 | "webpack": "^4.0.0", 57 | "webpack-cli": "^4.5.0", 58 | "webpack-dev-middleware": "^4.1.0", 59 | "webpack-dev-server": "^3.11.2" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/web/pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document, { Head, Main, NextScript, Html } from 'next/document' 2 | import { ServerStyleSheet } from 'styled-components' 3 | 4 | // To render styles on the server-side (for styled-components) 5 | class MyDocument extends Document { 6 | static getInitialProps({ renderPage }) { 7 | const isProduction = process.env.NODE_ENV === 'production' 8 | 9 | const sheet = new ServerStyleSheet() 10 | const page = renderPage(App => props => 11 | sheet.collectStyles() 12 | ) 13 | const styleTags = sheet.getStyleElement() 14 | return { ...page, styleTags, isProduction } 15 | } 16 | 17 | setGoogleTags = () => ({ 18 | __html: ` 19 | window.dataLayer = window.dataLayer || []; 20 | function gtag(){dataLayer.push(arguments);} 21 | gtag('js', new Date()); 22 | gtag('config', '${process.env.GA_TRACKING_ID}'); 23 | `, 24 | }) 25 | 26 | render() { 27 | return ( 28 | 29 | {this.props.styleTags} 30 | 31 | 32 | 47 | 48 |
49 | 50 | 51 | {this.props.isProduction && ( 52 | <> 53 | 73 | 74 | {title || META.title} 75 | 76 | 77 | 78 | 82 | 83 | 84 | 85 | 86 | ) 87 | } 88 | 89 | export default MyApp 90 | -------------------------------------------------------------------------------- /packages/web/next.config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | const withCSS = require('@zeit/next-css') 4 | const withTM = require('next-transpile-modules') 5 | const { META } = require('./constants/metadata') 6 | 7 | module.exports = withCSS( 8 | withTM({ 9 | transpileModules: ['react-flexbox-grid', 'react-syntax-highlighter'], 10 | webpack(config, { isServer }) { 11 | if (isServer) { 12 | // eslint-disable-next-line global-require 13 | require('./scripts/generateSitemap') 14 | } 15 | 16 | config.module.rules.push({ 17 | test: /\.md$/, 18 | use: 'raw-loader', 19 | }) 20 | 21 | return config 22 | }, 23 | target: 'serverless', 24 | env: { 25 | RECAPTCHA_CLIENT_KEY: process.env.RECAPTCHA_CLIENT_KEY, 26 | GA_TRACKING_ID: process.env.GA_TRACKING_ID, 27 | TWITCH_CLIENT_ID: process.env.TWITCH_CLIENT_ID, 28 | TWITCH_OAUTH_ID: process.env.TWITCH_OAUTH_ID, 29 | KUMAR_BACKEND_URL: process.env.KUMAR_BACKEND_URL, 30 | }, 31 | async redirects() { 32 | return [ 33 | { 34 | source: '/twitter', 35 | destination: `https://twitter.com/${META.social.twitter}`, 36 | permanent: true, 37 | }, 38 | { 39 | source: '/linkedin', 40 | destination: `https://linkedin.com/in/${META.social.linkedin}`, 41 | permanent: true, 42 | }, 43 | { 44 | source: '/instagram', 45 | destination: `https://instagram.com/${META.social.instagram}`, 46 | permanent: true, 47 | }, 48 | { 49 | source: '/youtube', 50 | destination: `https://youtube.com/${META.social.youtube}`, 51 | permanent: true, 52 | }, 53 | { 54 | source: '/github', 55 | destination: `https://github.com/${META.social.github}`, 56 | permanent: true, 57 | }, 58 | { 59 | source: '/twitch', 60 | destination: `https://twitch.tv/${META.social.twitch}`, 61 | permanent: true, 62 | }, 63 | { 64 | source: '/facebook', 65 | destination: `https://facebook.com/${META.social.facebook}`, 66 | permanent: true, 67 | }, 68 | { 69 | source: '/resume', 70 | destination: `${META.resume}`, 71 | permanent: true, 72 | }, 73 | { 74 | source: '/email/:body', 75 | destination: `mailto:${META.email}?body=:body`, 76 | permanent: true, 77 | }, 78 | { 79 | source: '/email', 80 | destination: `mailto:${META.email}`, 81 | permanent: true, 82 | }, 83 | { 84 | source: '/devto', 85 | destination: `https://dev.to/${META.social.dev}`, 86 | permanent: true, 87 | }, 88 | { 89 | source: '/discord', 90 | destination: `${META.discordLink}`, 91 | permanent: true, 92 | }, 93 | { 94 | source: '/schedule', 95 | destination: `https://cal.com/kumareth`, 96 | permanent: true, 97 | }, 98 | { 99 | source: '/calendly', 100 | destination: `https://cal.com/kumareth/chat`, 101 | permanent: true, 102 | }, 103 | ] 104 | }, 105 | }) 106 | ) 107 | -------------------------------------------------------------------------------- /packages/web/archived-writings/how-to-rate-limit-your-express-api-endpoints.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "How to Rate Limit your Express API Endpoints" 3 | date: "2021-01-16" 4 | og: 5 | description: "Rate Limit your API routes with IP Addresses or User Identities using the express-limiter module." 6 | image: "https://images.unsplash.com/photo-1517697063925-d82f8da41135?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=2378&q=80" 7 | author: 8 | twitter: "kumar_abhirup" 9 | name: "Kumar Abhirup" 10 | --- 11 | 12 | > The featured image of this article is from [Unsplash](https://images.unsplash.com/photo-1517697063925-d82f8da41135?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=2378&q=80). 13 | 14 | This won't be a long tutorial, but a small, very self explanatory code snippet, that you can easily read and learn by yourself. 15 | 16 | ![Rate Limit your Express API Endpoints](https://i.ibb.co/b10C3rz/rate-limit-your-endpoints.png) 17 | 18 | # 🚶‍♂️ Quick Walkthrough 19 | 20 | ### Step 1 21 | 22 | Install `express`, `express-limiter` and also install Redis to [set up a Redis Client](https://docs.redislabs.com/latest/rs/references/client_references/client_ioredis/). Redis Client will be created using `ioredis`. 23 | 24 | Why do you need Redis? Here's why 👇 25 | 26 | 27 | 28 |
29 | 30 | ### Step 2 31 | 32 | Import express and create an express app instance. Now, create a limiter instance. We will use `express-limiter` for this. 33 | 34 | ```js 35 | const limiter = expressLimiter(app, redisClient) 36 | ``` 37 | 38 | Make sure you spawned a Redis Client, [read how to do that here](https://docs.redislabs.com/latest/rs/references/client_references/client_ioredis/). 39 | 40 | This limiter instance will now help us create a `limiterForApi` to define Rate Limit rules to use it for your routes as a middleware. 41 | 42 | ### Step 3 43 | 44 | Define the rules. Create the middleware. 45 | 46 | ```js 47 | const limiterForApi = limiter({ 48 | onRateLimited(_req, res) { 49 | return res.status(429).send({ error: { message: "Rate limit exceeded" } }) 50 | }, 51 | 52 | total: 200, 53 | expire: 1000 * 60 * 60, 54 | lookup: ["headers.data.user._id"] 55 | }) 56 | ``` 57 | 58 | The above middleware sets the API rate limit at 200 requests per hour per User ID. If you want to rate limit per IP Address, you can add the IP Address header name in the `lookup` array. 59 | 60 | ### Step 4 61 | 62 | Use the middleware! 63 | 64 | ## Here's the complete code snippet 65 | 66 | If you read the code comments, you will get a fair idea of how things work! 67 | 68 | ![Rate Limit your Express API Endpoints](https://i.ibb.co/b10C3rz/rate-limit-your-endpoints.png) 69 | 70 | For more details and complexities, take a look at the [`express-limiter` documentation](http://npmjs.com/package/express-limiter). It's very neat. 71 | -------------------------------------------------------------------------------- /packages/web/pages/writings.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import matter from 'gray-matter' 3 | import Link from 'next/link' 4 | import { Row, Col } from 'react-flexbox-grid' 5 | 6 | import Layout from '../components/Layout' 7 | import { META } from '../constants/metadata' 8 | 9 | export function formatDate(date) { 10 | const options = { year: 'numeric', month: 'long', day: 'numeric' } 11 | const today = new Date(date) 12 | 13 | return today.toLocaleDateString('en-US', options) 14 | } 15 | 16 | function Homepage({ writings, og }) { 17 | return ( 18 | <> 19 | 20 | 21 | {writings.map(({ document, slug }) => { 22 | const { 23 | data: { 24 | title, 25 | date, 26 | og: { image }, 27 | }, 28 | } = document 29 | 30 | return ( 31 | 32 |
33 | 34 | 35 |
{formatDate(date)}
36 | 37 | 38 | {image && ( 39 | 40 | 41 | 42 | {title} 48 | 49 | 50 | 51 | )} 52 | 53 | 54 | 55 | 56 | {title} 57 | 58 | 59 | 60 |
61 |
62 | 63 | ) 64 | })} 65 |
66 |
67 | 68 | ) 69 | } 70 | 71 | Homepage.getInitialProps = async context => { 72 | // eslint-disable-next-line no-shadow 73 | const writings = (context => { 74 | const keys = context.keys() 75 | const values = keys.map(context) 76 | const data = keys.map((key, index) => { 77 | const slug = key 78 | // eslint-disable-next-line no-useless-escape 79 | .replace(/^.*[\\\/]/, '') 80 | .split('.') 81 | .slice(0, -1) 82 | .join('.') 83 | const value = values[index] 84 | const document = matter(value.default) 85 | return { document, slug } 86 | }) 87 | 88 | return data 89 | .slice() 90 | .sort( 91 | (a, b) => 92 | new Date(b.document.data.date) - new Date(a.document.data.date) 93 | ) 94 | .filter( 95 | post => 96 | post.document.data.isDraft === undefined || 97 | post.document.data.isDraft === false || 98 | post.document.data.isDraft === null 99 | ) 100 | })(require.context('../writings', true, /\.md$/)) 101 | 102 | return { 103 | writings, 104 | data: { 105 | title: 'Kumar Abhirup Essays', 106 | og: { 107 | image: META.pageOgs.writings, 108 | }, 109 | }, 110 | } 111 | } 112 | export default Homepage 113 | -------------------------------------------------------------------------------- /packages/web/archived-writings/pkgreview-dev-ratings-review-website-npm-packages.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "pkgreview.dev - The Ratings and Review Website for NPM Packages 🎉" 3 | date: "2020-05-06" 4 | og: 5 | description: "I am very thrilled to announce the side-project I have been working on since last week..." 6 | image: "https://res.cloudinary.com/practicaldev/image/fetch/s--6XoqW46m--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/i/cj694wkr77z93hijbdjj.jpg" 7 | imageTypeOnArticle: "NORMAL" 8 | author: 9 | twitter: "kumar_abhirup" 10 | name: "Kumar Abhirup" 11 | --- 12 | 13 | Hey all! 🙌 14 | 15 | I am very thrilled to announce the side-project I have been working on since last week... 16 | 17 | I named it **[pkgreview.dev](https://pkgreview.dev)**! The complete product was born out of one tweet. 18 | 19 |
20 | 21 | --- 22 | 23 | # 🎊 pkgreview.dev 24 | 25 | A lot of people find it very difficult to decide what NPM Package to use. 26 | 27 | There comes pkgreview.dev to your rescue! 28 | 29 | 📦 0.3 Million NPM Packages with GitHub Stats listed! 30 | 31 | 🎉 Helps analyze NPM Packages 32 | 33 | 🚨 Write & Read Reviews 34 | 35 | ⭐️ Star Ratings like _rottentomatoes_! 36 | 37 | ![React @ pkgreview.dev](https://ph-files.imgix.net/7dc59a54-0d54-4804-8c9d-301482cafb0a?auto=format&auto=compress&codec=mozjpeg&cs=strip&w=582.330383480826&h=380&fit=max&dpr=2) 38 | 39 | --- 40 | 41 | # 🕹 The idea 42 | 43 | A lot of people find it very difficult to decide what NPM Package, React Component, Module or Library to use. 44 | 45 | **They end up checking on GitHub** to see how active the community is. GitHub does not tell you a clear picture. It is just a part of the picture. 46 | 47 | **What people often miss**, is the first-hand experience of the users who have used the library. 48 | 49 | `pkgreview.dev` displays all sorts of important metadata of a package (_reviews, ratings, last updated, stargazers, etc._) and also shows `other developers' opinion` about them. 50 | 51 | And you can **contribute by posting your review** about the NPM Package. 🎉 52 | 53 | # 🔥 Building a Community 54 | 55 | The app that you are currently seeing is in its MVP phase. 56 | 57 | **To be useful to the masses, we need more reviews.** You, as a developer, can contribute a lot in this process by just posting a review to any useful package. 58 | 59 | This project needs the support of the **JavaScript Community**. 60 | 61 | **[Login with GitHub (takes two seconds) and write your first review!](https://pkgreview.dev/npm/react)** 62 | 63 | --- 64 | 65 | **This is my first fast-shipped side project. Please do give me feedback on it! 🙌** This is just a start. 66 | 67 | # In my Todolist 68 | 69 | - Add support for other package managers like PHP Composer, Docker Hub for Images, PIP for Python, and much more! 70 | 71 | - Give maintainers of the package special permissions like replying to the review or reporting it. 72 | 73 | - Use AI for creating reviews by reading about the package on the web (eg. GitHub issues, Blogposts, etc.) 74 | 75 | - Make UI better and less gimmicky. 76 | 77 | --- 78 | 79 | I will be able to achieve the goals in the todo list only by your support. 80 | 81 | I love positive as well as critical feedback 🙌 82 | -------------------------------------------------------------------------------- /packages/web/archived-writings/starter-guide-create-command-line-app-node-js.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "A starter guide to create a command-line app in Node.js" 3 | date: "2020-05-10" 4 | og: 5 | description: "Many developers use command-line apps on a daily basis. Today, we will learn how to create Command-Line Apps in Node." 6 | image: "https://i.ibb.co/s11JCZq/thumbnails-001.png" 7 | imageTypeOnArticle: "NORMAL" 8 | author: 9 | twitter: "kumar_abhirup" 10 | name: "Kumar Abhirup" 11 | --- 12 | 13 | Many developers use command-line apps on a daily basis. 14 | 15 | From git to yarn, we use many CLI (Command Line Interface) apps. Not everyone likes CLI over GUI, but many geeks love using CLIs. 16 | 17 | Today, we will learn how to create Command-Line Apps in Node.js 18 | 19 | Command-Line apps can be written in many languages. If you write them in Node.js, you can serve your app to the NPM/Yarn users. 20 | 21 | # Assumptions 22 | 23 | - You know how to install NPM packages. 24 | - You know the basics of Node.js 25 | - You know basic terminal commands like `cd`, `pwd`, `ll`, `ls`. 26 | 27 | # Get Started 28 | 29 | - `cd` to the repository where you store all your code. 30 | 31 | - Do the following... 32 | 33 | ```bash 34 | mkdir demo-cli && cd demo-cli 35 | touch index.js 36 | npm init -y 37 | ``` 38 | 39 | - Open the `demo-cli` folder in your favorite code editor. 40 | 41 | ## index.js 42 | 43 | CLI apps are all about input and output. In this Starter Guide, we won't go deep into creating a CLI that does something important. 44 | 45 | For now, we will only make use of `chalk` and `figlet` to do make some creative output. 46 | 47 | Run the following command to install needed packages... 48 | 49 | ```bash 50 | npm i chalk figlet clear -s 51 | ``` 52 | 53 | And then, just paste the following snippet in your `index.js`. 54 | 55 | ```js 56 | #!/usr/bin/env node 57 | 58 | const clear = require("clear") 59 | const chalk = require("chalk") 60 | const figlet = require("figlet") 61 | 62 | clear() 63 | 64 | console.log( 65 | chalk.yellow.bold( 66 | figlet.textSync("CLI!", { 67 | horizontalLayout: "full" 68 | }) 69 | ) 70 | ) 71 | ``` 72 | 73 | The `#!/usr/bin/env node` line needs to be at the top for a Command Line App to work. 74 | 75 | ## package.json 76 | 77 | Add a Start Script in your `package.json`... 78 | 79 | ```json 80 | { 81 | "scripts": { 82 | "start": "node index.js" 83 | } 84 | } 85 | ``` 86 | 87 | Also, add a `bin` script. That script decides what command will user need to type to see the output. 88 | 89 | ```json 90 | { 91 | "bin": { 92 | "thisDemoCli": "index.js" 93 | } 94 | } 95 | ``` 96 | 97 | # See the output 98 | 99 | Run `npm start` to see how the output looks like. Isn't it just like running a Node app? 100 | 101 | Okay, now, do this... 102 | 103 | ```bash 104 | pwd # Copy the output you get 105 | npm i -g 106 | ``` 107 | 108 | **And now try running `thisDemoCli` in the terminal, and you should see the same output!** 109 | 110 | Output... 111 | 112 | ``` 113 | / ___| | | |_ _| | | 114 | | | | | | | | | 115 | | |___ | |___ | | |_| 116 | \____| |_____| |___| (_) 117 | ``` 118 | 119 | # Publish to NPM 120 | 121 | To publish to NPM so that you can serve your apps to users, give your package a unique name, and provide it a version. 122 | 123 | And then run... 124 | 125 | ```bash 126 | npm publish 127 | ``` 128 | 129 | **That is it!** 130 | 131 | # 🦄 More info 132 | 133 | This tutorial only helps you create a naive command-line app. Real command-line apps have a lot going on. Like [the one I created](https://github.com/KumarAbhirup/bulk-mail-cli). 134 | 135 | Make use of the `commander` NPM module to make the CLI do different tasks on different flags like `--help`, `--version`, etc. 136 | 137 | Here's the [Part Two of the Series - Creating a command-line app in Node.js: Argument Parsing](https://dev.to/kumar_abhirup/a-starter-guide-to-creating-a-command-line-app-in-node-js-part-2-3ln3). 138 | -------------------------------------------------------------------------------- /packages/web/archived-writings/five-package-json-scripts-you-dont-use.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "5 package.json magic scripts that you don't use!" 3 | date: "2020-05-07" 4 | og: 5 | description: "There are many magic scripts available for use by the Node Package Manager ecosystem, that beginners yet don't use." 6 | image: "https://res.cloudinary.com/practicaldev/image/fetch/s--WMWUiYbE--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://res.cloudinary.com/practicaldev/image/fetch/s--PLhINg-k--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://thepracticaldev.s3.amazonaws.com/i/5iy792219yylpm6chzf9.png" 7 | imageTypeOnArticle: "NORMAL" 8 | author: 9 | twitter: "kumar_abhirup" 10 | name: "Kumar Abhirup" 11 | --- 12 | 13 | There are many magic scripts available for use by the Node Package Manager ecosystem, that beginners yet don't use. 14 | 15 | When I wanted to publish a package, I would manually bump the version, build the project, and then run `npm publish` to ship the package. Which by itself took a lot of time. 16 | 17 | But then, I read [this documentation](https://docs.npmjs.com/misc/scripts) by npm and realized that all the processes can be automated and can be done in just one command. 18 | 19 | The documentation has a lot going on, so in this DEV post, I'll try to 20 | demystify the 5 most important package.json scripts using the documentation as a reference. 21 | 22 | # Let us begin 23 | 24 | ## 1. prepublish 25 | 26 | ```json 27 | "scripts": { 28 | "prepublish": "minify or build your code here", 29 | } 30 | ``` 31 | 32 | _This command is run BEFORE the package is packed and published. This command also runs when user runs `npm i` locally without any parameters and arguments._ 33 | 34 | **From the NPM Docs:** 35 | 36 | > If you need to perform operations on your package before it is used, in a way that is not dependent on the operating system or architecture of the target system, use a prepublish script. 37 | 38 | Prepublish script includes tasks such as: 39 | 40 | - Compiling CoffeeScript source code into JavaScript. 41 | - Creating minified versions of JavaScript source code. 42 | - Fetching remote resources that your package will use. 43 | 44 | The advantage of doing these things at prepublish time is that they can be done once, in a single place, thus reducing complexity and variability. 45 | 46 | Additionally, this means that: 47 | 48 | - You can depend on `coffee-script` as a `devDependency`, and thus your users don’t need to have it installed. 49 | - You don’t need to include minifiers in your package, reducing the size for your users. 50 | - You don’t need to rely on your users having `curl` or `wget` or other system tools on the target machines. 51 | 52 | ## 2. prepare 53 | 54 | There is a little difference between `prepare` and `prepublish`... 55 | 56 | _`prepare` script runs when `git` dependencies are being installed. This script runs after `prepublish` and before `prepublishOnly`._ 57 | 58 | Example 👇 59 | 60 | ```json 61 | "scripts": { 62 | "build": "rollup -c", 63 | "prepare": "npm run build" 64 | } 65 | ``` 66 | 67 | Building the project could be the best thing you can execute in the `prepare` script. 68 | 69 | ## 3. prepublishOnly 70 | 71 | This command serves the same purpose as `prepublish` and `prepare` but runs only on `npm publish`! 🔥 72 | 73 | ## 4. postpublish 74 | 75 | As the name suggests, the command runs after `npm publish`... 76 | 77 | ## 5. Custom `pre`ing and `post`ing of scripts 78 | 79 | Take a look at the below scripts. 80 | 81 | ``` 82 | "scripts": { 83 | "predeploy": "cd example && npm install && npm run build", 84 | "deploy": "gh-pages -d example/build" 85 | } 86 | ``` 87 | 88 | To execute `deploy` completely, you don't need to `npm run predeploy && npm run deploy`, just running `npm run deploy` will do the `pre` and `post` task. 89 | 90 | You can add the `pre` and `post` prefixes to any script and have it run chronologically. 91 | 92 | # And there's much more 93 | 94 | - preinstall 95 | - postinstall 96 | - preuninstall 97 | - postuninstall 98 | - preversion 99 | - postversion 100 | - prestart 101 | - poststart 102 | 103 | The names are pretty self-explanatory. 104 | 105 | To read more about these, you can refer the [NPM Docs](https://docs.npmjs.com/misc/scripts) about `npm-scripts`. 106 | 107 | # Conclusion 108 | 109 | The NPM Magic Scripts can prove useful to anyone and everyone. I regret not using it for my past projects. 😅 110 | -------------------------------------------------------------------------------- /packages/web/pages/writings/[slug]/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/destructuring-assignment */ 2 | 3 | import React from 'react' 4 | import matter from 'gray-matter' 5 | import ReactMarkdown from 'react-markdown' 6 | import Link from 'next/link' 7 | import ProgressBar from 'react-scroll-progress-bar' 8 | import readingTime from 'reading-time' 9 | 10 | import { ELEMENTS } from '../../../constants/Meta' 11 | import { META } from '../../../constants/metadata' 12 | import Layout from '../../../components/Layout' 13 | import CodeBlock from '../../../components/CodeBlock' 14 | import Subscribe from '../../../components/Subscribe' 15 | import { DiscordInviteBox } from '../../subscribe' 16 | import { formatDate } from '../../writings' 17 | 18 | function Writing({ content, data, slug }) { 19 | const frontmatter = data 20 | const { title, author, og, date } = frontmatter 21 | 22 | const imageTypeOnArticle = 23 | og?.imageTypeOnArticle === null || og?.imageTypeOnArticle === undefined 24 | ? 'LEADERBOARD' 25 | : og?.imageTypeOnArticle 26 | 27 | const avatar = `https://images.weserv.nl/?url=https://unavatar.io/twitter/${author.twitter}&w=40` 28 | 29 | return ( 30 | <> 31 |
32 | 33 |
34 | 35 | {og.image && 36 | !og?.noImageOnArticle && 37 | imageTypeOnArticle === 'LEADERBOARD' && ( 38 | {title} 39 | )} 40 | 41 | 42 |
43 |
44 | 45 | back 46 | 47 | 48 | 54 | edit this article 55 | 56 |
57 | 58 | {og.image && 59 | !og?.noImageOnArticle && 60 | imageTypeOnArticle === 'NORMAL' && ( 61 | <> 62 |
63 |
64 |
65 | {title} 66 | 67 | )} 68 | 69 |

{title}

70 | 71 | 82 | 83 |
84 | { 96 | if (!props.href.startsWith('http')) { 97 | return props.href 98 | } 99 | 100 | return ( 101 | 106 | {props.children} 107 | 108 | ) 109 | }, 110 | }} 111 | /> 112 | 113 |
114 | 115 | {/*
*/} 116 | 117 | {/*
*/} 118 | 119 |
120 |
121 | 122 | 123 |
124 |
125 |
126 | 127 | ) 128 | } 129 | 130 | Writing.getInitialProps = async context => { 131 | const { slug } = context.query 132 | const content = await import(`../../../writings/${slug}.md`) 133 | const data = matter(content.default) 134 | 135 | return { ...data, slug } 136 | } 137 | 138 | export default Writing 139 | -------------------------------------------------------------------------------- /packages/web/components/Layout.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import simpleIcons from 'simple-icons' 3 | import { Grid, Row, Col } from 'react-flexbox-grid' 4 | import { Sun, Moon } from 'react-feather' 5 | import Link from 'next/link' 6 | import dynamic from 'next/dynamic' 7 | 8 | import { ELEMENTS } from '../constants/Meta' 9 | import { META } from '../constants/metadata' 10 | import useTwitchIsLive from '../hooks/useTwitchIsLive' 11 | import GridBackground from './grid-background' 12 | 13 | const ReactTwitchEmbedVideo = dynamic( 14 | () => import('react-twitch-embed-video'), 15 | { ssr: false } 16 | ) 17 | 18 | export const Icon = ({ stack, style }) => { 19 | const icon = simpleIcons.get(stack) 20 | 21 | return ( 22 |
33 | ) 34 | } 35 | 36 | const menu = [ 37 | { 38 | path: '/', 39 | name: 'start', 40 | }, 41 | { 42 | path: '/writings', 43 | name: 'essays', 44 | }, 45 | // { path: META.discordLink, name: 'discord', newTab: true }, 46 | // { 47 | // path: '/subscribe', 48 | // name: 'subscribe', 49 | // }, 50 | ] 51 | 52 | function Layout({ 53 | children, 54 | isHomepage, 55 | secondaryPage, 56 | pageTitle, 57 | noColorModeChange = false, 58 | noLiveShow, 59 | noHead = false, 60 | }) { 61 | const onLoadTheme = 62 | typeof localStorage !== 'undefined' && localStorage.getItem('BLOG_THEME') 63 | 64 | const [theme, setTheme] = useState(onLoadTheme) 65 | 66 | const [mounted, setMounted] = useState(false) 67 | 68 | const switchTheme = () => { 69 | const setTo = theme === 'dark' ? 'light' : 'dark' 70 | 71 | setTheme(setTo) 72 | } 73 | 74 | useEffect(() => { 75 | // if (window.matchMedia('(prefers-color-scheme: dark)').matches) { 76 | // setTheme('dark') 77 | // } 78 | 79 | // always light mode 80 | setTheme('light') 81 | setMounted(true) 82 | }, []) 83 | 84 | useEffect(() => { 85 | document.documentElement.setAttribute('data-theme', theme) 86 | 87 | localStorage.setItem('BLOG_THEME', theme) 88 | }, [theme]) 89 | 90 | const isLive = useTwitchIsLive() 91 | 92 | const containerProps = { 93 | ...(isHomepage && { md: 12 }), 94 | ...(!isHomepage && { md: 8, mdOffset: 2 }), 95 | } 96 | 97 | // if (!mounted) return
98 | 99 | return ( 100 | <> 101 | {isLive && !noLiveShow && ( 102 | 110 | )} 111 | 112 | {/* Avatar */} 120 | 121 | 122 | 123 |
124 | 125 | 134 |
    135 | {menu.map(({ path, name, newTab }) => ( 136 |
  • 137 | 138 | {name} 139 | 140 |
  • 141 | ))} 142 |
143 | 144 |
145 |
146 | 147 | 148 | 149 | 150 | {!secondaryPage && ( 151 |

155 | {pageTitle || ELEMENTS.mainText} 156 |

157 | )} 158 | 159 | {children} 160 | 161 |
162 |
163 | 164 |
165 | Follow me on{' '} 166 | Twitter, 167 | that's where I usually hangout. 168 |
169 | 170 | ) 171 | } 172 | 173 | export default Layout 174 | -------------------------------------------------------------------------------- /packages/web/constants/Meta.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from 'next/link' 3 | import { META } from './metadata' 4 | 5 | export const ELEMENTS = { 6 | mainText: ( 7 | <> 8 | Essays & Writings 9 | 10 | ), 11 | about: ( 12 | <> 13 | I founded my first company (Beam Community, Inc.) when I was 16, and 14 | successfully exited after running it for two years. 15 |
16 |
I am a 21yo Founder and Engineer, having built many projects in 17 | past, I am on a path aiming to solve humanities' most pressing issues, in 18 | an iterative fashion, starting from most trivial things imaginable. 19 | {/*
20 |
21 | Led a hackathon team in Korea Blockchain Week in August 2022, and won 2nd 22 | place at two hackathons for{' '} 23 | 28 | judiciary.app 29 | 30 | , spent three months in{' '} 31 | 36 | Nonce Korea hackerhouse 37 | {' '} 38 | in Seoul, South Korea. I am in the evergreen process of learning, and I am 39 | loving it! */} 40 |
41 |
— Co-founder of{' '} 42 | 43 | Dench.com (YC S24) 44 | 45 |
— Co-founder of{' '} 46 | 47 | Merse.co 48 | {' '} 49 | ( 50 | 55 | Business Insider 56 | 57 | ) 58 |
— Previously building{' '} 59 | 64 | Airchat (acquired) 65 | {' '} 66 | with Naval Ravikant. 67 |
— Founder{' '} 68 | 69 | itsbeam.com 70 | {' '} 71 | (acquired) 72 |
— Recipient of{' '} 73 | 78 | The O1 Visa 79 | {' '} 80 | 🇺🇸 81 |
—{' '} 82 | 87 | Nonce Korea 88 | {' '} 89 | 2022 Fellow 90 |
— Alumni of{' '} 91 | 96 | The First Residency Cohort 97 | {' '} 98 | in Berkeley, CA 99 |
— Judge at{' '} 100 | 105 | Stanford Treehacks 2024 106 | 107 |
— Winner of EthSeoul Hackathon 2022, Public Goods Track 108 | {/*
— Granted $5k by{' '} 109 | 114 | David Senra 115 | {' '} 116 | of Founders Podcast, to go to the major{' '} 117 | 122 | FoundersOnly 123 | {' '} 124 | conference in Austin, Texas. */} 125 |
126 |
I write{' '} 127 | 128 | here 129 | 130 | . 131 |
132 | {/*
133 |
134 | Currently, Software Engineer at{' '} 135 | 140 | Airchat 141 | {' '} 142 | ⚒️ */} 143 |
144 |
145 | {META.email}﹒{' '} 146 | {/* eslint-disable-next-line react/jsx-no-target-blank */} 147 | 152 | Twitter 153 | 154 | 155 | ), 156 | belowArticle: ` 157 | --- 158 | 159 | # Author 160 | 161 | I am Kumar Abhirup, a 21yo Founder and Engineer, having built many projects in past, I am on a path aiming to solve humanities' most pressing issues, in an iterative fashion, starting from most trivial things imaginable. To connect, you may DM me on [twitter](https://kumareth.com/twitter). 162 | 163 | _Like what I write? [Subscribe to **my newsletter**](https://kumareth.com/subscribe)._ 164 | `, 165 | } 166 | -------------------------------------------------------------------------------- /packages/web/components/CodeBlock.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/destructuring-assignment */ 2 | /* eslint-disable no-plusplus */ 3 | /* eslint-disable react/no-access-state-in-setstate */ 4 | 5 | import React, { PureComponent } from 'react' 6 | import SyntaxHighlighter from 'react-syntax-highlighter' 7 | import { anOldHope } from 'react-syntax-highlighter/dist/esm/styles/hljs' 8 | import { CopyToClipboard } from 'react-copy-to-clipboard' 9 | 10 | const preStyle = { 11 | borderRadius: 6, 12 | padding: '1.7em', 13 | lineHeight: '2.3em', 14 | } 15 | 16 | const codeProps = { 17 | style: { 18 | fontFamily: `ibm-plex-mono, Consolas, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New'`, 19 | fontSize: '18.5px', 20 | }, 21 | } 22 | 23 | class CodeBlock extends PureComponent { 24 | constructor(props) { 25 | super(props) 26 | 27 | this.state = { 28 | removeLines: [], 29 | addLines: [], 30 | updateLines: [], 31 | copiedToClipboard: false, 32 | displayCopyButton: false, 33 | } 34 | } 35 | 36 | componentDidMount() { 37 | const { language } = this.props 38 | const linesObj = language && language.split(':')[1] 39 | 40 | if (linesObj) { 41 | const splittedValues = linesObj.split(',') 42 | let stateLabel 43 | const linesToUpdate = { 44 | removeLines: [], 45 | addLines: [], 46 | updateLines: [], 47 | } 48 | 49 | // eslint-disable-next-line array-callback-return 50 | splittedValues.map(lines => { 51 | const linesRange = lines.split(',') 52 | 53 | // eslint-disable-next-line array-callback-return 54 | linesRange.map(eachLine => { 55 | const splitted = eachLine.split('-') 56 | 57 | if (splitted[0] === '') { 58 | // Is removing lines 59 | splitted.shift() 60 | stateLabel = 'removeLines' 61 | } else if (splitted[0] === '!') { 62 | splitted.shift() 63 | stateLabel = 'updateLines' 64 | } else { 65 | stateLabel = 'addLines' 66 | } 67 | 68 | if (splitted.length > 1) { 69 | for ( 70 | let i = parseInt(splitted[0]); 71 | i <= parseInt(splitted[1]); 72 | i++ 73 | ) { 74 | linesToUpdate[stateLabel].push(i) 75 | } 76 | } else { 77 | // Only one liner 78 | linesToUpdate[stateLabel].push(parseInt(splitted[0])) 79 | } 80 | 81 | this.setState({ 82 | [stateLabel]: [ 83 | ...this.state[stateLabel], 84 | ...linesToUpdate[stateLabel], 85 | ], 86 | }) 87 | }) 88 | }) 89 | } 90 | } 91 | 92 | copyToClipboard = () => { 93 | this.setState( 94 | { 95 | ...this.state, 96 | copiedToClipboard: true, 97 | }, 98 | () => { 99 | setTimeout(() => { 100 | this.setState({ 101 | ...this.state, 102 | copiedToClipboard: false, 103 | }) 104 | }, 3500) 105 | } 106 | ) 107 | } 108 | 109 | toggleCopyButton = () => { 110 | this.setState({ 111 | ...this.state, 112 | displayCopyButton: !this.state.displayCopyButton, 113 | }) 114 | } 115 | 116 | render() { 117 | const { language, value } = this.props 118 | const { 119 | addLines, 120 | removeLines, 121 | updateLines, 122 | copiedToClipboard, 123 | displayCopyButton, 124 | } = this.state 125 | 126 | return ( 127 |
this.toggleCopyButton()} 129 | onMouseLeave={() => this.toggleCopyButton()} 130 | > 131 | { 138 | const mergedLines = addLines.concat(removeLines).concat(updateLines) 139 | let style = { display: 'block' } 140 | 141 | if (mergedLines.includes(lineNumber)) { 142 | style = { 143 | ...style, 144 | margin: '0 -22px', 145 | padding: '3px 12px 6px', 146 | } 147 | } 148 | 149 | if (removeLines.includes(lineNumber)) { 150 | style = { 151 | ...style, 152 | borderLeft: `6px #f9320c solid`, 153 | background: `rgba(249, 50, 12, .1)`, 154 | } 155 | } else if (addLines.includes(lineNumber)) { 156 | style = { 157 | ...style, 158 | borderLeft: `6px #3ac569 solid`, 159 | background: `rgba(58, 197, 105, .1)`, 160 | } 161 | } else if (updateLines.includes(lineNumber)) { 162 | style = { 163 | ...style, 164 | borderLeft: `6px #f9c00c solid`, 165 | background: `rgba(249, 192, 12, .1)`, 166 | } 167 | } 168 | 169 | return { style } 170 | }} 171 | > 172 | {value} 173 | 174 | 175 |
176 | this.copyToClipboard()}> 177 | 180 | 181 |
182 |
183 | ) 184 | } 185 | } 186 | 187 | export default CodeBlock 188 | -------------------------------------------------------------------------------- /packages/web/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Row, Col } from 'react-flexbox-grid' 3 | import { Gallery } from 'react-grid-gallery' 4 | 5 | import Layout from '../components/Layout' 6 | import { ELEMENTS } from '../constants/Meta' 7 | import { META } from '../constants/metadata' 8 | 9 | const images = [ 10 | { 11 | src: '/images/YCombinator.jpg', 12 | width: 801, 13 | height: 571, 14 | caption: 'Merse got into Y Combinator S24 batch', 15 | tags: [{ value: 'Y Combinator', title: 'Y Combinator' }], 16 | alt: 'Merse got into the Y Combinator S24 batch', 17 | }, 18 | { 19 | src: '/images/YC Speaking.jpg', 20 | width: 3032, 21 | height: 3024, 22 | caption: 'YC Demo Event', 23 | alt: 'Mark Rachapoom, and Kumar Abhirup (me)', 24 | }, 25 | { 26 | src: '/images/SF Tech Week 2024 (1).jpg', 27 | width: 901, 28 | height: 871, 29 | caption: 'Panel Speaker at SF Tech Week 2024', 30 | tags: [{ value: 'SF Tech Week', title: 'SF Tech Week' }], 31 | alt: 'Panel Speaker at SF Tech Week 2024', 32 | }, 33 | { 34 | src: '/images/SF Tech Week 2024 (2).jpg', 35 | width: 801, 36 | height: 571, 37 | caption: 'Panel Speaker at SF Tech Week 2024', 38 | tags: [{ value: 'SF Tech Week', title: 'SF Tech Week' }], 39 | alt: 'Panel Speaker at SF Tech Week 2024', 40 | }, 41 | { 42 | src: '/images/mark rachapoom founders.JPG', 43 | width: 4032, 44 | height: 3024, 45 | caption: 'FoundersOnly Event with Mark Rachapoom', 46 | alt: 'FoundersOnly Event with Mark Rachapoom', 47 | }, 48 | { 49 | src: '/images/walk_with_naval.jpg', 50 | width: 801, 51 | height: 871, 52 | caption: 53 | 'A two hour walk with Naval in San Francisco, before joining Airchat.', 54 | tags: [{ value: 'Naval Ravikant', title: 'Naval Ravikant' }], 55 | alt: 'A two hour walk with Naval in San Francisco, before joining Airchat.', 56 | }, 57 | { 58 | src: '/images/bryan_johnson.jpeg', 59 | width: 2048, 60 | height: 1536, 61 | tags: [ 62 | { value: 'Bryan Johnson', title: 'Bryan Johnson' }, 63 | { value: "Don't Die", title: "Don't Die" }, 64 | ], 65 | alt: "Bryan Johnson, Don't Die Blueprint", 66 | caption: `Bryan Johnson, Don't Die Blueprint`, 67 | }, 68 | { 69 | src: '/images/eric_jorgenson.jpeg', 70 | width: 2048, 71 | height: 1152, 72 | tags: [ 73 | { value: 'Eric Jorgenson', title: 'Eric Jorgenon' }, 74 | { 75 | value: 'The Almanak of Naval Ravikant', 76 | title: 'The Almanak of Naval Ravikant', 77 | }, 78 | ], 79 | alt: 'Eric Jorgenson, signing my The Almanak of Naval Ravikant', 80 | caption: 'Eric Jorgenson, signing my The Almanak of Naval Ravikant', 81 | }, 82 | { 83 | src: '/images/stanford treehacks.jpeg', 84 | width: 2048, 85 | height: 1152, 86 | tags: [ 87 | { value: 'Stanford Treehacks', title: 'Stanford Treehacks' }, 88 | { 89 | value: 'Judge', 90 | title: 'Judge', 91 | }, 92 | ], 93 | alt: 'Judging at Stanford Treehacks', 94 | caption: 'Judging at Stanford Treehacks', 95 | }, 96 | // { 97 | // src: '/images/sam_hinkie.jpeg', 98 | // width: 2048, 99 | // height: 1152, 100 | // tags: [ 101 | // { value: 'Sam Hinkie', title: 'Sam Hinkie' }, 102 | // { value: 'FoundersOnly Event', title: 'FoundersOnly Event' }, 103 | // ], 104 | // alt: 'Met Sam Hinkie in Austin, Texas.', 105 | // caption: 'Met Sam Hinkie in Austin, Texas.', 106 | // }, 107 | // { 108 | // src: '/images/agi house.jpeg', 109 | // width: 4032, 110 | // height: 3024, 111 | // tags: [ 112 | // { value: 'AGI House', title: 'AGI House' }, 113 | // { 114 | // value: 'Hackathon', 115 | // title: 'Hackathon', 116 | // }, 117 | // ], 118 | // alt: 'AGI House Hackathon, in Palo Alto, California.', 119 | // caption: 'AGI House Hackathon, in Palo Alto, California.', 120 | // }, 121 | { 122 | src: '/images/nonce.JPG', 123 | width: 3390, 124 | height: 2170, 125 | tags: [ 126 | { value: 'Nonce', title: 'Nonce' }, 127 | { 128 | value: 'Korea', 129 | title: 'Korea', 130 | }, 131 | ], 132 | alt: 'Nonce Korea 2022', 133 | caption: 'Nonce Korea 2022', 134 | }, 135 | // { 136 | // src: '/images/korea_dinner.jpeg', 137 | // width: 4032, 138 | // height: 2268, 139 | // alt: 'Nonce Korea Dinner', 140 | // caption: 'Nonce Korea Dinner', 141 | // }, 142 | // { 143 | // src: '/images/arcadia dinner 2.jpeg', 144 | // width: 2016, 145 | // height: 1512, 146 | // tags: [ 147 | // { value: 'Arcadia', title: 'Arcadia' }, 148 | // { 149 | // value: 'Farewell', 150 | // title: 'Farewell', 151 | // }, 152 | // ], 153 | // alt: 'Arcadia Farewell Dinner', 154 | // caption: 'Arcadia Farewell Dinner', 155 | // }, 156 | ] 157 | 158 | function About() { 159 | return ( 160 | <> 161 | 162 |
165 |

166 | {' '} 167 | {META.fname} {META.lname} {/* */}{' '} 168 |

{' '} 169 |
170 | 171 | {ELEMENTS.about} {' '} 172 | {' '} 173 |

174 |

Moments📸

{' '} 175 |
176 | {' '} 181 |
{' '} 182 |
{' '} 183 |
{' '} 184 |
{' '} 185 | 186 | ) 187 | } 188 | 189 | export default About 190 | -------------------------------------------------------------------------------- /packages/web/writings/what-keeps-founders-motivated-not-quit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "What keeps Founders Motivated" 3 | date: "2021-09-20" 4 | og: 5 | description: "Startups run (in the initial days) only if founders want to run it. What keeps founders motivated? Let's find out." 6 | image: "https://images.unsplash.com/photo-1504540790158-a8126030896a?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1700&q=80" 7 | author: 8 | twitter: "kumareth" 9 | name: "Kumar Abhirup" 10 | --- 11 | 12 | > The cover image of this essay is from [Unsplash](https://images.unsplash.com/photo-1504540790158-a8126030896a?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1700&q=80). 13 | 14 | A lot of startups are "launched" every year. 15 | 16 | **Startups run** (in the initial days) **only if founders want to run it.** 17 | 18 | What keeps founders motivated? 19 | 20 | What demotivates them? 21 | 22 | What makes some founders quit early? 23 | 24 | There are various reasons why some founders give up early, and some give up late, and some just don't give up. 25 | 26 | --- 27 | 28 | # The Boulder Up the Hill Analogy 29 | 30 | Running a Startup in the beginning could feel like you are pushing a boulder up the hill. 31 | 32 | ![Pushing Boulder Up The Hill](https://i.ibb.co/RNHXSrB/Pushing-Boulder-Up-The-Hill.jpg) 33 | Image Credits: 34 | 35 | Building a Startup initially gets you into a lot. Finding those first set of paying customers would seem like an uphill battle (for most of the startups). **It would feel like you are pushing this huge boulder up the hill.** 36 | 37 | When Startups reach [Product Market Fit](https://review.firstround.com/how-superhuman-built-an-engine-to-find-product-market-fit), the boulder is at mountain's peak and then founders don't have to push the boulder up, but gotta chase it down. **Every Startup dreams this moment.** 38 | 39 | Founders usually lack motivation while they are pushing up the boulder for too long with least incremental gains. Founders mostly quit in these circumstances. (Usually a good decision). 40 | 41 | When founders reach PMF and are "chasing the boulder down the hill", this is the time founders are the busiest and will always have things to do. This is the PRIME time for founders to feel excited and are motivated to keep working on the startup. 42 | 43 | --- 44 | 45 | # What keeps founders motivated? 46 | 47 | I would boil it down to these four things. 48 | 49 | - Core Belief in Idea 50 | - Traction / Growth / Hype 51 | - Money 52 | - Being "Busy" with the Startup 53 | 54 | # Core Belief in Idea 55 | 56 | It’s easy to be motivated and enthusiastic when things are going well. The true test of a founder is how the founder reacts when things **aren’t going well.** 57 | 58 | For example, you just lost your biggest customer. Or maybe your funding went wrong at the last possible moment. Or you are gaining NO traction since many months. 59 | 60 | **If at such times you still remain motivated,** it probably means **that your belief in the startup**, its idea and its vision **is strong.** 61 | 62 | However, Core Belief in Idea isn't always here to stay. Depending on the traction or other factors, it can erode with time. **Erosion of belief in the vision of the startup leads to founders quitting** and calling it a day. 63 | 64 | Founders who learn to decouple their "trust in the original idea" from external factors (like money and traction) are usually motivated regardless the circumstances. 65 | 66 | # Traction / Growth / Hype 67 | 68 | If people know what you are building, are talking about it, are excited about it, **you have hype.** 69 | 70 | If people actually use your product or are paying for it, **you have traction.** 71 | 72 | Initially when you are planning out or building the idea, you wouldn't have any of that. But when you are building for years with no traction YET, you would lose some motivation. (Obviously lol) 73 | 74 | When people are interested in what you are building and are looking forward to whatever it is that you are building, then you would feel an innate obligation to finish the project, run it and make it successful. 75 | 76 | **Traction is one main factor why founders remain motivated.** 77 | 78 | # Money 79 | 80 | **If the startup is well funded by investors,** founders will usually compensate themselves. That compensation becomes an incentive for founders to stay motivated and work even during bad times, or during times when there is no traction or hype. 81 | 82 | **If the startup isn't funded by investors,** and if founders have enough money already that they can live on, without having to do another job for a long time, it would prevent founders from quitting early. It's not a motivation, but definitely provides them relief in demotivating times. 83 | 84 | If the startup is generating you enough money, you would be motivated enough to keep working on it, usually. 85 | 86 | # Being "Busy" with the Startup 87 | 88 | If you have a lot of things to do with your startup, **if you find yourself busy with the project, this usually means it's going well as it should.** 89 | 90 | Customers are calling, Social Media is flooded with a lot of feedback, you got many calls booked, product design meetings, and what not. This happens when you are on the other side of the mountain chasing down the boulder. **This "busyness" keeps founders motivated.** 91 | 92 | **Failing startups seem to have one thing in common for founders, there's not much left to do**, or it doesn't seem like there's anything meaningful left. That demotivates founders, eventually leading them to quit. 93 | 94 | --- 95 | 96 | # Hmm, so? 97 | 98 | **If you are a founder and if you have marked all the above listed things as safe**, you would probably never quit. **Those four things** or **some of those four things** would always **keep you motivated**. 99 | 100 | **If you have none of them, you'd definitely quit**,
AND IT WOULD MAKE SENSE IF YOU DO SO. 101 | 102 | ## When to quit? 103 | 104 | Ask yourself this question. If you could somehow go back in time and build your startup again, would you choose the same idea? If yes, don't quit. Else, think of quitting maybe? 105 | -------------------------------------------------------------------------------- /packages/web/archived-writings/what-keeps-founders-motivated-not-quit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "What keeps Founders Motivated" 3 | date: "2021-09-20" 4 | og: 5 | description: "Startups run (in the initial days) only if founders want to run it. What keeps founders motivated? Let's find out." 6 | image: "https://images.unsplash.com/photo-1504540790158-a8126030896a?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1700&q=80" 7 | author: 8 | twitter: "kumareth" 9 | name: "Kumar Abhirup" 10 | --- 11 | 12 | > The cover image of this essay is from [Unsplash](https://images.unsplash.com/photo-1504540790158-a8126030896a?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1700&q=80). 13 | 14 | A lot of startups are "launched" every year. 15 | 16 | **Startups run** (in the initial days) **only if founders want to run it.** 17 | 18 | What keeps founders motivated? 19 | 20 | What demotivates them? 21 | 22 | What makes some founders quit early? 23 | 24 | There are various reasons why some founders give up early, and some give up late, and some just don't give up. 25 | 26 | --- 27 | 28 | # The Boulder Up the Hill Analogy 29 | 30 | Running a Startup in the beginning could feel like you are pushing a boulder up the hill. 31 | 32 | ![Pushing Boulder Up The Hill](https://i.ibb.co/RNHXSrB/Pushing-Boulder-Up-The-Hill.jpg) 33 | Image Credits: 34 | 35 | Building a Startup initially gets you into a lot. Finding those first set of paying customers would seem like an uphill battle (for most of the startups). **It would feel like you are pushing this huge boulder up the hill.** 36 | 37 | When Startups reach [Product Market Fit](https://review.firstround.com/how-superhuman-built-an-engine-to-find-product-market-fit), the boulder is at mountain's peak and then founders don't have to push the boulder up, but gotta chase it down. **Every Startup dreams this moment.** 38 | 39 | Founders usually lack motivation while they are pushing up the boulder for too long with least incremental gains. Founders mostly quit in these circumstances. (Usually a good decision). 40 | 41 | When founders reach PMF and are "chasing the boulder down the hill", this is the time founders are the busiest and will always have things to do. This is the PRIME time for founders to feel excited and are motivated to keep working on the startup. 42 | 43 | --- 44 | 45 | # What keeps founders motivated? 46 | 47 | I would boil it down to these four things. 48 | 49 | - Core Belief in Idea 50 | - Traction / Growth / Hype 51 | - Money 52 | - Being "Busy" with the Startup 53 | 54 | # Core Belief in Idea 55 | 56 | It’s easy to be motivated and enthusiastic when things are going well. The true test of a founder is how the founder reacts when things **aren’t going well.** 57 | 58 | For example, you just lost your biggest customer. Or maybe your funding went wrong at the last possible moment. Or you are gaining NO traction since many months. 59 | 60 | **If at such times you still remain motivated,** it probably means **that your belief in the startup**, its idea and its vision **is strong.** 61 | 62 | However, Core Belief in Idea isn't always here to stay. Depending on the traction or other factors, it can erode with time. **Erosion of belief in the vision of the startup leads to founders quitting** and calling it a day. 63 | 64 | Founders who learn to decouple their "trust in the original idea" from external factors (like money and traction) are usually motivated regardless the circumstances. 65 | 66 | # Traction / Growth / Hype 67 | 68 | If people know what you are building, are talking about it, are excited about it, **you have hype.** 69 | 70 | If people actually use your product or are paying for it, **you have traction.** 71 | 72 | Initially when you are planning out or building the idea, you wouldn't have any of that. But when you are building for years with no traction YET, you would lose some motivation. (Obviously lol) 73 | 74 | When people are interested in what you are building and are looking forward to whatever it is that you are building, then you would feel an innate obligation to finish the project, run it and make it successful. 75 | 76 | **Traction is one main factor why founders remain motivated.** 77 | 78 | # Money 79 | 80 | **If the startup is well funded by investors,** founders will usually compensate themselves. That compensation becomes an incentive for founders to stay motivated and work even during bad times, or during times when there is no traction or hype. 81 | 82 | **If the startup isn't funded by investors,** and if founders have enough money already that they can live on, without having to do another job for a long time, it would prevent founders from quitting early. It's not a motivation, but definitely provides them relief in demotivating times. 83 | 84 | If the startup is generating you enough money, you would be motivated enough to keep working on it, usually. 85 | 86 | # Being "Busy" with the Startup 87 | 88 | If you have a lot of things to do with your startup, **if you find yourself busy with the project, this usually means it's going well as it should.** 89 | 90 | Customers are calling, Social Media is flooded with a lot of feedback, you got many calls booked, product design meetings, and what not. This happens when you are on the other side of the mountain chasing down the boulder. **This "busyness" keeps founders motivated.** 91 | 92 | **Failing startups seem to have one thing in common for founders, there's not much left to do**, or it doesn't seem like there's anything meaningful left. That demotivates founders, eventually leading them to quit. 93 | 94 | --- 95 | 96 | # Hmm, so? 97 | 98 | **If you are a founder and if you have marked all the above listed things as safe**, you would probably never quit. **Those four things** or **some of those four things** would always **keep you motivated**. 99 | 100 | **If you have none of them, you'd definitely quit**,
AND IT WOULD MAKE SENSE IF YOU DO SO. 101 | 102 | ## When to quit? 103 | 104 | Ask yourself this question. If you could somehow go back in time and build your startup again, would you choose the same idea? If yes, don't quit. Else, think of quitting maybe? 105 | -------------------------------------------------------------------------------- /packages/web/archived-writings/google-recaptcha-react-node-js.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Implementing Google reCAPTCHA with React and Node.js" 3 | date: "2020-06-02" 4 | og: 5 | description: "Creating a Google reCAPTCHA checkbox on frontend is easy. But making it fully secure through backend will need you some efforts." 6 | image: "https://i.ibb.co/0XGJzZk/Google-re-CAPTCHA-React-and-Node.jpg" 7 | author: 8 | twitter: "kumar_abhirup" 9 | name: "Kumar Abhirup" 10 | --- 11 | 12 | > The thumbnail of this article is from [Google reCAPTCHA's official landing page](https://www.google.com/recaptcha/intro/v3.html). 13 | 14 | In this tutorial, we will learn how to use reCAPTCHA v2 with React and Node. 15 | 16 | Most of the tutorials online only cover the frontend part of this technology. The truth is, **your captcha solution can only be useful, if it validates the input on backend.** 17 | 18 | Let's begin with how you can show the captcha checkbox on React Frontend by first setting up the Google reCAPTCHA console. 19 | 20 | # Setup Google reCAPTCHA Admin 21 | 22 | - Go to [this URL](https://www.google.com/recaptcha/admin/create) to create a reCAPTCHA app. 23 | 24 | - Give the reCAPTCHA a label. 25 | 26 | - Select `reCAPTCHA v2`. 27 | 28 | - Add the domain name of where this captcha checkbox will be used. To test this checkbox on your development server, add `localhost` as your domain name. 29 | 30 | - Click Submit. 31 | 32 | Now that you have created the app, you can get your reCAPTCHA keys. 33 | 34 | > Note that these keys are extremely important. The `SITE KEY` can be used in the client, but the `SECRET KEY` should be never used in the client. It should only be used in the backend. 35 | 36 | ![Get your Google reCAPTCHA Credentials](https://i.ibb.co/7WFsKWr/Site-Key-Secret-Key-Google-re-CAPTCHA-Screenshot.jpg) 37 | 38 | --- 39 | 40 | # Setup React Frontend 41 | 42 | Install the `react-google-recaptcha` React Component. It will simplify this terrific process. 43 | 44 | ```bash 45 | yarn add react-google-recaptcha 46 | ``` 47 | 48 | After you install the component, here's how you can display the reCAPTCHA checkbox on the frontend. 49 | 50 | ```jsx 51 | import ReCAPTCHA from "react-google-recaptcha" 52 | 53 | function onChange(value) { 54 | console.log("Captcha value:", value) 55 | } 56 | 57 | export default function App() { 58 | return 59 | } 60 | ``` 61 | 62 | Make sure you replace `YOUR_CLIENT_SITE_KEY` with the real client site key that you got from the Google reCAPTCHA Console. 63 | 64 | With the correct usage of the above code, you should see something like this in the browser. 65 | 66 | ![Google reCAPTCHA GIF](https://www.cmnty.com/support/wp-content/uploads/2017/07/nocaptcha.gif) 67 | 68 | Now, if you check your console, you will see the `Captcha value: some_value` logged on the console. That same value, will be sent to the backend to verify if the user is really not a robot. 69 | 70 | --- 71 | 72 | # Backend Verification 73 | 74 | To verify the value that the backend gets, after the user checks the box, you will have to make a POST request to Google reCAPTCHA API. 75 | 76 | ```bash 77 | POST https://www.google.com/recaptcha/api/siteverify 78 | ``` 79 | 80 | Here's how you do it in Node.js 👇 81 | 82 | ```js 83 | // Install 'es6-promise' and 'isomorphic-fetch' from NPM or Yarn. 84 | require("es6-promise").polyfill() 85 | require("isomorphic-fetch") 86 | 87 | const RECAPTCHA_SERVER_KEY = process.env.RECAPTCHA_SERVER_KEY 88 | 89 | const humanKey = "value_that_we_got_from_the_frontend" 90 | 91 | // Validate Human 92 | const isHuman = await fetch(`https://www.google.com/recaptcha/api/siteverify`, { 93 | method: "post", 94 | headers: { 95 | Accept: "application/json", 96 | "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" 97 | }, 98 | body: `secret=${RECAPTCHA_SERVER_KEY}&response=${humanKey}` 99 | }) 100 | .then(res => res.json()) 101 | .then(json => json.success) 102 | .catch(err => { 103 | throw new Error(`Error in Google Siteverify API. ${err.message}`) 104 | }) 105 | 106 | if (humanKey === null || !isHuman) { 107 | throw new Error(`YOU ARE NOT A HUMAN.`) 108 | } 109 | 110 | // The code below will run only after the reCAPTCHA is succesfully validated. 111 | console.log("SUCCESS!") 112 | ``` 113 | 114 | > **Note:** This article does not tell you how to communicate between frontend and backend, this article only tells you what logic to use in backend, to verify the reCAPTCHA string value you get from the frontend. 115 | 116 | To understand the above code, we first need to understand what `fetch()` function results. 117 | 118 | ```js 119 | await fetch(`https://www.google.com/recaptcha/api/siteverify`, { 120 | method: "post", 121 | headers: { 122 | Accept: "application/json", 123 | "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" 124 | }, 125 | body: `secret=${RECAPTCHA_SERVER_KEY}&response=${humanKey}` 126 | }) 127 | ``` 128 | 129 | The above snippet makes a POST request to the Google reCAPTCHA API route to get a response (to know if the humanKey we got from the frontend is correct or not). 130 | 131 | We also supply `RECAPTCHA_SERVER_KEY` and the `humanKey` in the request body. Remember that the `RECAPTCHA_SERVER_KEY` should be kept secret. Do not push it in GitHub Open Source. [Use Environment Variables](https://www.freecodecamp.org/news/heres-how-you-can-actually-use-node-environment-variables-8fdf98f53a0a/#:~:text=env%20files%20allow%20you%20to,in%20there%20on%20different%20lines.&text=To%20read%20these%20values%2C%20there,the%20dotenv%20package%20from%20npm.). 132 | 133 | Here's a Sample Response that the API gives us. 134 | 135 | ```json 136 | { 137 | "success": true, 138 | "challenge_ts": "ISODateString", // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ) 139 | "hostname": "string" // the hostname of the site where the reCAPTCHA was solved 140 | } 141 | ``` 142 | 143 | The `success: true` is enough for us to understand that the user is not a robot. If `success: false`, then the `humanKey` we got was probably a wrong one or the user tried to hack in. 144 | 145 | --- 146 | 147 | # Aftermath 148 | 149 | Once you get `success: true`, you would probably want to send your frontend a response that the reCAPTCHA was not botched and the user was not a robot. 150 | 151 | You must be using some kind of an Express/Koa API Route, or a GraphQL backend where you can get API requests from the frontend to give it some response. 152 | 153 | If you want to see it in practise, I have hosted my code here 🔥 154 | 155 | - [Frontend reCAPTCHA](https://github.com/KumarAbhirup/kumarabhirup/blob/0f883462652afc521d4938e1f06f0128f233e29f/src/components/mobirise/Contact.js#L270) 156 | - [Backend reCAPTCHA Validation](https://github.com/KumarAbhirup/kumarabhirup-backend/blob/d0b9daa97e724c428ebdf3728f475d35ee7370de/src/resolvers/Mutation.js#L44) 157 | 158 | I hope this tutorial gave you decent information on how you can validate your reCAPTCHA input on backend using Node.js and React. 159 | 160 | Peace ✌️ 161 | -------------------------------------------------------------------------------- /packages/web/archived-writings/build-publish-react-components.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "This is how you should build and publish a Modern React Component!" 3 | date: "2020-05-05" 4 | og: 5 | description: "A brief guide about publishing react components to NPM using the best practises." 6 | image: "https://res.cloudinary.com/practicaldev/image/fetch/s--U98S70Pc--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://thepracticaldev.s3.amazonaws.com/i/ih3qpm4vxhpscgdk24fg.png" 7 | imageTypeOnArticle: "NORMAL" 8 | author: 9 | twitter: "kumar_abhirup" 10 | name: "Kumar Abhirup" 11 | --- 12 | 13 | React developers use similar component code day in and day out. But not all of them streamline the React Component building and publishing workflow. 14 | Before we discuss the building and publishing workflow, here's why we need to have it. 15 | 16 | # Before we discuss the building and publishing workflow, here's why we need to have it 17 | 18 | - **Reason 1:** Most of our React code is reusable and there's a high chance that we need the same React Component in another project. 19 | - **Reason 2:** To keep a code uniformity among all of our React Projects. 20 | - **Reason 3:** An opportunity to contribute to the open-source. We can publish the component, and have others use it as well! 21 | 22 | You know how NPM packages are built. Yet, when it comes to publishing the React Component we use in a project, we hesitate. 23 | 24 | # Reasons why you hesitate 25 | 26 | - Building a new React Component that is publishable on NPM takes a lot of efforts. 27 | - You feel it is not worthy to waste time installing and updating the published package in your project. 28 | - You want your component in your components folder, and when component code is changed, you want your project to "live reload". 29 | 30 | # When I was a novice, I used to publish a React Component like this… 31 | 32 | I used to create a new Git Repository. 33 | 34 | Then setup all the babel and webpack stuff. 35 | 36 | Then I would write the React Component code. 37 | 38 | Then I would compile it manually and publish it to npm using `npm publish`. 39 | 40 | To test the React Component, I would manually install the component in my project directory using `npm i -S that-react-component`. That had no "Live Reload"… 41 | 42 | I had to update the package in the Component Repository and Update the module back in my project. 43 | 44 | > How shitty that procedure was. I knew it was shitty. So, I stopped open sourcing React Components. I let them lie in my Project Directories. If I would need a component, I copy-pasted the component files to another project. And that went on. Until I learned about… 45 | 46 | `create-react-library` and `npm link`! 47 | 48 | I was just strolling through the GitHub search box typing weird stuff, and then I found `create-react-library`. 49 | 50 | ![React Workflow](https://cdn-images-1.medium.com/max/2560/1*tt-8-gpw-xLZu0heaMZ57g.png) 51 | 52 | # Here's the much better way of doing it 53 | 54 | ## Step 1 55 | 56 | So, every time you know you are building a component for the ongoing project, think… 57 | 58 | - If you might need the component for other projects. 59 | - If the component can be useful for other devs out there. 60 | - If your component logic and use-case is not very project-specific. 61 | - Or for the sanity sake, you want your project files to be less cluttered. 62 | 63 | **If you see any of those reasons valid, you should consider building the component as a reusable component that is packaged as a module.** 64 | 65 | If yes, then move to Step 2. 66 | 67 | ## Step 2 68 | 69 | Visit the master folder where you store all your projects via terminal and run the following 👇 70 | 71 | ```bash 72 | npx create-react-library # then answer few basic prompts 73 | cd # enter in that directory 74 | ``` 75 | 76 | **And voila! You got your React Component Boilerplate Code setup.** 77 | 78 | ## Step 3 79 | 80 | Do the following in the terminal 👇 81 | 82 | ```bash 83 | npm i # this will install your component modules 84 | cd example && npm i # install CRA modules 85 | npm start 86 | ``` 87 | 88 | Running above commands will install the modules that your React Component and Create React App Example will need. 89 | 90 | ## Step 4 91 | 92 | Let us browse the file structure of the project. 93 | 94 | ``` 95 | project 96 | │ README.md 97 | │ package.json 98 | | ... 99 | │ 100 | └───example 101 | │ │ package.json 102 | │ │ ... 103 | │ 104 | └───src 105 | │ package.json 106 | │ ... 107 | ``` 108 | 109 | Now, anytime you make a change to your library in `src/` or to the example app's `example/src`, `create-react-app` will live-reload your local dev server so you can iterate on your component in real-time! 110 | 111 | ## Step 5 112 | 113 | Well, what if **you want the component to work live with your ongoing project** without having to update and publish on npm? 114 | 115 | Here, `npm link` comes for the rescue! 116 | 117 | In your the root of your component directory, run `$ npm link`. That makes your component available globally to all projects! 118 | 119 | ## Step 6 120 | 121 | Now via terminal, reach the ongoing project directory, and run 122 | `$ npm link `. 123 | 124 | That is it. You have connected both the worlds! 125 | 126 | **Let both of your component directory and ongoing project watch and compile the code live.** 127 | 128 | ## Step 7 129 | 130 | It's time to use the component in your project! 131 | You can import and use the component in a normal way. 132 | 133 | ```javascript 134 | import ThatAwesomeComponent from "" 135 | ``` 136 | 137 | Now when you update your component code and refresh your project in the browser, you'll see things come alive! 138 | 139 | **Go! Try that yourself!** 140 | 141 | --- 142 | 143 | # Publishing your React Component 144 | 145 | Now that you want to publish/deploy/ship your React Component… 146 | 147 | - **Make sure you have good documentation supporting your code.** 148 | - Also, check your version number. If you feel it's all right and working, it's time to publish! 149 | 150 | ```bash 151 | npm publish 152 | ``` 153 | 154 | That is it. Your files will be built and published on NPM Registry! 155 | 156 | **If you want your build to be less bloated, publish the component with the following `.npmignore` file.** 157 | 158 | ```bash 159 | # it is also configured for TypeScript Components 160 | src 161 | example 162 | .vscode 163 | .eslintrc 164 | .prettierrc 165 | package-lock.json 166 | tsconfig.json 167 | tsconfig.test.json 168 | .gitignore 169 | node_modules 170 | .travis.yml 171 | rollup.config.js 172 | ``` 173 | 174 | ## You forgot one thing 175 | 176 | Do not forget to `npm unlink` the component from your ongoing project directory so that you can use the "real" and "published" component. When do develop the component, you may link it again. 177 | 178 | To unlink, do `$ npm unlink ` from the project directory. 179 | 180 | --- 181 | 182 | # Links 183 | 184 | - [create-react-library](https://github.com/transitive-bullshit/create-react-library) 185 | - [how-to-use-npm-link](https://codurance.com/2016/12/21/how-to-use-npm-link) 186 | 187 | # Conclusion 188 | 189 | React components can be more awesome if built and published the right way. 190 | -------------------------------------------------------------------------------- /packages/web/archived-writings/next-js-docker-made-easy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Next.js + Docker. Made easy." 3 | date: "2020-05-04" 4 | og: 5 | description: "After a few days of research and setting up a Containerized Next.js App, I am here to share with you how to do it." 6 | image: "https://res.cloudinary.com/practicaldev/image/fetch/s--3XKn-1bL--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/i/vhyp2izu2jx3a6oj2euz.jpg" 7 | author: 8 | twitter: "kumar_abhirup" 9 | name: "Kumar Abhirup" 10 | --- 11 | 12 | This week while starting to build a huge SaaS product, I had to make many decisions. The biggest decision I made was to build that SaaS product with the Microservices architecture. 13 | 14 | Thankfully, [Lucas Chen](https://dev.to/bettercodingacademy) had [this amazing series](https://dev.to/bettercodingacademy/i-m-a-professional-react-developer-and-here-s-how-i-set-up-react-node-js-microservices-using-docker-and-graphql-20gk) that explained the React + GraphQL + Docker Microservices architecture. In his series, the backends were microservices but React was not hosted on Docker. I wanted it all on Docker, so I had to research a lot, about integrating React (especially Next.js) with Docker. 15 | 16 | After a few days of research and setting up a Containerized Next.js App, I am here to share with you how to do it. 17 | 18 | Hope you like it :) 19 | 20 | --- 21 | 22 | # 🦋 Getting Started 23 | 24 | Setting up a Next.js shouldn't be hard. 25 | 26 | ```bash 27 | yarn create next-app 28 | ``` 29 | 30 | **Wait! We are not doing it all from scratch.** 31 | 32 | Instead, I would recommend you to clone [this repo](https://github.com/KumarAbhirup/dockerized). We will learn about containerized Next.js from there. In this way, you will be able to compare your progress to that repository so that you can ensure you don't get lost in a long tutorial. 33 | 34 | [Click here](https://github.com/KumarAbhirup/dockerized) to access the GitHub repository. 35 | 36 | The above repository includes... 37 | 38 | - A setup that is scalable. You may append your dockerized backends to it later. 39 | - ESLint + Prettier setup included. 40 | - It's TypeScript. :) 41 | 42 | # 🔰 Things you need 43 | 44 | - Docker Installed on your machine 45 | - Some basic knowledge of Next.js 46 | 47 | # 🚀 Clone and Setup the repository 48 | 49 | - Run the below command 50 | 51 | ```bash 52 | git clone https://github.com/KumarAbhirup/dockerized dockerized 53 | 54 | cd dockerized 55 | ``` 56 | 57 | - Rename all the `.env.example` to `.env`. You'll find it in `packages/landingpage` 58 | 59 | - Create a `.env` file in the root of the directory. 60 | 61 | As you cloned the project, the Next.js App is ready to run. 62 | 63 | Just run the below command to fire up the development environment for the Next.js project. 64 | 65 | ```bash 66 | docker-compose up 67 | ``` 68 | 69 | --- 70 | 71 | # 👁 But Kumar, how does this thing even work 72 | 73 | You might be wondering where your Next.js project is staying. 74 | 75 | It is in the `packages/landingpage`... 76 | 77 | ![Next.js Project Folder Structure](https://i.ibb.co/yqX1wSC/Screenshot-2020-03-27-at-5-18-48-PM.jpg) 78 | 79 | You might be wondering why that Next.js project is kept deep inside the file system. 80 | 81 | I did it because no one dockerizes Next.js when they are only using Next.js... 82 | 83 | Dockerizing makes sense when you have a huge container architecture that connects your React frontends to the Containerized backends. 84 | 85 | So, the repository would not just contain a Next.js project but would have backends kept in the same `packages` folder. 86 | 87 | --- 88 | 89 | # 📦 How to containerize Next.js 90 | 91 | To use Docker to containerize any code, we need to have a `Dockerfile` in the package. Every container has its own `Dockerfile`. 92 | 93 | Next.js too, will have its own Dockerfile. Let us take a look at it. 94 | 95 | [**`packages/landingpage/Dockerfile`**](https://github.com/KumarAbhirup/dockerized/blob/master/packages/landingpage/Dockerfile) 96 | 97 | ```docker 98 | FROM node:12 99 | 100 | ENV PORT 3000 101 | 102 | # Create app directory 103 | RUN mkdir -p /usr/src/app 104 | WORKDIR /usr/src/app 105 | 106 | # Installing dependencies 107 | COPY package*.json /usr/src/app/ 108 | RUN npm install 109 | 110 | # Copying source files 111 | COPY . /usr/src/app 112 | 113 | # Building app 114 | RUN npm run build 115 | EXPOSE 3000 116 | 117 | # Running the app 118 | CMD "npm" "run" "dev" 119 | ``` 120 | 121 | Let me explain what's happening here. Here, by `FROM node:12`, we are telling Docker to work with the Node.js image. 122 | 123 | `ENV PORT 3000` just exposes the environment variable `PORT=3000`. 124 | 125 | The below code snippet tells docker to create directories, namely `/usr/src/app`. We also tell Docker to use that directory as its primary workspace (for carrying out processes) hereafter. 126 | 127 | ``` 128 | RUN mkdir -p /usr/src/app 129 | WORKDIR /usr/src/app 130 | ``` 131 | 132 | The below code snippet copies `package.json` and `package-lock.json` from your local cloned repository to the Docker Container and then runs `npm install` on it. I recommend you to take a look at [**`package.json`**](https://github.com/KumarAbhirup/dockerized/blob/master/packages/landingpage/package.json) of the Next.js container so you get the idea. 133 | 134 | ``` 135 | COPY package*.json /usr/src/app/ 136 | RUN npm install 137 | ``` 138 | 139 | Now that we have all the `node_modules` ready, below code will copy our code from our local computer directory and will put it into the Docker Container Directory. 140 | 141 | ``` 142 | # Copying source files 143 | COPY . /usr/src/app 144 | ``` 145 | 146 | Then the `Dockerfile` builds the Next.js app, exposes port 3000 (where Next.js works by default), and runs the command `npm run dev`. 147 | 148 | ``` 149 | # Building app 150 | RUN npm run build 151 | EXPOSE 3000 152 | 153 | # Running the app 154 | CMD "npm" "run" "dev" 155 | ``` 156 | 157 | I hope you understood all that is happening due to the Dockerfile. 158 | 159 | --- 160 | 161 | > Now, we have successfully containerized the application with Next.js! But we are not yet done. 162 | 163 | For hot-reloading to work with Next.js and Docker, you need to have the below code snippet added to the `packages/landingpage/next.config.js`. 164 | 165 | ```javascript 166 | module.exports = { 167 | webpackDevMiddleware: config => { 168 | config.watchOptions = { 169 | poll: 1000, 170 | aggregateTimeout: 300 171 | } 172 | 173 | return config 174 | } 175 | } 176 | ``` 177 | 178 | --- 179 | 180 | We are still not done! 181 | 182 | To run all our containers (in this case only one) together successfully, we will need a `docker-compose.yml` file in the root of the project. 183 | 184 | Check out the [**`docker-compose.yml`**](https://github.com/KumarAbhirup/dockerized/blob/master/docker-compose.yml) in your folder structure. 185 | 186 | ```docker 187 | version: "3.3" 188 | 189 | services: 190 | nextjs: 191 | ports: 192 | - 3000:3000 193 | build: 194 | context: packages/landingpage 195 | dockerfile: Dockerfile 196 | volumes: 197 | - ./packages/landingpage:/usr/src/app 198 | - /usr/src/app/node_modules 199 | - /usr/src/app/.next 200 | env_file: 201 | - .env 202 | ``` 203 | 204 | The above code snippet makes sure that port 3000 is exposed. The `docker-compose.yml` file also tells Docker what services to build and which `Dockerfile` to use. 205 | 206 | The `env_file` tells the composer to use a `.env` file which if you have not yet made in your project, please add it for it to work. 207 | 208 | The `volumes` part is very important here. Without it, your Next.js will work, but the \_Hot Reloading Developmental Feature` would not work. 209 | 210 | --- 211 | 212 | # 🔥 Hurray 213 | 214 | If you surf through the repository carefully, you will understand how to containerize Next.js with Docker. 215 | 216 | **We are done!** 217 | 218 | To run the Dockerized Next.js app... 219 | 220 | Run **`docker-compose up`** and open `http://localhost:3000` in your browser. 221 | 222 | To make changes in code, make changes to `packages/landingpage/pages/index.tsx` file to see your website development experience come alive. 223 | 224 | --- 225 | 226 | # 🚀 For production 227 | 228 | When deploying to production, just make sure that you make a small change in your `packages/landingpage/Dockerfile`. 229 | 230 | Change the last line (`CMD "npm" "run" "dev"`) to **`CMD "npm" "start"`**. 231 | 232 | --- 233 | 234 | # 💚 Links 235 | 236 | - [Source Code](https://github.com/KumarAbhirup/dockerized/) 237 | - [Docker + Next.js Tutorial](https://www.codemochi.com/blog/2019-08-27-nextjs-hmr/) 238 | -------------------------------------------------------------------------------- /packages/web/styles/base.css: -------------------------------------------------------------------------------- 1 | @import url("https://rsms.me/inter/inter.css"); 2 | @import url("https://use.typekit.net/cyw3eir.css"); 3 | 4 | :root { 5 | --primary-color: #0070f4; 6 | --secondary-color: #666; 7 | --font-color: #000; 8 | --bg-color: #fff; 9 | --heading-color: #000; 10 | --highlight-color: #ffe74c; 11 | --hr-color: #eee; 12 | --writing-body: #444; 13 | --uses-secondary-color: #bbb; 14 | } 15 | 16 | [data-theme="dark"] { 17 | --primary-color: #9a97f3; 18 | --secondary-color: rgba(204, 204, 204, 0.963); 19 | --font-color: #fff; 20 | --bg-color: #010101ef; 21 | --heading-color: #fff; 22 | --highlight-color: #267b9c; 23 | --hr-color: #222; 24 | --writing-body: #ccc; 25 | } 26 | 27 | ::-moz-selection { 28 | background: var(--highlight-color); 29 | } 30 | 31 | ::selection { 32 | background: var(--highlight-color); 33 | } 34 | 35 | html { 36 | font-family: "Kode Mono", "Inter", sans-serif; 37 | overflow-x: hidden; 38 | } 39 | 40 | body { 41 | background-color: var(--bg-color); 42 | color: var(--font-color); 43 | margin: 0; 44 | padding: 0; 45 | } 46 | 47 | * { 48 | transition: background 0.2s linear; 49 | } 50 | 51 | .top-menu { 52 | margin: 20px 10px 0; 53 | } 54 | 55 | .top-menu ul { 56 | margin: 3px 0 0 10px; 57 | padding: 0; 58 | } 59 | 60 | .menubar { 61 | /**/ 62 | } 63 | 64 | /* @media (max-width: 520px) { 65 | .menubar { 66 | overflow-x: scroll; 67 | } 68 | } */ 69 | 70 | .top-menu ul li { 71 | display: inline; 72 | margin-right: 5px; 73 | } 74 | 75 | .top-menu ul li a { 76 | color: var(--heading-color); 77 | font-family: "Kode Mono", ibm-plex-mono, Monaco, "Lucida Console"; 78 | padding: 0 0 3px; 79 | text-decoration: none; 80 | font-size: 12px; 81 | text-transform: uppercase; 82 | padding: 3px 10px 5px; 83 | border-radius: 4px; 84 | } 85 | 86 | .top-menu ul li a:hover { 87 | background: var(--heading-color); 88 | color: var(--bg-color); 89 | } 90 | 91 | .theme-switch-button { 92 | border: none; 93 | background: none; 94 | cursor: pointer; 95 | cursor: hand; 96 | color: var(--heading-color); 97 | outline: 0; 98 | } 99 | 100 | .theme-switch-button svg { 101 | fill: var(--heading-color); 102 | } 103 | 104 | .amp { 105 | font-family: Baskerville, "Goudy Old Style", Palatino, "Book Antiqua", serif; 106 | font-style: italic; 107 | font-weight: normal; 108 | } 109 | 110 | h1, 111 | h2, 112 | h3, 113 | h4, 114 | h5, 115 | h6 { 116 | color: var(--heading-color); 117 | } 118 | 119 | .blog-title { 120 | font-weight: 700; 121 | font-size: 80px; 122 | letter-spacing: -0.2rem !important; 123 | text-align: center; 124 | margin: 100px 0 50px; 125 | } 126 | 127 | .dynamicCenter { 128 | text-align: center; 129 | } 130 | 131 | @media (max-width: 500px) { 132 | .blog-title { 133 | font-size: 65px; 134 | text-align: left; 135 | margin: 100px 0 50px 14px; 136 | } 137 | 138 | .dynamicCenter { 139 | text-align: center; 140 | margin: 0px 0 0px -10px; 141 | } 142 | } 143 | 144 | .writing-row a { 145 | text-decoration: none; 146 | } 147 | 148 | .writing-row span { 149 | display: inline-flex; 150 | align-items: center; 151 | } 152 | 153 | .writing-row { 154 | padding: 20px; 155 | } 156 | 157 | .writing-row a { 158 | color: var(--heading-color); 159 | display: inline-block; 160 | padding: 5px 0; 161 | position: relative; 162 | margin-top: 10px; 163 | } 164 | 165 | .writing-row a:before { 166 | content: ""; 167 | position: absolute; 168 | width: 100%; 169 | height: 3px; 170 | top: 0; 171 | left: 0; 172 | background-color: var(--highlight-color); 173 | transform: scaleX(0); 174 | transform-origin: top left; 175 | 176 | transition: transform 0.3s; 177 | } 178 | 179 | .writing-row a:after { 180 | content: ""; 181 | position: absolute; 182 | width: 100%; 183 | height: 3px; 184 | bottom: 0; 185 | right: 0; 186 | background-color: var(--highlight-color); 187 | transform: scaleX(0); 188 | transform-origin: bottom right; 189 | 190 | transition: transform 0.3s; 191 | } 192 | 193 | .writing-row a:hover:before { 194 | transform-origin: top right; 195 | transform: scaleX(1); 196 | } 197 | 198 | .writing-row a:hover:after { 199 | transform-origin: bottom left; 200 | transform: scaleX(1); 201 | } 202 | 203 | .writing-row .writing-date { 204 | padding-right: 20px; 205 | font-size: 14px; 206 | color: var(--secondary-color); 207 | } 208 | 209 | .writing-row .writing-title { 210 | font-weight: 600; 211 | font-size: 20px; 212 | line-height: 1.9em; 213 | } 214 | 215 | .writing-title-h1, 216 | .uses-h1, 217 | .about-h1 { 218 | font-weight: 700; 219 | font-size: 56px; 220 | letter-spacing: -0.1rem; 221 | margin: 50px 0 30px; 222 | } 223 | 224 | @media (max-width: 400px) { 225 | .writing-title-h1, 226 | .uses-h1, 227 | .about-h1 { 228 | font-size: 48px; 229 | } 230 | } 231 | 232 | @media (max-width: 300px) { 233 | .writing-title-h1, 234 | .uses-h1, 235 | .about-h1 { 236 | font-size: 40px; 237 | } 238 | } 239 | 240 | .about-h1 { 241 | margin-bottom: 60px; 242 | font-size: 72px; 243 | } 244 | 245 | /* writing-container { 246 | overflow-x: scroll !important; 247 | } */ 248 | 249 | .writing-container h1, 250 | .writing-container h2, 251 | .writing-container h3, 252 | .writing-container h4, 253 | .writing-container h5, 254 | .writing-container h6 { 255 | letter-spacing: -1px; 256 | } 257 | 258 | img.writings { 259 | margin: 15px 0px; 260 | } 261 | 262 | .writing-container img, 263 | img.nice { 264 | width: 100%; 265 | border-radius: 13px; 266 | -webkit-border-radius: 13px; 267 | -moz-border-radius: 13px; 268 | -ms-border-radius: 13px; 269 | -o-border-radius: 13px; 270 | translate: scale(1); 271 | -webkit-transform: scale(1); 272 | -moz-transform: scale(1); 273 | -ms-transform: scale(1); 274 | -o-transform: scale(1); 275 | transition: all 0.6s; 276 | -webkit-transition: all 0.6s; 277 | -moz-transition: all 0.6s; 278 | -ms-transition: all 0.6s; 279 | -o-transition: all 0.6s; 280 | } 281 | 282 | .writing-container img:hover, 283 | img.nice:hover { 284 | translate: scale(1.03); 285 | -webkit-transform: scale(1.03); 286 | -moz-transform: scale(1.03); 287 | -ms-transform: scale(1.03); 288 | -o-transform: scale(1.03); 289 | /* box-shadow: 3px 3px 20px var(--highlight-color); */ 290 | } 291 | 292 | .writing-container ul { 293 | margin: 0; 294 | padding: 0 0 30px 20px; 295 | list-style: circle; 296 | } 297 | 298 | .writing-container ul li { 299 | padding: 4px 0; 300 | line-height: 1.8em; 301 | font-size: 20px; 302 | color: var(--writing-body); 303 | } 304 | 305 | span.darker { 306 | color: var(--writing-body); 307 | } 308 | 309 | .writing-container > blockquote { 310 | border-left: 5px var(--highlight-color) solid; 311 | background: var(--highlight-color); 312 | font-style: italic; 313 | margin: 0; 314 | margin-bottom: 40px; 315 | padding: 30px 30px; 316 | font-size: 18px; 317 | line-height: 40px; 318 | } 319 | 320 | .writing-container > p { 321 | line-height: 1.8em; 322 | margin-bottom: 40px; 323 | font-size: 20px; 324 | color: var(--writing-body); 325 | } 326 | 327 | .writing-container *:not(pre) > code { 328 | font-family: ibm-plex-mono, Monaco, "Lucida Console"; 329 | background: var(--highlight-color); 330 | color: var(--heading-color); 331 | padding: 3px 7px 6px; 332 | border-radius: 4px; 333 | line-height: 1em; 334 | font-size: 16px; 335 | display: inline-flex; 336 | } 337 | 338 | hr { 339 | border: none; 340 | border-top: 1px var(--hr-color) solid; 341 | margin: 45px 0; 342 | } 343 | 344 | .writing-container a, 345 | .card a, 346 | .twitter-follow a { 347 | color: var(--heading-color); 348 | text-decoration: none; 349 | border-bottom: 1px var(--heading-color) dashed; 350 | } 351 | 352 | .back-button { 353 | color: var(--secondary-color); 354 | text-decoration: none; 355 | text-transform: uppercase; 356 | font-size: 14px; 357 | letter-spacing: 0px; 358 | border: 1px var(--hr-color) solid; 359 | display: inline-block; 360 | padding: 10px 20px; 361 | border-radius: 4px; 362 | font-weight: 500; 363 | } 364 | 365 | .back-button:hover { 366 | border-color: var(--secondary-color); 367 | } 368 | 369 | .copy-to-clipboard { 370 | text-align: right; 371 | padding-right: 15px; 372 | margin: -10px 0 25px; 373 | } 374 | 375 | .copy-to-clipboard button { 376 | background: none; 377 | border: none; 378 | font-size: 11px; 379 | text-transform: uppercase; 380 | font-weight: 600; 381 | color: var(--secondary-color); 382 | cursor: pointer; 383 | cursor: hand; 384 | outline: 0; 385 | padding: 0; 386 | } 387 | 388 | .copy-to-clipboard button:hover { 389 | color: var(--heading-color); 390 | } 391 | 392 | footer { 393 | text-align: center; 394 | font-size: 13px; 395 | padding: 100px 0; 396 | color: var(--secondary-color); 397 | } 398 | 399 | footer > a { 400 | text-align: center; 401 | font-size: 13px; 402 | color: var(--heading-color); 403 | text-decoration: none; 404 | border-bottom: 1px var(--heading-color) dashed; 405 | } 406 | 407 | .author { 408 | margin-bottom: 40px; 409 | } 410 | 411 | .author a { 412 | color: var(--heading-color); 413 | text-decoration: none; 414 | font-weight: 500; 415 | } 416 | 417 | .author img { 418 | border-radius: 250px; 419 | margin-right: 10px; 420 | border: 2px var(--highlight-color) solid; 421 | padding: 3px; 422 | } 423 | 424 | .author * { 425 | display: flex; 426 | align-items: center; 427 | } 428 | 429 | .twitter-follow { 430 | border: 3px var(--highlight-color) dashed; 431 | padding: 30px; 432 | border-radius: 8px; 433 | font-size: 18px; 434 | line-height: 1.9em; 435 | } 436 | 437 | .card { 438 | background: var(--highlight-color); 439 | color: var(--heading-color) !important; 440 | box-shadow: 3px 3px 20px var(--highlight-color) !important; 441 | padding: 30px 40px; 442 | border-radius: 25px; 443 | font-size: 18px; 444 | /* font-weight: bold; */ 445 | line-height: 1.9em; 446 | -webkit-border-radius: 25px; 447 | -moz-border-radius: 25px; 448 | -ms-border-radius: 25px; 449 | -o-border-radius: 25px; 450 | } 451 | 452 | .card .revue-form-group { 453 | padding: 10px 0; 454 | } 455 | 456 | .card form { 457 | margin-top: -10px; 458 | } 459 | 460 | .revue-embed label { 461 | font-weight: 100 !important; 462 | } 463 | 464 | .substackIframe { 465 | background: rgb(241, 241, 241); 466 | color: var(--heading-color) !important; 467 | /* box-shadow: 3px 3px 20px var(--highlight-color) !important; */ 468 | /* padding: 30px 40px; */ 469 | border-radius: 25px; 470 | border: 3px solid var(--highlight-color) !important; 471 | font-size: 18px; 472 | font-weight: bold; 473 | line-height: 1.9em; 474 | -webkit-border-radius: 25px; 475 | -moz-border-radius: 25px; 476 | -ms-border-radius: 25px; 477 | -o-border-radius: 25px; 478 | } 479 | 480 | .card h1 { 481 | margin-top: 0px; 482 | margin-bottom: 0px; 483 | } 484 | 485 | form { 486 | display: flex; 487 | flex-direction: column; 488 | justify-content: space-between; 489 | } 490 | 491 | form p { 492 | font-weight: normal; 493 | color: var(--heading-color); 494 | } 495 | 496 | form input { 497 | box-sizing: border-box; 498 | -webkit-box-sizing: border-box; 499 | -moz-box-sizing: border-box; 500 | -ms-box-sizing: border-box; 501 | -o-box-sizing: border-box; 502 | 503 | width: 100% !important; 504 | padding: 10px 20px; 505 | font-size: 18px; 506 | border: none; 507 | border-radius: 20px; 508 | -webkit-border-radius: 20px; 509 | -moz-border-radius: 20px; 510 | -ms-border-radius: 20px; 511 | -o-border-radius: 20px; 512 | background-color: #fff; 513 | } 514 | 515 | form input:focus { 516 | outline: none; 517 | box-shadow: 0px 0px 1px 2px var(--heading-color); 518 | } 519 | 520 | .uses-intro, 521 | .about-intro { 522 | font-size: 20px; 523 | line-height: 1.8em; 524 | color: var(--secondary-color); 525 | } 526 | 527 | .uses-intro a, 528 | .about-intro a { 529 | color: var(--heading-color); 530 | text-decoration: none; 531 | border-bottom: 1px var(--heading-color) dashed; 532 | } 533 | 534 | /* .uses-intro a:hover, 535 | .about-intro a:hover { 536 | color: var(--heading-color); 537 | text-decoration: none; 538 | border-bottom: 1px var(--heading-color) dashed; 539 | } */ 540 | 541 | .writing-image { 542 | object-fit: cover; 543 | width: 100%; 544 | height: 600px; 545 | /* border-radius: 25px; */ 546 | /* margin-top: 40px; */ 547 | } 548 | 549 | @media (max-width: 1000px) { 550 | .writing-image { 551 | width: 100%; 552 | height: auto; 553 | object-fit: contain; 554 | } 555 | } 556 | 557 | .about-intro h3 { 558 | font-weight: 600; 559 | } 560 | 561 | .about-avatar { 562 | width: 60%; 563 | margin-bottom: 30px; 564 | border-radius: 100%; 565 | position: fixed; 566 | z-index: -1; 567 | bottom: -40%; 568 | left: -10%; 569 | opacity: 0.1; 570 | } 571 | 572 | .uses-list { 573 | list-style: none; 574 | margin: 40px 0 80px; 575 | padding: 0 15px; 576 | font-family: ibm-plex-mono, Monaco, "Lucida Console"; 577 | } 578 | 579 | .uses-list li.head { 580 | font-weight: 600; 581 | font-size: 32px; 582 | margin-bottom: 20px; 583 | box-shadow: inset 0 -16px 0 var(--highlight-color); 584 | display: inline-block; 585 | /* color: var(--heading-color); */ 586 | } 587 | 588 | .uses-list li:not(.head) { 589 | padding: 15px 0; 590 | } 591 | 592 | .uses-list li:not(.head):before { 593 | content: "//"; 594 | color: var(--uses-secondary-color); 595 | padding-right: 10px; 596 | } 597 | 598 | .uses-list li:not(.head) { 599 | color: var(--uses-secondary-color); 600 | } 601 | 602 | .uses-list li:not(.head) a { 603 | color: var(--heading-color); 604 | font-weight: 600; 605 | text-decoration: none; 606 | margin-right: 15px; 607 | padding-bottom: 3px; 608 | } 609 | 610 | .uses-list li:not(.head) a:hover { 611 | border-bottom: 1px var(--heading-color) dashed; 612 | } 613 | 614 | [data-icon="Next.js"] svg { 615 | fill: var(--heading-color); 616 | } 617 | 618 | [data-icon="Apollo GraphQL"] svg { 619 | fill: var(--heading-color); 620 | } 621 | 622 | [data-icon="GitHub"] svg { 623 | fill: var(--heading-color); 624 | } 625 | 626 | [data-icon="ZEIT"] svg { 627 | fill: var(--heading-color); 628 | } 629 | 630 | [data-icon="DEV.TO"] svg { 631 | fill: var(--heading-color); 632 | } 633 | 634 | /* [data-icon="LinkedIn"] svg { 635 | fill: var(--heading-color); 636 | } */ 637 | 638 | /* [data-icon="Twitter"] svg { 639 | fill: var(--heading-color); 640 | } */ 641 | 642 | /* [data-icon="YouTube"] svg { 643 | fill: var(--heading-color); 644 | } */ 645 | 646 | /* [data-icon="Facebook"] svg { 647 | fill: var(--heading-color); 648 | } */ 649 | 650 | .stack-name { 651 | font-family: ibm-plex-mono, Monaco, "Lucida Console"; 652 | font-size: 13px; 653 | color: var(--heading-color); 654 | } 655 | 656 | /* Override scroll progress */ 657 | .writing-progress > div { 658 | background: var(--highlight-color) !important; 659 | margin: 0 -8px !important; 660 | } 661 | 662 | @media only screen and (max-width: 960px) { 663 | .writing-row .writing-date { 664 | text-align: left; 665 | padding: 10px 0; 666 | } 667 | 668 | .uses-list li:not(.head) span { 669 | display: block; 670 | margin-top: 10px; 671 | } 672 | 673 | .uses-list li:not(.head) { 674 | padding-bottom: 25px; 675 | } 676 | 677 | .stack-name { 678 | font-size: 10px; 679 | } 680 | 681 | .text-transition { 682 | display: block !important; 683 | } 684 | 685 | .about-avatar { 686 | width: 120%; 687 | } 688 | 689 | .writing-row a { 690 | margin-top: 0; 691 | } 692 | } 693 | 694 | .writing-row { 695 | background: var(--bg-color); 696 | /* border: 1px solid var(--writing-body); */ 697 | border-radius: 13px; 698 | -webkit-border-radius: 13px; 699 | -moz-border-radius: 13px; 700 | -ms-border-radius: 13px; 701 | -o-border-radius: 13px; 702 | margin-top: 20px; 703 | } 704 | 705 | [data-testid="grid-gallery-item_viewport"] { 706 | border-radius: 20px !important; 707 | -webkit-border-radius: 20px !important; 708 | -moz-border-radius: 20px !important; 709 | -ms-border-radius: 20px !important; 710 | -o-border-radius: 20px !important; 711 | filter: grayscale(100%); 712 | transition: filter 0.5s ease; 713 | -webkit-filter: grayscale(100%); 714 | } 715 | 716 | [data-testid="grid-gallery-item_viewport"]:hover { 717 | filter: grayscale(0%); 718 | } 719 | 720 | -------------------------------------------------------------------------------- /packages/web/constants/Stack.js: -------------------------------------------------------------------------------- 1 | import { META } from './metadata' 2 | 3 | export const TECH = { 4 | title: 'Technologies I work with', 5 | data: [ 6 | 'HTML5', 7 | 'CSS3', 8 | 'JavaScript', 9 | 'React', 10 | 'Next.js', 11 | 'GraphQL', 12 | 'TypeScript', 13 | 'Apollo GraphQL', 14 | 'Node.js', 15 | 'Docker', 16 | 'Electron', 17 | 'DigitalOcean', 18 | 'Redux', 19 | 'MongoDB', 20 | { 21 | title: 'Vercel', 22 | svg: `Vercel`, 23 | }, 24 | { 25 | title: 'Amazon S3', 26 | hex: '#569A31', 27 | svg: `Amazon S3`, 28 | }, 29 | { 30 | title: 'AWS EC2', 31 | svg: `Amazon AWS`, 32 | }, 33 | { 34 | title: 'Solidity', 35 | hex: '#3C3C3D', 36 | svg: `Ethereum`, 37 | }, 38 | { 39 | title: 'NPM', 40 | hex: '#CB3837', 41 | svg: `npm`, 42 | }, 43 | { 44 | title: 'PayPal', 45 | hex: '#00457C', 46 | svg: `PayPal`, 47 | }, 48 | { 49 | title: 'Stripe', 50 | hex: '#008CDD', 51 | svg: `Stripe`, 52 | }, 53 | { 54 | title: 'PostgreSQL', 55 | hex: '#4169E1', 56 | svg: `PostgreSQL`, 57 | }, 58 | { 59 | title: 'Redis', 60 | hex: '#DC382D', 61 | svg: `Redis`, 62 | }, 63 | { 64 | title: 'Sentry', 65 | hex: '#362D59', 66 | svg: `Sentry`, 67 | }, 68 | ], 69 | } 70 | 71 | export const SOCIAL = { 72 | title: 'Available here', 73 | data: [ 74 | { platform: 'Twitter', link: `https://twitter.com/${META.social.twitter}` }, 75 | { 76 | platform: 'LinkedIn', 77 | link: `https://linkedin.com/in/${META.social.linkedin}`, 78 | }, 79 | { 80 | platform: 'GitHub', 81 | link: `https://github.com/${META.social.github}`, 82 | }, 83 | { 84 | platform: 'YouTube', 85 | link: `https://www.youtube.com/${META.social.youtube}`, 86 | }, 87 | { 88 | platform: 'Twitch', 89 | link: `/twitch`, 90 | }, 91 | { 92 | platform: 'Instagram', 93 | link: `https://www.instagram.com/${META.social.instagram}`, 94 | }, 95 | // { 96 | // platform: 'Facebook', 97 | // link: `https://www.facebook.com/${META.social.facebook}`, 98 | // }, 99 | ], 100 | } 101 | 102 | export const PROJECTS = { 103 | title: 'Experience', 104 | data: [ 105 | { 106 | image: 107 | 'https://pbs.twimg.com/profile_images/1658696039195627522/LiQoMntF_400x400.jpg', 108 | name: 'Airchat', 109 | link: 'https://getairchat.com', 110 | description: ( 111 | <> 112 | 113 | Software Engineer, August 2023 - Feb 2024 114 | 115 |
Re-established and improved the Airchat Web Client, better Link 116 | Previews, helped the app get 2k+ installs via CTA and its accurate 117 | tracking, worked with the Java GRPC Backend, owned certain parts of 118 | the Airchat iOS app. 119 | 120 | ), 121 | }, 122 | { 123 | image: 'https://www.deva.me/favicon_384.png', 124 | name: 'Deva.me', 125 | link: 'https://deva.me/creators', 126 | description: ( 127 | <> 128 | 129 | Founding Engineer, February - August 2023 130 | 131 |
We aim to provide everyone a second voice, and create a massive 132 | supply of automated representative intellect that shall transcend into 133 | new learning experiences for the masses. 134 | 135 | ), 136 | }, 137 | // { 138 | // image: 139 | // 'https://media.licdn.com/dms/image/C4E0BAQEHs1C2gRRukA/company-logo_200_200/0/1620129594556?e=2147483647&v=beta&t=OJNnxV2Tz6cwTXmAk_Tm7P-8v7-MlLPsCsd5ugCoxFg', 140 | // name: 'Stealth Startup', 141 | // // link: 'https://app.serendipity.lol', 142 | // description: ( 143 | // <> 144 | // 145 | // Consultant & Tech Lead, March 2023 - Present 146 | // 147 | //
Working (in & from India) with a Web3 + AI startup, with a 148 | // globally operated remote team. 149 | // 150 | // ), 151 | // }, 152 | { 153 | image: 'https://i.ibb.co/Z8gmths/serendipity.jpg', 154 | // width: 100, 155 | name: 'Serendipity', 156 | // link: '#', // 'https://app.serendipity.lol', 157 | description: ( 158 | <> 159 | 160 | Software Engineer, August 2022 - Februrary 2023 161 | 162 |
Serendipity approaches Note Taking in a whole new way, by 163 | focusing on People, not just Notes. It is a note-taking app that helps 164 | you remember the people you meet, and the things you learn from them, 165 | and creates you a beautiful looking network graph. 166 |
167 |
168 |
175 |