From 4304c1d38c830096dbcf69190033cff36a4e51bc Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 12 Oct 2021 15:27:56 -0400 Subject: [PATCH 01/40] feat: rebuild site using Next.js with new design and more documentation --- site/.eslintrc.yml | 45 +- site/.gitignore | 86 +- site/README.md | 240 +- site/babel.config.js | 18 + site/components/ApiHeading.jsx | 154 + site/components/Head.jsx | 224 + site/components/Hero.jsx | 81 + site/components/Hero.module.css | 108 + site/components/Link.jsx | 77 + site/components/LinkedHeading.jsx | 22 + site/components/Logo.jsx | 52 + site/components/Terminal.jsx | 32 + site/components/Terminal.module.css | 73 + site/components/VersionSelect.jsx | 68 + site/components/VersionSelect.module.css | 7 + site/components/base/Footer.jsx | 51 + site/components/base/Footer.module.css | 97 + site/components/base/Header.jsx | 29 + site/components/base/Header.module.css | 37 + .../contributing.mdx} | 20 +- .../development.mdx} | 27 +- site/docs/faqs.mdx | 24 + site/docs/sponsorship.mdx | 15 + site/docs/v3/_nav.js | 79 + site/{src/data/20-api.md => docs/v3/api.mdx} | 199 +- .../v3/custom-log-levels.mdx} | 60 +- site/docs/v3/debug-logs.mdx | 59 + .../v3/dynamic-metadata.mdx} | 45 +- .../v3/enhanced-tags.mdx} | 18 +- site/docs/v3/examples.mdx | 123 + site/docs/v3/future.mdx | 38 + site/docs/v3/index.mdx | 40 + site/docs/v3/install.mdx | 77 + .../v3/log-handler.mdx} | 23 +- site/docs/v3/message-handling.mdx | 29 + site/docs/v3/metadata.mdx | 147 + site/docs/v3/pretty-printing.mdx | 48 + site/docs/v3/silent-mode.mdx | 34 + site/docs/v3/supported-environments.mdx | 20 + site/docs/v3/tagging.mdx | 82 + site/docs/v3/troubleshooting.mdx | 49 + .../v3/upgrade-guide.mdx} | 21 +- site/docs/versions.js | 6 + site/gatsby-config.js | 75 - site/gatsby-node.js | 28 - site/jsconfig.json | 36 +- site/next-sitemap.js | 7 + site/next.config.js | 85 + site/package-lock.json | 49007 +++++----------- site/package.json | 88 +- site/pages/404.jsx | 47 + site/pages/404.module.css | 73 + site/pages/Home.module.css | 75 + site/pages/_app.jsx | 64 + site/pages/_document.jsx | 22 + site/pages/api/npm-version.js | 19 + site/pages/docs/[version]/[[...slug]].jsx | 198 + site/pages/index.js | 134 + site/postcss.config.js | 8 + .../android-chrome-192x192.png | Bin .../android-chrome-512x512.png | Bin site/{static => public}/apple-touch-icon.png | Bin site/{static => public}/browserconfig.xml | 0 site/{static => public}/favicon-16x16.png | Bin site/{static => public}/favicon-32x32.png | Bin site/{static => public}/favicon.ico | Bin .../google8081710f83a021fc.html | 0 site/{static => public}/mstile-150x150.png | Bin site/{static => public}/mstile-70x70.png | Bin site/public/og-image.png | Bin 0 -> 26051 bytes site/{static => public}/safari-pinned-tab.svg | 0 site/{static => public}/site.webmanifest | 4 +- site/src/components/docs/content.js | 79 - site/src/components/docs/sidebar.js | 195 - site/src/components/docs/ui/alert.js | 41 - site/src/components/docs/ui/header.js | 317 - site/src/components/docs/ui/heading.js | 75 - site/src/components/hero.js | 59 - site/src/components/inject.js | 16 - site/src/components/ui/link.js | 49 - site/src/components/ui/terminal.js | 105 - site/src/data/01-getting-started.md | 110 - site/src/layouts/elements/footer.js | 42 - site/src/layouts/elements/head.js | 51 - site/src/layouts/elements/header.js | 261 - site/src/layouts/index.js | 53 - site/src/pages/docs/index.js | 11 - site/src/pages/index.js | 92 - site/src/styles/components.js | 19 - site/src/styles/global/html.js | 90 - site/src/styles/global/index.js | 55 - site/src/styles/global/link.js | 38 - site/src/styles/prism.js | 202 - site/src/styles/theme.js | 39 - site/src/styles/typography.js | 13 - site/src/templates/docPage.js | 47 - site/src/utils.js | 10 - site/static/CNAME | 1 - site/static/og-image.png | Bin 29201 -> 0 bytes site/styles/globals.css | 8 + site/styles/partials/base.css | 74 + site/styles/partials/docs.css | 451 + site/styles/partials/torchlight.css | 54 + site/styles/partials/typography.css | 117 + site/tailwind.config.js | 146 + site/utils/ApiVersionContext.js | 32 + site/utils/BreakpointContext.js | 93 + site/utils/docs.js | 67 + site/utils/remarkCodeTabs.js | 121 + 109 files changed, 17692 insertions(+), 38295 deletions(-) create mode 100644 site/babel.config.js create mode 100644 site/components/ApiHeading.jsx create mode 100644 site/components/Head.jsx create mode 100644 site/components/Hero.jsx create mode 100644 site/components/Hero.module.css create mode 100644 site/components/Link.jsx create mode 100644 site/components/LinkedHeading.jsx create mode 100644 site/components/Logo.jsx create mode 100644 site/components/Terminal.jsx create mode 100644 site/components/Terminal.module.css create mode 100644 site/components/VersionSelect.jsx create mode 100644 site/components/VersionSelect.module.css create mode 100644 site/components/base/Footer.jsx create mode 100644 site/components/base/Footer.module.css create mode 100644 site/components/base/Header.jsx create mode 100644 site/components/base/Header.module.css rename site/{src/data/35-contributing.md => docs/contributing.mdx} (83%) rename site/{src/data/30-development.md => docs/development.mdx} (87%) create mode 100644 site/docs/faqs.mdx create mode 100644 site/docs/sponsorship.mdx create mode 100644 site/docs/v3/_nav.js rename site/{src/data/20-api.md => docs/v3/api.mdx} (75%) rename site/{src/data/02-custom-log-levels.md => docs/v3/custom-log-levels.mdx} (65%) create mode 100644 site/docs/v3/debug-logs.mdx rename site/{src/data/03-dynamic-metadata.md => docs/v3/dynamic-metadata.mdx} (57%) rename site/{src/data/04-enhanced-tags.md => docs/v3/enhanced-tags.mdx} (85%) create mode 100644 site/docs/v3/examples.mdx create mode 100644 site/docs/v3/future.mdx create mode 100644 site/docs/v3/index.mdx create mode 100644 site/docs/v3/install.mdx rename site/{src/data/05-log-handler.md => docs/v3/log-handler.mdx} (76%) create mode 100644 site/docs/v3/message-handling.mdx create mode 100644 site/docs/v3/metadata.mdx create mode 100644 site/docs/v3/pretty-printing.mdx create mode 100644 site/docs/v3/silent-mode.mdx create mode 100644 site/docs/v3/supported-environments.mdx create mode 100644 site/docs/v3/tagging.mdx create mode 100644 site/docs/v3/troubleshooting.mdx rename site/{src/data/40-migrating-to-v3.md => docs/v3/upgrade-guide.mdx} (68%) create mode 100644 site/docs/versions.js delete mode 100644 site/gatsby-config.js delete mode 100644 site/gatsby-node.js create mode 100644 site/next-sitemap.js create mode 100644 site/next.config.js create mode 100644 site/pages/404.jsx create mode 100644 site/pages/404.module.css create mode 100644 site/pages/Home.module.css create mode 100644 site/pages/_app.jsx create mode 100644 site/pages/_document.jsx create mode 100644 site/pages/api/npm-version.js create mode 100644 site/pages/docs/[version]/[[...slug]].jsx create mode 100644 site/pages/index.js create mode 100644 site/postcss.config.js rename site/{static => public}/android-chrome-192x192.png (100%) rename site/{static => public}/android-chrome-512x512.png (100%) rename site/{static => public}/apple-touch-icon.png (100%) rename site/{static => public}/browserconfig.xml (100%) rename site/{static => public}/favicon-16x16.png (100%) rename site/{static => public}/favicon-32x32.png (100%) rename site/{static => public}/favicon.ico (100%) rename site/{static => public}/google8081710f83a021fc.html (100%) rename site/{static => public}/mstile-150x150.png (100%) rename site/{static => public}/mstile-70x70.png (100%) create mode 100644 site/public/og-image.png rename site/{static => public}/safari-pinned-tab.svg (100%) rename site/{static => public}/site.webmanifest (84%) delete mode 100644 site/src/components/docs/content.js delete mode 100644 site/src/components/docs/sidebar.js delete mode 100644 site/src/components/docs/ui/alert.js delete mode 100644 site/src/components/docs/ui/header.js delete mode 100644 site/src/components/docs/ui/heading.js delete mode 100644 site/src/components/hero.js delete mode 100644 site/src/components/inject.js delete mode 100644 site/src/components/ui/link.js delete mode 100644 site/src/components/ui/terminal.js delete mode 100644 site/src/data/01-getting-started.md delete mode 100644 site/src/layouts/elements/footer.js delete mode 100644 site/src/layouts/elements/head.js delete mode 100644 site/src/layouts/elements/header.js delete mode 100644 site/src/layouts/index.js delete mode 100644 site/src/pages/docs/index.js delete mode 100644 site/src/pages/index.js delete mode 100644 site/src/styles/components.js delete mode 100644 site/src/styles/global/html.js delete mode 100644 site/src/styles/global/index.js delete mode 100644 site/src/styles/global/link.js delete mode 100644 site/src/styles/prism.js delete mode 100644 site/src/styles/theme.js delete mode 100644 site/src/styles/typography.js delete mode 100644 site/src/templates/docPage.js delete mode 100644 site/src/utils.js delete mode 100644 site/static/CNAME delete mode 100644 site/static/og-image.png create mode 100644 site/styles/globals.css create mode 100644 site/styles/partials/base.css create mode 100644 site/styles/partials/docs.css create mode 100644 site/styles/partials/torchlight.css create mode 100644 site/styles/partials/typography.css create mode 100644 site/tailwind.config.js create mode 100644 site/utils/ApiVersionContext.js create mode 100644 site/utils/BreakpointContext.js create mode 100644 site/utils/docs.js create mode 100644 site/utils/remarkCodeTabs.js diff --git a/site/.eslintrc.yml b/site/.eslintrc.yml index 3fb6262..9a677d1 100644 --- a/site/.eslintrc.yml +++ b/site/.eslintrc.yml @@ -1,28 +1,30 @@ --- root: true +ignorePatterns: + - 'node_modules/**/*' + - '.next' + env: browser: true node: true parser: '@babel/eslint-parser' parserOptions: - requireConfigFile: false - sourceType: module + ecmaVersion: 12 ecmaFeatures: jsx: true -globals: - __PATH_PREFIX__: true - babelOptions: - plugins: - - 'babel-plugin-styled-components' - presets: - - 'babel-preset-gatsby' - extends: - - 'xo-space/esnext' + - 'xo-space' - 'xo-react/space' + - 'plugin:jsx-a11y/recommended' + - 'plugin:@next/next/recommended' + +plugins: + - '@babel' + - 'jsx-a11y' + - 'tailwindcss' settings: react: @@ -30,15 +32,34 @@ settings: rules: array-element-newline: off + arrow-body-style: off capitalized-comments: off + comma-dangle: ['error', 'never'] curly: ['error', 'multi-line'] + func-names: ['error', 'as-needed'] object-curly-spacing: ['error', 'always'] no-multiple-empty-lines: ['error', { max: 2, maxEOF: 1, maxBOF: 0 }] no-negated-condition: off keyword-spacing: ['error', { overrides: { if: { after: false }, for: { after: false }, while: { after: false }, catch: { after: false } } }] react/jsx-tag-spacing: ['error', { closingSlash: 'never', beforeSelfClosing: 'always', afterOpening: 'never', beforeClosing: 'never' }] - react/no-danger: off react/boolean-prop-naming: off react/no-array-index-key: off react/jsx-fragments: off react/no-unescaped-entities: ['error', { forbid: ['>', '}' ] }] + react/state-in-constructor: ['error', 'always'] + react/static-property-placement: ['error', 'property assignment'] + react/prop-types: ['error', { ignore: ['className', 'children'] }] + jsx-a11y/no-onchange: off + jsx-a11y/alt-text: ['warn', { elements: ['img'], img: ['Image', 'Picture'] }] + tailwindcss/no-contradicting-classname: ['error'] + +overrides: + - files: '**/*.mdx' + parser: 'eslint-mdx' + extends: + - 'plugin:mdx/recommended' + rules: + indent: off + spaced-comment: off + react/jsx-indent: off + semi: off diff --git a/site/.gitignore b/site/.gitignore index f813275..822f985 100644 --- a/site/.gitignore +++ b/site/.gitignore @@ -1,69 +1,31 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript +# dependencies +/node_modules -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release +# testing +/coverage -# Dependency directories -node_modules/ -jspm_packages/ +# next.js +/.next/ +/out/ -# Typescript v1 declaration files -typings/ +# production +/build -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# dotenv environment variable files -.env* +# misc +.DS_Store +*.pem -# gatsby files -.cache/ -public +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* -# Mac files -.DS_Store +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local -# Yarn -yarn-error.log -.pnp/ -.pnp.js -# Yarn Integrity file -.yarn-integrity +# auto-generated sitemap and robots.txt +public/sitemap.xml +public/robots.txt diff --git a/site/README.md b/site/README.md index 761ad64..3a6dde7 100644 --- a/site/README.md +++ b/site/README.md @@ -1,243 +1,49 @@ -# Lambda Log Website +# lambdalog.dev Since Lambda Log has become more widely used and is growing, it was time to upgrade the documentation and web presence of this package. This website provides a brief overview of the package along with much more extensive documentation than previously with just a README file. Here's an overview of the website: -- Statically-generated using [Gatsby](https://www.gatsbyjs.com/). -- Hosted on Github Pages, deployed to the [gh-pages](https://github.com/KyleRoss/node-lambda-log/tree/gh-pages) branch. -- CNAME'd as [lambdalog.js.org](https://lambdalog.js.org). -- All content and documentation is manually managed (for now). -- Custom design; does not use any UI framework. +- Statically-generated using [Next.js](https://nextjs.org/). +- Hosted on [Vercel](https://vercel.com/). +- Top-level domain: [lambdalog.dev](https://lambdalog.dev) +- All content is manually managed using MDX files. +- Custom design using [TailwindCSS](https://tailwindcss.com). _No UI Kits._ -**Future Plans** +**This is version 2 of the website.** -When time allows or if the community is able to assist, these are the future plans for the website: +The first version of the website used Gatsby and Styled Components which was messy and had multiple issues when running on Github pages. Instead of trying to jerry-rig the old site, I've completely rebuilt the site from the ground-up using Next.js and TailwindCSS with a more user-friendly and accessible design along with much better documentation. -- Automatic generation of API documentation. -- UI/UX fixes. -- Desktop browser support testing. -- Mobile browser support testing. -- Cleanup of code, styles, and markup. +## Getting Started -## Files and Content +1. Ensure you have Node v14+ installed on your machine. -All files for the website lives inside of this directory (`/site`) and is a separate entity from the Lambda Log NPM package itself. +2. Clone this repo to your local machine and `cd` to the `node-lambda-log/site` directory. -``` -. -└── site/ - ├── src/ - │ ├── components/ - │ │ ├── docs/ - │ │ ├── ui/ - │ │ ├── hero.js - │ │ └── inject.js - │ ├── data/ - │ ├── layouts/ - │ │ ├── elements/ - │ │ │ ├── footer.js - │ │ │ ├── head.js - │ │ │ └── header.js - │ │ └── index.js - │ ├── pages/ - │ │ ├── docs/ - │ │ │ └── index.js - │ │ └── index.js - │ ├── styles/ - │ │ ├── global/ - │ │ ├── components.js - │ │ ├── prism.js - │ │ ├── theme.js - │ │ └── typography.js - │ ├── templates/ - │ │ └── docPage.js - │ └── utils.js - ├── static/ - ├── gatsby-config.js - └── gatsby-node.js -``` +3. Create a `.env` file in the root of the site and insert the following: -_Important files/directories only shown above._ + ``` + TORCHLIGHT_TOKEN= + ``` + **Note:** In order to render the code blocks locally, you must [sign up](https://app.torchlight.dev/register?plan=free_month) for a free API Key with [Torchlight](https://torchlight.dev/). Paste your API Key as the value of the environment variable above. +4. Run `npm i` in terminal to install the dependencies. -- `src/` — The source files for the Gatsby site. +5. Start the application locally using `npm run dev`. - - `src/components/` — All the reusable components used on the website. +6. Open [http://localhost:3000](http://localhost:3000) with your browser to see the website. - - `src/components/docs/` — Contains reusable components for the documentation pages. - - `src/components/ui/` — Contains reusable components used throughout the site. - - `src/components/hero.js` — Hero component used on the home page. - - `src/components/inject.js` — React injector component to use with rehype-react. - - `src/data/` — Contains all of the content for the documentation pages. - - `src/data/*.md` — Each markdown file is automatically parsed by Gatsby. The files are named using: +## Documentation Content - ``` - 00-name.md - ``` +The MDX files containing the content of the documentation pages are located in `site/docs`. - Where `00` is the priority/order of the file and `name` is a friendly name for the file. - - `src/layouts/` — Contains the layouts and elements for the layouts of the website. Every page utilizes a layout. - - `src/layouts/elements/` — Specific elements used by a layout. This includes a header, footer, and tags to be added to the `` of the page. - - `src/layouts/index.js` — The main layout used for the entire site. - - - `src/pages/` — Where all the pages for the site are defined. - - - `src/pages/docs/index.js` — The main documentation page. This is simply a redirect to the first documentation page. - - `src/pages/index.js` — The home page. - - - `src/styles/` — Styles used throughout the site. Uses styled-components. - - - `src/styles/global/*.js` — Global site styles. Which includes styles for various HTML tags, links, etc. - - `src/styles/components.js` — Generic styles that are used with various components throughout the site. - - `src/styles/prism.js` — Styles for prism.js (code block highlighting). - - `src/styles/theme.js` — The styled-components theme for the site. See [Branding](#branding) below. - - `src/styles/typography.js` — Configuration for typography.js. - - - `src/templates/` — Templates for dynamically-created pages. - - - `src/templates/docPage.js` — Template/layout used for all documentation pages generated from the markdown files. - - - `src/utils.js` — Utility functions used throughout the site. - -- `static/` — Static files and folder to be outputted to the root on build. - -- `gatsby-config.js` — Configuration file for Gatsby. - -- `gatsby-node.js` — Functions for Gatsby. Used to dynamically create doc pages from the markdown files. - - - -### Content - -The content for the homepage is hardcoded into the page itself within `src/pages/index.js`. The documentation pages are dynamically generated from markdown files within `src/data/`. If additional markdown files are added within that directory, they will automatically be added to the site. - -#### Markdown Files - -The markdown files are named as `ORDER-NAME.md` where `ORDER` is a 2-digit number and `NAME` is a friendly name for the page. Each of the files **must** contain the following front matter: - -``` ---- -title: Title of Page -slug: title-of-page -order: 10 ---- - -# Title of Page -Content... -``` - -- `title` — The title of the page. Used to generate the navigation and ``. -- `slug` — A url-friendly slug for the page. -- `order` — The order of the page. Should be the same number that is prefixed on the file name. - - - -HTML and all markdown syntax is supported within the files. In addition, rehype-react is used to take the AST that is generated by remark and use it directly within a React component without needing to use `dangerouslySetInnerHTML`. This also allows us to use React components within the markdown itself. - -#### Custom Components in Markdown - -Using rehype-react, we are able to specify HTML tags and map them to a custom React component. The following components are implemented: - -##### `<a>` or `[text](link)` — CustomLink - -All links added within the markdown files are mapped to the CustomLink component (`src/components/ui/link.js`). This handles adding `rel` attributes to external links, analytics tracking, etc. - -##### `<h>` — Header - -This is used exclusively for formatting a heading for API documentation by rendering the `src/components/docs/ui/header.js` component. This will generate a type badge, format/style the content, and add a returns if applicable. The available props/attributes are: - -| Prop/Attribute | Type | Required? | Description | -| -------------- | ------ | --------- | ------------------------------------------------------------ | -| `type` | String | No | The type of the header. Must be one of `class`, `property`, `event`, `function`, `module`, `getter`, or `setter`. | -| `scope` | String | No | The scope of the particular API element. Must be one of `static` or `instance`. | -| `text` | String | Yes | The text to display in the header. | -| `returns` | String | No | The returns for the API element. Prepend a `#` to automatically link. | -| `link` | String | No | Override the link for the returns. | - -```markdown -## <h type="function" text="obj.myFunction(arg1, [arg2], [arg3])" returns="Boolean"> -``` - -##### `<alert>` — Alert - -A simple colored box on the page to highlight specific information that may be important. Renders `src/components/docs/ui/alert.js`. The props/attributes are: - -| Prop/Attribute | Type | Required? | Description | -| -------------- | ------ | --------- | ------------------------------------------------------------ | -| `type` | String | Yes | The type of the alert. Must be one of `info`, `warn`, `success`, or `plain`. | -| `children` | Node | Yes | The content of the alert. | - -```markdown -<alert type="info">This is an info alert.</alert> -``` - -##### `<h*>` — Heading - -Applies to all heading tags (`<h1>` - `<h6>`). Renders the `src/components/docs/ui/heading.js` component. Adds an `id` attribute to the HTML tag for URL fragments. - - - -## Local Development - -It easy to get the site running locally for development. - -1. Clone this repo locally. -2. `cd` to `path/to/node-lambda-log/site`. -3. Install dependencies with: `npm i`. -4. Run `npm run develop`. The site will build and a dev server will be started on port `8000`. - - - -## Build and Deployment - -This site is built and deployed every time there is a push or merge to the `master` branch. This happens through Github Actions, particularly the [site workflow](https://github.com/KyleRoss/node-lambda-log/blob/master/.github/workflows/site.yml). Once the site is built, it will be automatically deployed to the [gh-pages](https://github.com/KyleRoss/node-lambda-log/tree/gh-pages) branch. - - - -## Branding - -While the branding is not the most important aspect of a documentation site, this outlines the current branding/styles of the website: - -### Fonts - -The site uses 2 web fonts for all of the fonts on the page; one sans-serif, the other is monospace. Instead of loading these fonts from Google Fonts, they are imported locally from the `@fontsource` repo. - -**Sans-Serif:** [Fira Sans](https://fonts.google.com/specimen/Fira+Sans) — Used for all text throughout the site. - -**Monospace:** [Fira Code](https://fonts.google.com/specimen/Fira+Code) — Used for inline code and code blocks throughout the site. - -### Colors - -**Note:** Not all colors are used on the site and are available for future use. - -| Color | Hex Code | Name | Description | -| ------------------------------------------------------------ | --------- | --------- | ------------------------------------------------------------ | -| ![Black](https://via.placeholder.com/50.png/2e3135?text=+) | `#2e3135` | black | The color used for text on the site. | -| ![White](https://via.placeholder.com/50.png/fff?text=+) | `#ffffff` | white | | -| ![Primary](https://via.placeholder.com/50.png/eb6125?text=+) | `#eb6125` | primary | The primary color. Used in the logo, links, and various elements. | -| ![Secondary](https://via.placeholder.com/50.png/4f5d75?text=+) | `#4f5d75` | secondary | | -| ![Red](https://via.placeholder.com/50.png/E84855?text=+) | `#e84855` | red | | -| ![Green](https://via.placeholder.com/50.png/70AE6E?text=+) | `#70ae6e` | green | | -| ![Blue](https://via.placeholder.com/50.png/2b9dd4?text=+) | `#2b9dd4` | blue | | -| ![Yellow](https://via.placeholder.com/50.png/FDCA40?text=+) | `#fdca40` | yellow | | -| ![Purple](https://via.placeholder.com/50.png/6247AA?text=+) | `#6247aa` | purple | | -| ![Pink](https://via.placeholder.com/50.png/ce2389?text=+) | `#ce2389` | pink | | -| ![Gray](https://via.placeholder.com/50.png/bfc0c0?text=+) | `#bfc0c0` | gray | Used for different accents and separators. Variations of this gray is used throughout the site. | -| ![Dark](https://via.placeholder.com/50.png/2d3142?text=+) | `#2d3142` | dark | Dark blue used as the header and footer background. | - -#### Color Variations - -Currently, all colors variations are generated in-place where needed based off of the theme colors above. [Polished.js](https://polished.js.org/) is used for creating the variations. - -### Icons - -All the icons being used on the site is imported from [react-icons](https://react-icons.github.io/react-icons/). +## Deployment +This site is built and deployed every time there is a push or merge to the `master` branch. The build process takes place on Vercel and is deployed automatically. diff --git a/site/babel.config.js b/site/babel.config.js new file mode 100644 index 0000000..0c5ab3f --- /dev/null +++ b/site/babel.config.js @@ -0,0 +1,18 @@ +module.exports = { + plugins: [ + [ + 'module-resolver', { + alias: { + '@': './', + '@components': './components', + '@styles': './styles', + '@public': './public', + '@utils': './utils' + } + } + ] + ], + presets: [ + 'next/babel' + ] +}; diff --git a/site/components/ApiHeading.jsx b/site/components/ApiHeading.jsx new file mode 100644 index 0000000..539f5ce --- /dev/null +++ b/site/components/ApiHeading.jsx @@ -0,0 +1,154 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import reactReplace from 'react-string-replace'; +import clsx from 'clsx'; +import { slug } from 'github-slugger'; +import { IoMdReturnRight } from 'react-icons/io'; +import LinkedHeading from '@components/LinkedHeading'; +import Link from '@components/Link'; + + +const ApiHeading = ({ as, type, text, returns, link, scope, since, deprecated }) => { + function getType(t) { + if(t.url) { + return ( + <Link plain href={t.url}>{t.type}</Link> + ); + } + + return t.type; + } + + function getReturns() { + if(!returns) return null; + + const types = returns.split('|').map(type => { + let url = link && link !== 'null' ? link : null; + if(!link && /^#/.test(type)) { + type = type.replace(/^#/, ''); + url = `#${slug(type)}`; + } + + if(!link && /<#.*>/.test(type)) { + const regx = /<(#.*)>/; + url = `#${slug(type.match(regx)[1].replace(/#/, ''))}`; + type = type.replace(/#/, ''); + } + + return { + type, + url + }; + }); + + return ( + <div className="api-returns"> + <IoMdReturnRight /> + {types.map(t => ( + <span key={`${type}-${text}-${t.type}`} className="return-type"> + {getType(t)} + </span> + ))} + </div> + ); + } + + function getBadges() { + const badges = []; + if(deprecated) { + badges.push(( + <div className="badge deprecated badge-small"> + <span>{deprecated}</span> + </div> + )); + } + + if(since) { + badges.push(( + <div className="badge since badge-small"> + <span>{since}</span> + </div> + )); + } + + return <span className="api-heading-badges">{badges}</span>; + } + + function formatText(text) { + let txt = null; + if(type === 'event') txt = <><span className="prefix">Event:</span> {text}</>; + if(type === 'function') { + let [, start, args] = /^(.*)(?=\()(\(.*\))/.exec(text); + let [, prefix, method] = /^([a-zA-Z0-9]*\.?)(.*)/.exec(start); + if(!method) { + method = prefix; + prefix = ''; + } + + const str = []; + + if(prefix) str.push(<span key={`prefix-${text}-${prefix}-fn`} className="prefix">{prefix}</span>); + method = reactReplace(method, /(<|>|\|)/g, (match, i) => { + return <span key={`${text}-${i}-method`} className="punc">{match}</span>; + }); + + str.push(<span key={`method-${text}-${method}-fn`} className="func">{method}</span>); + + args = reactReplace(args, /([a-zA-Z0-9[\]]+)/g, (match, i) => { + const isOptional = match.match(/^\[.*\]$/); + return <span key={`${text}-${i}-args`} className={clsx('arg', isOptional ? ' optional' : null)}>{match}</span>; + }); + + str.push(<span key={`args-${text}-${args}-fn`} className="args">{args}</span>); + + txt = str; + } + + if(type === 'property' || type === 'getter' || type === 'setter') { + const str = []; + const parts = text.split('.'); + + let prop = parts.pop(); + if(parts.length) str.push(<span key={`parts-${text}-${parts}-${type}`} className="prefix">{parts.join('.')}.</span>); + + if(prop.match(/=/)) { + prop = reactReplace(prop, /(=.*)/, (match, i) => ( + <span key={`${text}-${i}-prop-equals`} className="equal-value">{match}</span> + )); + } + + str.push(<span key={`prop-${text}-${prop}-${type}`} className="prop">{prop}</span>); + + txt = str; + } + + return txt || text; + } + + return ( + <LinkedHeading as={as} className={clsx('api-heading', scope, type)} id={slug(text)} title={type}> + <span className="api-heading-content"> + {formatText(text)} + {getReturns()} + </span> + {getBadges()} + </LinkedHeading> + ); +}; + +ApiHeading.propTypes = { + as: PropTypes.string, + type: PropTypes.oneOf(['class', 'property', 'event', 'function', 'module', 'getter', 'setter']), + scope: PropTypes.oneOf(['static', 'instance']), + text: PropTypes.string.isRequired, + returns: PropTypes.string, + link: PropTypes.string, + since: PropTypes.string, + deprecated: PropTypes.string +}; + +ApiHeading.defaultProps = { + as: 'h4' +}; + +export default ApiHeading; diff --git a/site/components/Head.jsx b/site/components/Head.jsx new file mode 100644 index 0000000..2e68fb4 --- /dev/null +++ b/site/components/Head.jsx @@ -0,0 +1,224 @@ +/* eslint-disable react/no-danger */ +import React from 'react'; +import PropTypes from 'prop-types'; +import NextHead from 'next/head'; +import { useRouter } from 'next/router'; + +const siteUrl = 'https://lambdalog.dev'; + +const metaDefaults = { + description: 'Node.js package to enforce standards when logging to CloudWatch from Lambda functions, other AWS services, or anywhere you desire JSON-formatted logs.', + ogImage: 'https://lambdalog.dev/og-image.png', + ogType: 'website' +}; + +/** + * Ensures the passed URL contains the site domain. + * @param {string} url The URL to ensure contains the site domain. + * @return {string} The formatted URL with the site domain included. + */ +function ensureDomain(url) { + if(!url) return null; + if(/^https?:\/\//.test(url)) return url; + + return `${siteUrl}${/^\//.test(url) ? '' : '/'}${url}`; +} + +/** + * Compiles the passed metadata into a uniform object with proper defaults. + * @param {object} meta Raw metadata object passed into the component. + * @param {string} path The current path from Next Router. + * @return {object} The normalized metadata object. + */ +function buildMetadata(meta, path) { + const baseTitle = meta.baseTitle || 'LambdaLog'; + const title = meta.title || ''; + const description = meta.description || meta.ogDescription || metaDefaults.description; + const ogUrl = ensureDomain(meta.ogUrl || path); + const canonicalUrl = ensureDomain(meta.canonicalUrl) || ogUrl; + const ogTitle = meta.ogTitle || title; + const ogDescription = meta.ogDescription || description; + const ogImage = ensureDomain(meta.ogImage || metaDefaults.ogImage); + + const fullTitle = [baseTitle]; + if(title) fullTitle.push(title); + + return { + title: fullTitle.join(' | '), + description, + canonicalUrl, + ogUrl, + ogTitle: fullTitle.join(' | '), + ogDescription, + ogType: meta.ogType || metaDefaults.ogType, + ogImage, + twitterTitle: meta.twitterTitle || ogTitle, + twitterDescription: meta.twitterDescription || ogDescription, + twitterImage: ensureDomain(meta.twitterImage || ogImage) + }; +} + +const Head = ({ meta, schema, children, noIndex = false }) => { + const router = useRouter(); + const metadata = buildMetadata(meta, router?.asPath || '/'); + const copyrightYear = new Date().getFullYear(); + + const jsonLd = { + '@context': 'https://schema.org', + '@graph': [{ + '@id': 'https://lambdalog.dev/#person', + '@type': 'Person', + name: 'Kyle Ross', + jobTitle: 'Software Engineer', + description: 'Software Engineer in South Carolina. Specializing in JavaScript and Node.js.', + image: 'https://kyleross.me/static-images/kyle-ross.jpg', + url: 'https://kyleross.me', + gender: 'Male', + address: { + '@type': 'PostalAddress', + addressRegion: 'SC' + }, + sameAs: [ + 'https://www.linkedin.com/in/kylerross/', + 'https://github.com/KyleRoss', + 'https://www.npmjs.com/~kyleross' + ] + }, { + '@id': 'https://lambdalog.dev/#website', + '@type': 'WebSite', + name: 'LambdaLog', + description: 'Node.js package to enforce standards when logging to CloudWatch from Lambda functions, other AWS services, or anywhere you desire JSON-formatted logs.', + url: 'https://lambdalog.dev', + about: { '@id': 'https://lambdalog.dev/#person' }, + copyrightHolder: { '@id': 'https://lambdalog.dev/#person' }, + copyrightYear, + creator: { '@id': 'https://lambdalog.dev/#person' }, + isFamilyFriendly: true + }] + }; + + if(schema) { + if(Array.isArray(schema)) { + jsonLd['@graph'] = jsonLd['@graph'].concat(schema); + } else { + jsonLd['@graph'].push(schema); + } + } + + return ( + <NextHead> + <title>{metadata.title} + + + + + + + + + + + + {noIndex && ( + + )} + + + + + Skip to main content. + + + + + ); +}; + +export default App; diff --git a/site/pages/_document.jsx b/site/pages/_document.jsx new file mode 100644 index 0000000..ebb53d3 --- /dev/null +++ b/site/pages/_document.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import Document, { Html, Head, Main, NextScript } from 'next/document'; + +export default class MyDocument extends Document { + static async getInitialProps(ctx) { + const initialProps = await Document.getInitialProps(ctx); + return { ...initialProps }; + } + + render() { + const pageProps = this.props?.__NEXT_DATA__?.props?.pageProps; + return ( + + + +
+ + + + ); + } +} diff --git a/site/pages/api/npm-version.js b/site/pages/api/npm-version.js new file mode 100644 index 0000000..36781c0 --- /dev/null +++ b/site/pages/api/npm-version.js @@ -0,0 +1,19 @@ +export default async function getNpmVersion(req, res) { + try { + const data = await fetch('https://registry.npmjs.org/lambda-log').then(resp => resp.json()); + const version = data?.['dist-tags']?.latest; + + if(!version) { + return res.status(500).json({ error: 'Version not returned from NPM registry' }); + } + + return res.status(200).json({ + version, + date: data.time[version] + }); + } catch(err) { + console.log(err); + + return res.status(500).json({ error: err.message }); + } +} diff --git a/site/pages/docs/[version]/[[...slug]].jsx b/site/pages/docs/[version]/[[...slug]].jsx new file mode 100644 index 0000000..cbf7566 --- /dev/null +++ b/site/pages/docs/[version]/[[...slug]].jsx @@ -0,0 +1,198 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; +import { serialize } from 'next-mdx-remote/serialize'; +import { MDXRemote } from 'next-mdx-remote'; +import matter from 'gray-matter'; +import clsx from 'clsx'; +import { Tab, Tabs, TabList, TabPanels, TabPanel } from '@reach/tabs'; +import torchlight from 'remark-torchlight'; +import remarkHint from 'remark-hint'; +import rehypeSlug from 'rehype-slug'; +import remarkCodeTabs from '@utils/remarkCodeTabs'; +import { getDocumentList, getDocumentContent, getNavigation } from '@utils/docs'; +import apiVersions from '@/docs/versions'; +import { ApiVersionContext } from '@utils/ApiVersionContext'; +import Header from '@components/base/Header'; +import Footer from '@components/base/Footer'; +import Head from '@components/Head'; +import Link from '@components/Link'; +import ApiHeading from '@components/ApiHeading'; +import LinkedHeading from '@components/LinkedHeading'; + +function asLinkedHeading(as) { + return props => ; +} + +const DocsPage = ({ meta, nav, source, slug }) => { + const { version } = useContext(ApiVersionContext); + + function parseVersionedLinks(href) { + return href.replace(/(\[v\]|%5Bv%5D)/i, version); + } + + const components = { + a: ({ href, children, ...props }) => { + href = parseVersionedLinks(href); + return {children}; + }, + h1: asLinkedHeading('h1'), + h2: asLinkedHeading('h2'), + h3: asLinkedHeading('h3'), + h4: asLinkedHeading('h4'), + h5: asLinkedHeading('h5'), + h6: asLinkedHeading('h6'), + h: ApiHeading, + scope: ({ text, link, children, ...props }) => { + return ( +
+ Scope + {link ? ( + {text} + ) : ( + {text} + )} + {' — '}{children} +
+ ); + }, + since: ({ version, small, className, ...props }) => { + return ( +
+ {version} +
+ ); + }, + deprecated: ({ version, small, className, ...props }) => { + return ( +
+ {version} +
+ ); + }, + Tab, + Tabs, + TabList, + TabPanels, + TabPanel + }; + + + return ( + <> + +
+ +
+ + +
+

+ {meta.title} + {meta.description && {meta.description}} +

+
+ +
+
+
+
+ + ); +}; + +DocsPage.propTypes = { + meta: PropTypes.object, + source: PropTypes.object, + nav: PropTypes.arrayOf(PropTypes.object), + slug: PropTypes.string +}; + + +export async function getStaticProps({ params }) { + const slug = params.slug?.[0] ?? ''; + const isValidVersion = apiVersions.find(ver => ver.value === params.version); + + if(!isValidVersion) { + return { notFound: true }; + } + + const nav = await getNavigation(params.version); + const doc = await getDocumentContent(params.version, slug); + + const { content, data: meta } = matter(doc); + const mdxSource = await serialize(content, { + mdxOptions: { + remarkPlugins: [ + [torchlight, { + config: { + theme: 'min-light', + options: { + diffIndicators: true, + diffIndicatorsInPlaceOfLineNumbers: true + } + } + }], + remarkCodeTabs, + remarkHint + ], + rehypePlugins: [ + rehypeSlug + ] + } + }); + + return { + props: { + slug, + nav, + source: mdxSource, + meta: meta || {} + } + }; +} + +export async function getStaticPaths() { + const paths = []; + + for(const ver of apiVersions) { + // eslint-disable-next-line no-await-in-loop + const files = await getDocumentList(ver.value); + + paths.push({ params: { version: ver.value, slug: [] } }); + + files.forEach(file => { + const slug = []; + if(file.slug) slug.push(file.slug); + paths.push({ + params: { version: ver.value, slug } + }); + }); + } + + return { + paths, + fallback: false + }; +} + +export default DocsPage; diff --git a/site/pages/index.js b/site/pages/index.js new file mode 100644 index 0000000..ea06cd3 --- /dev/null +++ b/site/pages/index.js @@ -0,0 +1,134 @@ +import React from 'react'; +import clsx from 'clsx'; +import { IoArrowForward, IoDocumentText } from 'react-icons/io5'; +import Head from '@components/Head'; +import Logo from '@components/Logo'; +import Link from '@components/Link'; +import Hero from '@components/Hero'; +import Terminal from '@components/Terminal'; +import Footer from '@components/base/Footer'; +import styles from './Home.module.css'; + +const Home = () => { + const meta = { + title: 'Node.js Logger for Lambda' + }; + + return ( + <> + +
+
+ +
+
+ +
+ + +
+ +
+ npm install lambda-log +
+
+
+ +
+

+ LambdaLog facilitates and enforces logging standards in Node.js processes or applications anywhere by formatting your + log messages as JSON for simple parsing and filtering within log management tools, such as CloudWatch Logs. Works with all of the supported versions of Node.js on Lambda. +

+

+ Originally created for AWS Lambda Functions, LambdaLog is a lightweight and feature-rich library that has no dependency + on AWS or Lambda, meaning you can use it in any type of Node.js project you wish. +

+
+ +
+
+ Why another Lambda logger? +

+ There are plenty of other logging libraries in the NPM ecosystem but most are convoluted, included more functionality than needed, + not maintained, or are not configurable enough. I created LambdaLog to include the important functionality from other loggers, but + still maintaining simplicity with minimal dependencies. +

+
+
+ +
+
+

Features

+

Anyone can log JSON to the console, but with Lambda Log you also get:

+
+
+ Annotate Logs with Tags +

Add tags to your logs both globally and individually.

+
+ +
+ Include Extra Metadata in Logs +

Attach additional information to your logs globally and individually.

+
+ +
+ Formatted Errors +

Errors are parsed to include the relevant information in your logs.

+
+ +
+ Tons of Customization +

Many options that allow you to make LambdaLog work the way you want without being overwhelming.

+
+ +
+ Pretty-Printing of Logs during Development +

Pretty print the JSON logs during development, making it easier to read your logs in a terminal.

+
+ +
+ Extensibility +

Class-based library that allows for advanced customization down to the methods.

+
+ +
+ Excellent Documentation +

Full documentation and examples that cover every aspect of LambdaLog.

+
+ +
+ Fully Tested +

100% test coverage to ensure all functionality works in each release.

+
+ +
+ Enterprise Ready +

MIT Licensed and audited for vulnerabilities.

+
+ +
+ High Performance in a Small Package +

Only 1 dependency and ~40kB total package size.

+
+
+
+
+ +
+

Ready to use LambdaLog?

+ + + + Read the Docs + + +
+
+ +