From 51b270cb80b2b5dacfa5e304cacf3923e05a3d86 Mon Sep 17 00:00:00 2001 From: kitajchuk Date: Tue, 6 Apr 2021 17:52:42 -0700 Subject: [PATCH 1/8] Remove Nextra -- Custom docs theme --- .babelrc | 25 + README.md | 4 +- components/footer.js | 4 +- components/jambonz-ui.js | 11 +- components/layout.js | 1 + components/navi.js | 10 +- data/docs.yml | 44 + lib/data.js | 130 ++ next.config.js | 2 - package.json | 17 +- pages/_app.js | 1 - pages/docs/[[...slug]].js | 112 ++ pages/docs/advanced/meta.json | 3 - pages/docs/anchors.md | 57 - .../code-highlighting.mdx => api/rest.md} | 0 pages/docs/api/webhooks.md | 1 + pages/docs/built-in-components.mdx | 81 -- pages/docs/get-started.mdx | 78 -- pages/docs/getting-started/add-application.md | 3 + pages/docs/getting-started/add-carrier.md | 3 + .../docs/getting-started/add-phone-numbers.md | 3 + pages/docs/getting-started/add-speech.md | 3 + .../getting-started/register-sip-client.md | 3 + pages/docs/i18n.md | 40 - pages/docs/index.md | 14 +- pages/docs/mdx.mdx | 119 -- pages/docs/meta.json | 10 - pages/docs/open-source/overview.md | 1 + pages/docs/ssg.mdx | 32 - pages/docs/structure.md | 7 - pages/docs/tutorials/overview.md | 1 + pages/index.js | 15 +- pages/jambonz-ui.js | 6 +- pages/meta.json | 3 - pages/pricing.js | 16 +- pages/why.js | 11 +- styles/_mixins.scss | 9 + styles/_navi.scss | 2 +- styles/_nextra.scss | 7 - styles/_reset.scss | 77 ++ styles/_typography.scss | 21 +- styles/global.scss | 13 +- styles/pages/_docs.scss | 108 ++ theme.config.js | 44 - yarn.lock | 1153 +++-------------- 45 files changed, 785 insertions(+), 1520 deletions(-) create mode 100644 .babelrc create mode 100644 data/docs.yml delete mode 100644 next.config.js create mode 100644 pages/docs/[[...slug]].js delete mode 100644 pages/docs/advanced/meta.json delete mode 100644 pages/docs/anchors.md rename pages/docs/{advanced/code-highlighting.mdx => api/rest.md} (100%) create mode 100644 pages/docs/api/webhooks.md delete mode 100644 pages/docs/built-in-components.mdx delete mode 100644 pages/docs/get-started.mdx create mode 100644 pages/docs/getting-started/add-application.md create mode 100644 pages/docs/getting-started/add-carrier.md create mode 100644 pages/docs/getting-started/add-phone-numbers.md create mode 100644 pages/docs/getting-started/add-speech.md create mode 100644 pages/docs/getting-started/register-sip-client.md delete mode 100644 pages/docs/i18n.md delete mode 100644 pages/docs/mdx.mdx delete mode 100644 pages/docs/meta.json create mode 100644 pages/docs/open-source/overview.md delete mode 100644 pages/docs/ssg.mdx delete mode 100644 pages/docs/structure.md create mode 100644 pages/docs/tutorials/overview.md delete mode 100644 pages/meta.json delete mode 100644 styles/_nextra.scss create mode 100644 styles/_reset.scss create mode 100644 styles/pages/_docs.scss delete mode 100644 theme.config.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..3465227 --- /dev/null +++ b/.babelrc @@ -0,0 +1,25 @@ +{ + "presets": [ + "next/babel" + ], + "plugins": [ + [ + "prismjs", + { + "languages": [ + "javascript", + "js", + "json", + "bash", + "http" + ], + "plugins": [ + "line-numbers", + "show-language" + ], + "theme": "okaidia", + "css": true + } + ] + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 0f6be16..d6fc36b 100644 --- a/README.md +++ b/README.md @@ -37,4 +37,6 @@ We are using static data with [yamljs](https://www.npmjs.com/package/yamljs) and ## Jambonz developer docs -The project is using [Nextra.js](https://nextra.vercel.app) which provides a stylized developer docs theme for Next.js. We are loading theme CSS styles with the `styles/_nextra.scss` partial. It is being used to hide the frontend page links from the Nextra sidebar nav as there is currently no way to do this using pure nextra config. There is a [Github issue here](https://github.com/shuding/nextra/issues/59) referring to "Page exclusion from docs". +The project is generating developer docs from markdown files using static file JS utilities alongside Next.js static paths/props system. We are leveraging their [catch-all](https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes) dynamic routes logic located at `pages/docs/[[...slug]].js`. The markdown files are in the `pages/docs` directory. The docs structure is controlled in the docs page YAML data located in `data/docs.yaml`. You can create docs markdown files at will in the `pages/docs` directory but they will not render in the sidebar nav until they are also added to the nav structure in this file. + +We are using [remark](https://github.com/remarkjs/remark) & [remark-html](https://github.com/remarkjs/remark-html) as well as [gray-matter](https://github.com/jonschlinkert/gray-matter) for parsing the docs markdown files. Code syntax highlighting is done with [prismjs](https://prismjs.com) and the associative babel config is in the `.babelrc` file. It's important to leave the preset in this file that merges our config with `next/babel` so Next.js works properly. diff --git a/components/footer.js b/components/footer.js index d12b65e..384e0ac 100644 --- a/components/footer.js +++ b/components/footer.js @@ -1,7 +1,9 @@ +import { nanoid } from 'nanoid'; + import Link from 'next/link'; + import { Button } from './jambonz-ui'; import { homeObj } from '../lib/vars'; -import { nanoid } from 'nanoid'; function FooterItem({ obj }) { return ( diff --git a/components/jambonz-ui.js b/components/jambonz-ui.js index aa3a62e..38a02ad 100644 --- a/components/jambonz-ui.js +++ b/components/jambonz-ui.js @@ -1,10 +1,13 @@ -import Link from 'next/link'; import classNames from 'classnames'; import * as Icons from 'react-feather'; import { nanoid } from 'nanoid'; -import { mobileMedia } from '../lib/vars'; import { useState, useEffect } from 'react'; +import Link from 'next/link'; + +import { mobileMedia } from '../lib/vars'; + + // Normalize how we work with the subtext as an array[] export function normalizeSubtext(subtext) { if (!Array.isArray(subtext)) { @@ -93,7 +96,7 @@ export function Hero({ data, subStyle }) { } return ( -
+

{data.headline}

@@ -109,7 +112,7 @@ export function Hero({ data, subStyle }) {
)}
-
+ ); } diff --git a/components/layout.js b/components/layout.js index 8ab7038..22de6c9 100644 --- a/components/layout.js +++ b/components/layout.js @@ -1,4 +1,5 @@ import Head from 'next/head'; + import Navi from './navi'; import Footer from './footer'; diff --git a/components/navi.js b/components/navi.js index 8d83a8a..d8ca098 100644 --- a/components/navi.js +++ b/components/navi.js @@ -1,16 +1,18 @@ -import { useState, useEffect } from 'react'; +import { useState } from 'react'; +import { nanoid } from 'nanoid'; +import classNames from 'classnames'; + import Link from 'next/link'; import { useRouter } from 'next/router'; -import classNames from 'classnames'; + import { Button, Icon, useMobileMedia } from './jambonz-ui'; import { homeObj } from '../lib/vars'; -import { nanoid } from 'nanoid'; function NaviItem({obj}) { const router = useRouter(); const classes = { navi__link: true, - active: (router.route === obj.link), + active: (router.route.replace('/[[...slug]]', '') === obj.link), }; return ( diff --git a/data/docs.yml b/data/docs.yml new file mode 100644 index 0000000..7a1eec6 --- /dev/null +++ b/data/docs.yml @@ -0,0 +1,44 @@ +navi: + - + path: getting-started + title: Getting Started + pages: + - + path: add-carrier + title: Add a Carrier + - + path: add-speech + title: Add Speech Credentials + - + path: add-application + title: Add an Application + - + path: add-phone-numbers + title: Add Phone Numbers + - + path: register-sip-client + title: Register SIP clients + - + path: api + title: API Reference + pages: + - + path: webhooks + title: Webhooks + - + path: rest + title: REST APIs + - + path: tutorials + title: Tutorials + pages: + - + path: overview + title: Overview + - + path: open-source + title: Open Source + pages: + - + path: overview + title: Overview \ No newline at end of file diff --git a/lib/data.js b/lib/data.js index 29f020e..becc6f4 100644 --- a/lib/data.js +++ b/lib/data.js @@ -1,17 +1,147 @@ const fs = require('fs'); const path = require('path'); const yamljs = require('yamljs'); +const matter = require('gray-matter'); +const remark = require('remark'); +const remarkHtml = require('remark-html'); const dataDir = path.join(process.cwd(), 'data'); +const pagesDir = path.join(process.cwd(), 'pages'); +const docsDir = path.join(pagesDir, 'docs'); +/****************************************************************************** + * Static page data +*******************************************************************************/ function _getData(key) { const fullPath = path.join(dataDir, `${key}.yml`); const fileContents = fs.readFileSync(fullPath, 'utf8'); return yamljs.parse(fileContents); } +// Load YAML data to generate the static props for Next.js page components export function getData(key) { return { [key]: _getData(key), site: _getData('site'), }; +} + +/****************************************************************************** + * Static developer docs data +*******************************************************************************/ +function _getCleanSlug(slug) { + return slug + .replace(docsDir, '') + .replace(/^\/+|\/+$/, '') + .split('/'); +} + +// Load Markdown and YAML front-matter to generate the static props for Next.js docs component +async function _getDocs(filePath) { + const fileContents = fs.readFileSync(filePath, 'utf8'); + const fileMatter = matter(fileContents); + const frontMatter = fileMatter.data; + + // Use remark to convert markdown into HTML string + const processedContent = await remark() + .use(remarkHtml) + .process(fileMatter.content); + const contentHtml = processedContent.toString(); + + // Combine the data with the slug and contentHtml + return { + contentHtml, + frontMatter, + } +} + +// Walk the pages/docs file tree to generate the static paths for Next.js docs pages +function _getDocsPaths(dirPath, arrayOfFiles = [{params:{slug:[]}}]) { + const files = fs.readdirSync(dirPath); + + files.forEach((file) => { + const filePath = `${dirPath}/${file}`; + const isDirectory = fs.statSync(filePath).isDirectory(); + const isMarkdown = /\.md$/.test(file); + let slug; + + if (isDirectory) { + const indexFile = path.join(filePath, 'index.md'); + const isIndexFile = fs.existsSync(indexFile); + + if (isIndexFile) { + slug = _getCleanSlug(path.join( + __dirname, + dirPath, + '/', + file + )); + + arrayOfFiles.push({ + params: { + slug, + }, + }); + } + + arrayOfFiles = _getDocsPaths(filePath, arrayOfFiles); + + } else if (isMarkdown) { + slug = _getCleanSlug(path.join( + __dirname, + dirPath, + '/', + file.replace(/\.md$/, '') + )); + + arrayOfFiles.push({ + params: { + slug, + }, + }); + } + }); + + return arrayOfFiles; +} + +// Public proxy for _getDocs() to return static props for Next.js docs component +export async function getDocs(slug) { + let filePath = slug ? path.join( + docsDir, + slug.join('/') + ) : path.join( + docsDir, + 'index.md' + ); + const isDirectory = (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()); + let isMarkdown = /\.md$/.test(filePath); + + if (isDirectory) { + filePath = path.join( + docsDir, + slug.join('/'), + 'index.md' + ); + isMarkdown = true; + + } else if (!isMarkdown) { + filePath = path.join( + docsDir, + `${slug.join('/')}.md` + ); + isMarkdown = true; + } + + if (isMarkdown && fs.existsSync(filePath)) { + return _getDocs(filePath); + } + + return null; +} + +// Public proxy for _getDocsPaths() to return static paths for Next.js docs pages +export function getDocsPaths() { + const paths = _getDocsPaths(docsDir); + + return paths; } \ No newline at end of file diff --git a/next.config.js b/next.config.js deleted file mode 100644 index 851af14..0000000 --- a/next.config.js +++ /dev/null @@ -1,2 +0,0 @@ -const withNextra = require('nextra')('nextra-theme-docs', './theme.config.js') -module.exports = withNextra() diff --git a/package.json b/package.json index 7219c59..797d0bf 100644 --- a/package.json +++ b/package.json @@ -13,19 +13,22 @@ "license": "MIT", "dependencies": { "classnames": "^2.2.6", - "env-cmd": "^10.1.0", "nanoid": "^3.1.22", "next": "^10.0.8-canary.9", - "nextra": "^0.4.1", - "nextra-theme-docs": "^1.1.2", + "prismjs": "^1.23.0", "react": "^17.0.1", "react-dom": "^17.0.1", - "react-feather": "^2.0.9", - "sass": "^1.32.8", - "yamljs": "^0.3.0" + "react-feather": "^2.0.9" }, "devDependencies": { - "prettier": "^2.0.5" + "babel-plugin-prismjs": "^2.0.1", + "prettier": "^2.0.5", + "gray-matter": "^4.0.2", + "env-cmd": "^10.1.0", + "remark": "^13.0.0", + "remark-html": "^13.0.1", + "sass": "^1.32.8", + "yamljs": "^0.3.0" }, "prettier": { "semi": false, diff --git a/pages/_app.js b/pages/_app.js index c04bd99..2d2abc7 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,4 +1,3 @@ -import 'nextra-theme-docs/style.css'; import '../styles/global.scss'; export default function App({Component, pageProps}) { diff --git a/pages/docs/[[...slug]].js b/pages/docs/[[...slug]].js new file mode 100644 index 0000000..1e4502a --- /dev/null +++ b/pages/docs/[[...slug]].js @@ -0,0 +1,112 @@ +import { useEffect, useState } from 'react'; +import { nanoid } from 'nanoid'; +import classNames from 'classnames'; +import Prism from 'prismjs'; + +import Link from 'next/link'; +import { useRouter } from 'next/router'; + +import Layout from '../../components/layout'; +import { Icon } from '../../components/jambonz-ui'; +import { getData, getDocs, getDocsPaths } from '../../lib/data'; + +function Sidebar({data}) { + const router = useRouter(); + const [active, setActive] = useState({ + [data.navi[0].path]: true, + }); + + const handleToggle = (slug) => { + setActive((oldActive) => { + const newActive = {}; + + for (let i in oldActive) { + newActive[i] = oldActive[i]; + } + + newActive[slug] = newActive[slug] ? false : true; + + return newActive; + }); + }; + + return ( + + ); +} + +export default function Docs({ data, docs }) { + useEffect(() => { + setTimeout(() => Prism.highlightAll(), 0); + }); + + return ( + +
+
+ +
+
+
+
+
+ + ); +} + +export async function getStaticPaths() { + const paths = getDocsPaths(); + + return { + paths, + fallback: false, + }; +} + +export async function getStaticProps({ params }) { + const data = getData('docs'); + const docs = await getDocs(params.slug); + + return { + props: { + data, + docs, + }, + }; +} diff --git a/pages/docs/advanced/meta.json b/pages/docs/advanced/meta.json deleted file mode 100644 index b3382bd..0000000 --- a/pages/docs/advanced/meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "code-highlighting": "Code Highlighting" -} \ No newline at end of file diff --git a/pages/docs/anchors.md b/pages/docs/anchors.md deleted file mode 100644 index 7f8337c..0000000 --- a/pages/docs/anchors.md +++ /dev/null @@ -1,57 +0,0 @@ -# Anchor Links - -`h2` tags will become anchors in the sidebar automatically. - -## When You Are Old - -When you are old and grey and full of sleep, -And nodding by the fire, take down this book, -And slowly read, and dream of the soft look -Your eyes had once, and of their shadows deep; - -How many loved your moments of glad grace, -And loved your beauty with love false or true, -But one man loved the pilgrim soul in you, -And loved the sorrows of your changing face; - -And bending down beside the glowing bars, -Murmur, a little sadly, how Love fled -And paced upon the mountains overhead -And hid his face amid a crowd of stars. - -## The Young Man's Song - -I whispered, "I am too young," -And then, "I am old enough"; -Wherefore I threw a penny -To find out if I might love. -"Go and love, go and love, young man, -If the lady be young and fair," -Ah, penny, brown penny, brown penny, -I am looped in the loops of her hair. - -Oh, love is the crooked thing, -There is nobody wise enough -To find out all that is in it, -For he would be thinking of love -Till the stars had run away, -And the shadows eaten the moon. -Ah, penny, brown penny, brown penny, -One cannot begin it too soon. - -## The Sorrow of Love - -The quarrel of the sparrows in the eaves, -The full round moon and the star-laden sky, -And the loud song of the ever-singing leaves, -Had hid away earth's old and weary cry. - -And then you came with those red mournful lips, -And with you came the whole of the world's tears, -And all the sorrows of her labouring ships, -And all the burden of her myriad years. - -And now the sparrows warring in the eaves, -The curd-pale moon, the white stars in the sky, -And the loud chaunting of the unquiet leaves -Are shaken with earth's old and weary cry. diff --git a/pages/docs/advanced/code-highlighting.mdx b/pages/docs/api/rest.md similarity index 100% rename from pages/docs/advanced/code-highlighting.mdx rename to pages/docs/api/rest.md diff --git a/pages/docs/api/webhooks.md b/pages/docs/api/webhooks.md new file mode 100644 index 0000000..ac597c1 --- /dev/null +++ b/pages/docs/api/webhooks.md @@ -0,0 +1 @@ +# Webhooks diff --git a/pages/docs/built-in-components.mdx b/pages/docs/built-in-components.mdx deleted file mode 100644 index e944c5c..0000000 --- a/pages/docs/built-in-components.mdx +++ /dev/null @@ -1,81 +0,0 @@ -# Built-In Components - -Some helpful built-in components from `nextra-theme-docs`. - -## Callout - -import Callout from 'nextra-theme-docs/callout' - -#### Example - - - A **callout** is a short piece of text intended to attract attention. - - - - The **dodo** is an extinct flightless bird that was endemic to the island of Mauritius, east of Madagascar in the Indian Ocean. - - -#### Usage - -```mdx -import Callout from 'nextra-theme-docs/callout' - - - The **dodo** is an extinct flightless bird. - -``` - -## Bleed - -import Bleed from 'nextra-theme-docs/bleed' - -#### Example - -When wrapping your content with ``, it will be slightly wider than the container -and will overflow on both sides. - - -
- _There is nothing to writing. All you do is sit down at a typewriter and **bleed**._ - - — Ernest Hemingway -
-
- -It providers a better reading experience when you want to present some graphical information, which normally -looks nicer in a larger size. - -For example you can put text, image, video or any component inside: - - - - -``` diff --git a/pages/docs/get-started.mdx b/pages/docs/get-started.mdx deleted file mode 100644 index e51d638..0000000 --- a/pages/docs/get-started.mdx +++ /dev/null @@ -1,78 +0,0 @@ -# Get Started - -Create your own Nextra site and deploy to Vercel: - -[![](https://vercel.com/button)](https://vercel.com/import/git?s=https%3A%2F%2Fgithub.com%2Fshuding%2Fnextra&c=1) - -Vercel will create the Nextra repository and deploy the site for you with just a few clicks. -Once done, every change in the repository will be deployed automatically. - ---- - -### Configurations - -1. Install Next.js and React: `yarn add next react react-dom` - -2. Install Nextra and the docs theme: `yarn add nextra nextra-theme-docs` - -3. Create the following Next.js config and theme config under the root directory: - -```jsx -// next.config.js -const withNextra = require('nextra')('nextra-theme-docs', './theme.config.js') -module.exports = withNextra() -``` - -```jsx -// theme.config.js -export default { - repository: 'https://github.com/shuding/nextra', // project repo - docsRepository: 'https://github.com/shuding/nextra', // docs repo - branch: 'master', // branch of docs - path: '/', // path of docs - titleSuffix: ' – Nextra', - nextLinks: true, - prevLinks: true, - search: true, - customSearch: null, // customizable, you can use algolia for example - darkMode: true, - footer: true, - footerText: 'MIT 2020 © Shu Ding.', - footerEditOnGitHubLink: true, // will link to the docs repo - logo: <> - ... - Next.js Static Site Generator - , - head: <> - - - - -} -``` - -4. Create `pages/_app.js` and include the theme stylesheet: - -```jsx -import 'nextra-theme-docs/style.css' - -export default function Nextra({ Component, pageProps }) { - return -} -``` - -5. You are good to go! - ---- - -import Callout from 'nextra-theme-docs/callout' - - -Any `.md` or `.mdx` file will turn into a doc page and be displayed in sidebar. You can also create a `meta.json` file to customize the page order and title. - -Check the source code: https://github.com/shuding/nextra for more information. - - - -You can also use [`