From daf2b9cfaf666f67eab7a6d67c4733db01dbb4b4 Mon Sep 17 00:00:00 2001 From: Kevin Pruett Date: Wed, 2 Sep 2020 14:54:58 -0400 Subject: [PATCH] Integrate @hashicorp/react-search into layout --- website/components/search-bar/index.jsx | 28 +++ website/components/search-bar/style.css | 8 + website/layouts/docs.jsx | 19 +- website/layouts/intro.jsx | 21 ++- website/layouts/vagrant-cloud.jsx | 21 ++- website/package-lock.json | 234 ++++++++++++++++++++++-- website/package.json | 9 +- website/pages/style.css | 20 +- website/scripts/index_search_content.js | 84 ++++++--- 9 files changed, 376 insertions(+), 68 deletions(-) create mode 100644 website/components/search-bar/index.jsx create mode 100644 website/components/search-bar/style.css diff --git a/website/components/search-bar/index.jsx b/website/components/search-bar/index.jsx new file mode 100644 index 000000000..c5ebee114 --- /dev/null +++ b/website/components/search-bar/index.jsx @@ -0,0 +1,28 @@ +import Search from '@hashicorp/react-search' + +export default function SearchBar() { + return ( + ( + <> + + + + + + + + )} + resolveHitLink={(hit) => ({ + href: { + pathname: `/${transformIdtoUrl(hit.objectID)}`, + }, + })} + placeholder="Search Vagrant documentation" + /> + ) +} + +function transformIdtoUrl(id) { + return id.replace(/\/index$/, '') +} diff --git a/website/components/search-bar/style.css b/website/components/search-bar/style.css new file mode 100644 index 000000000..04fc456ce --- /dev/null +++ b/website/components/search-bar/style.css @@ -0,0 +1,8 @@ +.g-search { + width: calc(100% - 2rem); + max-width: 600px; + + & .c-hits .ais-Highlight-highlighted { + /* Insert custom background color applied to highlighted hits */ + } +} diff --git a/website/layouts/docs.jsx b/website/layouts/docs.jsx index 6b25bf094..a16cfa2b1 100644 --- a/website/layouts/docs.jsx +++ b/website/layouts/docs.jsx @@ -1,10 +1,12 @@ -import DocsPage from '@hashicorp/react-docs-page' -import order from '../data/docs-navigation.js' -import { frontMatter as data } from '../pages/docs/**/*.mdx' import Head from 'next/head' import Link from 'next/link' import { createMdxProvider } from '@hashicorp/nextjs-scripts/lib/providers/docs' +import DocsPage from '@hashicorp/react-docs-page' import Button from '@hashicorp/react-button' +import { SearchProvider } from '@hashicorp/react-search' +import SearchBar from '../components/search-bar' +import { frontMatter as data } from '../pages/docs/**/*.mdx' +import order from '../data/docs-navigation.js' const MDXProvider = createMdxProvider({ product: 'vagrant', @@ -13,10 +15,11 @@ const MDXProvider = createMdxProvider({ function DocsLayoutWrapper(pageMeta) { function DocsLayout(props) { + const { children, ...propsWithoutChildren } = props return ( + > + + + {children} + + ) } diff --git a/website/layouts/intro.jsx b/website/layouts/intro.jsx index 513a26f5b..46e6715de 100644 --- a/website/layouts/intro.jsx +++ b/website/layouts/intro.jsx @@ -1,18 +1,21 @@ -import DocsPage from '@hashicorp/react-docs-page' -import order from '../data/intro-navigation.js' -import { frontMatter as data } from '../pages/intro/**/*.mdx' -import { createMdxProvider } from '@hashicorp/nextjs-scripts/lib/providers/docs' import Head from 'next/head' import Link from 'next/link' +import { createMdxProvider } from '@hashicorp/nextjs-scripts/lib/providers/docs' +import DocsPage from '@hashicorp/react-docs-page' +import { SearchProvider } from '@hashicorp/react-search' +import SearchBar from '../components/search-bar' +import { frontMatter as data } from '../pages/intro/**/*.mdx' +import order from '../data/intro-navigation.js' const MDXProvider = createMdxProvider({ product: 'vagrant' }) function IntroLayoutWrapper(pageMeta) { function IntroLayout(props) { + const { children, ...propsWithoutChildren } = props return ( + > + + + {children} + + ) } diff --git a/website/layouts/vagrant-cloud.jsx b/website/layouts/vagrant-cloud.jsx index c112d68bb..5f84699b4 100644 --- a/website/layouts/vagrant-cloud.jsx +++ b/website/layouts/vagrant-cloud.jsx @@ -1,18 +1,21 @@ -import DocsPage from '@hashicorp/react-docs-page' -import order from '../data/cloud-navigation.js' -import { frontMatter as data } from '../pages/vagrant-cloud/**/*.mdx' -import { createMdxProvider } from '@hashicorp/nextjs-scripts/lib/providers/docs' import Head from 'next/head' import Link from 'next/link' +import { createMdxProvider } from '@hashicorp/nextjs-scripts/lib/providers/docs' +import DocsPage from '@hashicorp/react-docs-page' +import { SearchProvider } from '@hashicorp/react-search' +import SearchBar from '../components/search-bar' +import { frontMatter as data } from '../pages/vagrant-cloud/**/*.mdx' +import order from '../data/cloud-navigation.js' const MDXProvider = createMdxProvider({ product: 'vagrant' }) function CloudLayoutWrapper(pageMeta) { function CloudLayout(props) { + const { children, ...propsWithoutChildren } = props return ( + > + + + {children} + + ) } diff --git a/website/package-lock.json b/website/package-lock.json index 90378be28..8a950720f 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -1604,6 +1604,24 @@ "@hashicorp/react-button": "^2.2.1" } }, + "@hashicorp/react-search": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@hashicorp/react-search/-/react-search-2.0.0.tgz", + "integrity": "sha512-nMH0pUPNEOEcuIcQ/NRiqlZOXJkK7XnIs+KcwFoRxX6UyWSUUkhHPUU+TGy4Wgs8T65tFl5y4Ksnv+za+3CdPQ==", + "requires": { + "@hashicorp/react-inline-svg": "^1.0.2", + "@hashicorp/remark-plugins": "^3.0.0", + "react-instantsearch-dom": "^6.7.0", + "search-insights": "^1.6.0" + }, + "dependencies": { + "@hashicorp/react-inline-svg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@hashicorp/react-inline-svg/-/react-inline-svg-1.0.2.tgz", + "integrity": "sha512-AAFnBslSTgnEr++dTbMn3sybAqvn7myIj88ijGigF6u11eSRiV64zqEcyYLQKWTV6dF4AvYoxiYC6GSOgiM0Yw==" + } + } + }, "@hashicorp/react-section-header": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@hashicorp/react-section-header/-/react-section-header-2.0.0.tgz", @@ -1669,6 +1687,18 @@ "unist-util-is": "^4.0.2", "unist-util-map": "^2.0.1", "unist-util-visit": "^2.0.2" + }, + "dependencies": { + "remark": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/remark/-/remark-11.0.2.tgz", + "integrity": "sha512-bh+eJgn8wgmbHmIBOuwJFdTVRVpl3fcVP6HxmpPWO0ULGP9Qkh6INJh0N5Uy7GqlV7DQYGoqaKiEIpM5LLvJ8w==", + "requires": { + "remark-parse": "^7.0.0", + "remark-stringify": "^7.0.0", + "unified": "^8.2.0" + } + } } }, "@mapbox/rehype-prism": { @@ -1774,6 +1804,25 @@ "unist-util-visit": "^2.0.0" } }, + "unist-util-visit": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.2.tgz", + "integrity": "sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + } + }, + "unist-util-visit-parents": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.0.tgz", + "integrity": "sha512-0g4wbluTF93npyPrp/ymd3tCDTMnP0yo2akFD2FIBAYXq/Sga3lwaU1D8OYKbtpioaI6CkDcQ6fsMnmtzt7htw==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + }, "vfile-location": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.0.1.tgz", @@ -2496,6 +2545,21 @@ "@algolia/transporter": "4.3.0" } }, + "algoliasearch-helper": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.2.2.tgz", + "integrity": "sha512-/3XvE33R+gQKaiPdy3nmHYqhF8hqIu8xnlOicVxb1fD6uMFmxW8rGLzzrRfsPfxgAfm+c1NslLb3TzQVIB8aVA==", + "requires": { + "events": "^1.1.1" + }, + "dependencies": { + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + } + } + }, "ally.js": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/ally.js/-/ally.js-1.4.1.tgz", @@ -12356,6 +12420,34 @@ } } }, + "react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + }, + "react-instantsearch-core": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/react-instantsearch-core/-/react-instantsearch-core-6.7.0.tgz", + "integrity": "sha512-wIvSIwkWfqPbaQZcbKsfBK3Gpm1e7ahSwU8Bmx1N5RfUqA/NghqS0Ppv3sz4vCXjoEAdPV06R+Fpn9lT+cE9/Q==", + "requires": { + "@babel/runtime": "^7.1.2", + "algoliasearch-helper": "^3.1.0", + "prop-types": "^15.5.10", + "react-fast-compare": "^3.0.0" + } + }, + "react-instantsearch-dom": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/react-instantsearch-dom/-/react-instantsearch-dom-6.7.0.tgz", + "integrity": "sha512-J1C9xkHHLLa6rkKLKFDa7szA0TDo6yPFGmDzh2+JLaq4o694RIqivfUpROHus0Ki3BAQu9QmzLtodf6K1NOBWQ==", + "requires": { + "@babel/runtime": "^7.1.2", + "algoliasearch-helper": "^3.1.0", + "classnames": "^2.2.5", + "prop-types": "^15.5.10", + "react-instantsearch-core": "^6.7.0" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -12652,13 +12744,126 @@ } }, "remark": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/remark/-/remark-11.0.2.tgz", - "integrity": "sha512-bh+eJgn8wgmbHmIBOuwJFdTVRVpl3fcVP6HxmpPWO0ULGP9Qkh6INJh0N5Uy7GqlV7DQYGoqaKiEIpM5LLvJ8w==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-12.0.1.tgz", + "integrity": "sha512-gS7HDonkdIaHmmP/+shCPejCEEW+liMp/t/QwmF0Xt47Rpuhl32lLtDV1uKWvGoq+kxr5jSgg5oAIpGuyULjUw==", "requires": { - "remark-parse": "^7.0.0", - "remark-stringify": "^7.0.0", - "unified": "^8.2.0" + "remark-parse": "^8.0.0", + "remark-stringify": "^8.0.0", + "unified": "^9.0.0" + }, + "dependencies": { + "markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "requires": { + "repeat-string": "^1.0.0" + } + }, + "mdast-util-compact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz", + "integrity": "sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA==", + "requires": { + "unist-util-visit": "^2.0.0" + } + }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "requires": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + } + }, + "remark-stringify": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.1.1.tgz", + "integrity": "sha512-q4EyPZT3PcA3Eq7vPpT6bIdokXzFGp9i85igjmhRyXWmPs0Y6/d2FYwUNotKAWyLch7g0ASZJn/KHHcHZQ163A==", + "requires": { + "ccount": "^1.0.0", + "is-alphanumeric": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "longest-streak": "^2.0.1", + "markdown-escapes": "^1.0.0", + "markdown-table": "^2.0.0", + "mdast-util-compact": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "stringify-entities": "^3.0.0", + "unherit": "^1.0.4", + "xtend": "^4.0.1" + } + }, + "stringify-entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.0.1.tgz", + "integrity": "sha512-Lsk3ISA2++eJYqBMPKcr/8eby1I6L0gP0NlxF8Zja6c05yr/yCYyb2c9PwXjd08Ib3If1vn1rbs1H5ZtVuOfvQ==", + "requires": { + "character-entities-html4": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.2", + "is-hexadecimal": "^1.0.0" + } + }, + "unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + } + }, + "unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "requires": { + "unist-util-visit": "^2.0.0" + } + }, + "vfile-location": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.1.0.tgz", + "integrity": "sha512-FCZ4AN9xMcjFIG1oGmZKo61PjwJHRVA+0/tPUP2ul4uIwjGGndIxavEMRpWn5p4xwm/ZsdXp9YNygf1ZyE4x8g==" + } } }, "remark-footnotes": { @@ -13161,6 +13366,11 @@ "ajv-keywords": "^3.1.0" } }, + "search-insights": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-1.6.2.tgz", + "integrity": "sha512-mpy+57HZVMZH5HsMHYMCLvkf+tUvhy+ycP2tDy1j7wmj+mQsNZ3LC61IcMYomok02NozaMR3GiGyfH6uc+ibdA==" + }, "section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -15218,9 +15428,9 @@ } }, "unist-util-visit": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.2.tgz", - "integrity": "sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", "requires": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0", @@ -15228,9 +15438,9 @@ }, "dependencies": { "unist-util-visit-parents": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.0.2.tgz", - "integrity": "sha512-yJEfuZtzFpQmg1OSCyS9M5NJRrln/9FbYosH3iW0MG402QbdbaB8ZESwUv9RO6nRfLAKvWcMxCwdLWOov36x/g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.0.tgz", + "integrity": "sha512-0g4wbluTF93npyPrp/ymd3tCDTMnP0yo2akFD2FIBAYXq/Sga3lwaU1D8OYKbtpioaI6CkDcQ6fsMnmtzt7htw==", "requires": { "@types/unist": "^2.0.0", "unist-util-is": "^4.0.0" diff --git a/website/package.json b/website/package.json index b15a20428..0b3c20815 100644 --- a/website/package.json +++ b/website/package.json @@ -15,23 +15,26 @@ "@hashicorp/react-image": "2.0.1", "@hashicorp/react-mega-nav": "4.0.1-2", "@hashicorp/react-product-downloader": "4.0.2", + "@hashicorp/react-search": "^2.0.0", "@hashicorp/react-section-header": "2.0.0", "@hashicorp/react-subnav": "3.2.3", "@hashicorp/react-vertical-text-block-list": "2.0.1", - "algoliasearch": "4.3.0", + "algoliasearch": "^4.3.0", "babel-plugin-import-glob-array": "0.2.0", "dotenv": "8.2.0", + "glob": "^7.1.6", "gray-matter": "4.0.2", "imagemin-mozjpeg": "9.0.0", "imagemin-optipng": "8.0.0", "imagemin-svgo": "8.0.0", "next": "9.4.4", "react": "16.13.1", - "react-dom": "16.13.1" + "react-dom": "16.13.1", + "remark": "^12.0.1", + "unist-util-visit": "^2.0.3" }, "devDependencies": { "dart-linkcheck": "2.0.15", - "glob": "7.1.6", "husky": "4.2.5", "prettier": "2.0.5" }, diff --git a/website/pages/style.css b/website/pages/style.css index 25fc9ee29..ea86f3c63 100644 --- a/website/pages/style.css +++ b/website/pages/style.css @@ -11,18 +11,22 @@ } @import '~@hashicorp/react-button/dist/style.css'; +@import '~@hashicorp/react-code-block/dist/style.css'; @import '~@hashicorp/react-consent-manager/dist/style.css'; -@import '~@hashicorp/react-toggle/dist/style.css'; -@import '~@hashicorp/react-section-header/dist/style.css'; -@import '~@hashicorp/react-product-downloader/dist/style.css'; -@import '~@hashicorp/react-vertical-text-block-list/dist/style.css'; -@import '~@hashicorp/react-docs-sidenav/dist/style.css'; @import '~@hashicorp/react-content/dist/style.css'; +@import '~@hashicorp/react-docs-page/style.css'; +@import '~@hashicorp/react-docs-sidenav/dist/style.css'; +@import '~@hashicorp/react-mega-nav/style.css'; +@import '~@hashicorp/react-product-downloader/dist/style.css'; +@import '~@hashicorp/react-search/dist/style.css'; +@import '~@hashicorp/react-section-header/dist/style.css'; @import '~@hashicorp/react-subnav/dist/style.css'; @import '~@hashicorp/react-tabs/dist/style.css'; -@import '~@hashicorp/react-code-block/dist/style.css'; -@import '~@hashicorp/react-mega-nav/style.css'; -@import '~@hashicorp/react-docs-page/style.css'; +@import '~@hashicorp/react-toggle/dist/style.css'; +@import '~@hashicorp/react-vertical-text-block-list/dist/style.css'; + +/* Local Components */ +@import '../components/search-bar/style.css'; /* Print Styles */ @import './print.css'; diff --git a/website/scripts/index_search_content.js b/website/scripts/index_search_content.js index 871b542dc..480c0ca27 100644 --- a/website/scripts/index_search_content.js +++ b/website/scripts/index_search_content.js @@ -4,6 +4,8 @@ const algoliasearch = require('algoliasearch') const glob = require('glob') const matter = require('gray-matter') const path = require('path') +const remark = require('remark') +const visit = require('unist-util-visit') // In addition to the content of the page, // define additional front matter attributes that will be search-indexable @@ -15,30 +17,36 @@ async function main() { const pagesFolder = path.join(__dirname, '../pages') // Grab all search-indexable content and format for Algolia - const searchObjects = glob - .sync(path.join(pagesFolder, '**/*.mdx')) - .map((fullPath) => { - const { content, data } = matter.read(fullPath) + const searchObjects = await Promise.all( + glob + .sync(path.join(pagesFolder, '**/*.mdx'), { + ignore: path.join(pagesFolder, 'partials/**/*'), + }) + .map(async (fullPath) => { + const { content, data } = matter.read(fullPath) - // Get path relative to `pages` - const __resourcePath = fullPath.replace(`${pagesFolder}/`, '') + const searchableDimensions = SEARCH_DIMENSIONS.reduce( + (acc, dimension) => { + return { ...acc, [dimension]: data[dimension] } + }, + {} + ) - // Use clean URL for Algolia id - const objectID = __resourcePath.replace('.mdx', '') + const headings = await collectHeadings(content) - const searchableDimensions = Object.keys(data) - .filter((key) => SEARCH_DIMENSIONS.includes(key)) - .map((dimension) => ({ - [dimension]: data[dimension], - })) + // Get path relative to `pages` + const __resourcePath = fullPath.replace(`${pagesFolder}/`, '') - return { - ...searchableDimensions, - content, - __resourcePath, - objectID, - } - }) + // Use clean URL for Algolia id + const objectID = __resourcePath.replace('.mdx', '') + + return { + ...searchableDimensions, + headings, + objectID, + } + }) + ) try { await indexSearchContent(searchObjects) @@ -67,25 +75,23 @@ async function indexSearchContent(objects) { const searchClient = algoliasearch(appId, apiKey) const searchIndex = searchClient.initIndex(index) - await searchIndex.partialUpdateObjects(objects, { + const { objectIDs } = await searchIndex.partialUpdateObjects(objects, { createIfNotExists: true, }) - // Remove indices for items that aren't included in the new batch - const newObjectIds = objects.map(({ objectID }) => objectID) - let staleObjects = [] + let staleIds = [] await searchIndex.browseObjects({ query: '', batch: (batch) => { - staleObjects = staleObjects.concat( - batch.filter(({ objectID }) => !newObjectIds.includes(objectID)) + staleIds = staleIds.concat( + batch + .filter(({ objectID }) => !objectIDs.includes(objectID)) + .map(({ objectID }) => objectID) ) }, }) - const staleIds = staleObjects.map(({ objectID }) => objectID) - if (staleIds.length > 0) { console.log(`deleting ${staleIds.length} stale indices:`) console.log(staleIds) @@ -99,3 +105,25 @@ async function indexSearchContent(objects) { throw new Error(error) } } + +async function collectHeadings(mdxContent) { + const headings = [] + + const headingMapper = () => (tree) => { + visit(tree, 'heading', (node) => { + const title = node.children.reduce((m, n) => { + if (n.value) m += n.value + return m + }, '') + // Only include level 1 or level 2 headings + if (node.depth < 3) { + headings.push(title) + } + }) + } + + return remark() + .use(headingMapper) + .process(mdxContent) + .then(() => headings) +}