diff --git a/.eslintignore b/.eslintignore index 6a8267f9478f..c75dfdc42484 100644 --- a/.eslintignore +++ b/.eslintignore @@ -14,6 +14,7 @@ packages/docusaurus/lib/ packages/docusaurus-*/lib/* packages/docusaurus-*/lib-next/ packages/docusaurus-plugin-ideal-image/copyUntypedFiles.js +packages/docusaurus-theme-search-algolia/copyUntypedFiles.js packages/create-docusaurus/lib/* packages/create-docusaurus/templates/facebook/.eslintrc.js diff --git a/packages/docusaurus-plugin-ideal-image/copyUntypedFiles.js b/packages/docusaurus-plugin-ideal-image/copyUntypedFiles.js index 3f814cf0990d..e5555c55368f 100644 --- a/packages/docusaurus-plugin-ideal-image/copyUntypedFiles.js +++ b/packages/docusaurus-plugin-ideal-image/copyUntypedFiles.js @@ -15,6 +15,6 @@ const srcDir = path.resolve(__dirname, 'src'); const libDir = path.resolve(__dirname, 'lib'); fs.copySync(srcDir, libDir, { filter(filepath) { - return !/__tests__/.test(filepath) && !/\.ts$/.test(filepath); + return !/__tests__/.test(filepath) && !/\.tsx?$/.test(filepath); }, }); diff --git a/packages/docusaurus-theme-classic/update-code-translations.js b/packages/docusaurus-theme-classic/update-code-translations.js index 18e570c06d7a..73cba2ff9679 100644 --- a/packages/docusaurus-theme-classic/update-code-translations.js +++ b/packages/docusaurus-theme-classic/update-code-translations.js @@ -15,7 +15,7 @@ const CodeDirPaths = [ path.join(__dirname, 'lib-next'), // TODO other themes should rather define their own translations in the future? path.join(__dirname, '..', 'docusaurus-theme-common', 'lib'), - path.join(__dirname, '..', 'docusaurus-theme-search-algolia', 'src', 'theme'), + path.join(__dirname, '..', 'docusaurus-theme-search-algolia', 'lib', 'theme'), path.join(__dirname, '..', 'docusaurus-theme-live-codeblock', 'src', 'theme'), path.join(__dirname, '..', 'docusaurus-plugin-pwa', 'src', 'theme'), ]; diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index e1bb425c61f3..0a7890aba957 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -22,6 +22,8 @@ export {createStorageSlot, listStorageKeys} from './utils/storageUtils'; export {useAlternatePageUtils} from './utils/useAlternatePageUtils'; +export {useContextualSearchFilters} from './utils/useContextualSearchFilters'; + export { parseCodeBlockTitle, parseLanguage, diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useContextualSearchFilters.ts b/packages/docusaurus-theme-common/src/utils/useContextualSearchFilters.ts similarity index 82% rename from packages/docusaurus-theme-classic/src/theme/hooks/useContextualSearchFilters.ts rename to packages/docusaurus-theme-common/src/utils/useContextualSearchFilters.ts index 38e27d2bc0e6..2dd1741d1b8f 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useContextualSearchFilters.ts +++ b/packages/docusaurus-theme-common/src/utils/useContextualSearchFilters.ts @@ -5,21 +5,18 @@ * LICENSE file in the root directory of this source tree. */ import {useAllDocsData, useActivePluginAndVersion} from '@theme/hooks/useDocs'; -import { - useDocsPreferredVersionByPluginId, - DEFAULT_SEARCH_TAG, - docVersionSearchTag, -} from '@docusaurus/theme-common'; +import {useDocsPreferredVersionByPluginId} from './docsPreferredVersion/useDocsPreferredVersion'; +import {docVersionSearchTag, DEFAULT_SEARCH_TAG} from './searchUtils'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -type ContextualSearchFilters = { +export type useContextualSearchFiltersReturns = { locale: string; tags: string[]; }; // We may want to support multiple search engines, don't couple that to Algolia/DocSearch // Maybe users will want to use its own search engine solution -export default function useContextualSearchFilters(): ContextualSearchFilters { +export function useContextualSearchFilters(): useContextualSearchFiltersReturns { const {i18n} = useDocusaurusContext(); const allDocsData = useAllDocsData(); const activePluginAndVersion = useActivePluginAndVersion(); diff --git a/packages/docusaurus-theme-search-algolia/copyUntypedFiles.js b/packages/docusaurus-theme-search-algolia/copyUntypedFiles.js new file mode 100644 index 000000000000..e5555c55368f --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/copyUntypedFiles.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const path = require('path'); +const fs = require('fs-extra'); + +/** + * Copy all untyped and static assets files to lib. + */ +const srcDir = path.resolve(__dirname, 'src'); +const libDir = path.resolve(__dirname, 'lib'); +fs.copySync(srcDir, libDir, { + filter(filepath) { + return !/__tests__/.test(filepath) && !/\.tsx?$/.test(filepath); + }, +}); diff --git a/packages/docusaurus-theme-search-algolia/package.json b/packages/docusaurus-theme-search-algolia/package.json index 3e64f07c68a4..b7b57443da75 100644 --- a/packages/docusaurus-theme-search-algolia/package.json +++ b/packages/docusaurus-theme-search-algolia/package.json @@ -2,7 +2,8 @@ "name": "@docusaurus/theme-search-algolia", "version": "2.0.0-beta.9", "description": "Algolia search component for Docusaurus.", - "main": "src/index.js", + "main": "lib/index.js", + "types": "src/theme-search-algolia.d.ts", "publishConfig": { "access": "public" }, @@ -12,6 +13,12 @@ "directory": "packages/docusaurus-theme-search-algolia" }, "license": "MIT", + "scripts": { + "build": "yarn build:server && yarn build:browser && yarn build:copy", + "build:server": "tsc --project tsconfig.server.json", + "build:browser": "tsc --project tsconfig.browser.json", + "build:copy": "node copyUntypedFiles.js" + }, "dependencies": { "@docsearch/react": "^3.0.0-alpha.39", "@docusaurus/core": "2.0.0-beta.9", @@ -24,6 +31,10 @@ "eta": "^1.12.3", "lodash": "^4.17.20" }, + "devDependencies": { + "@docusaurus/module-type-aliases": "2.0.0-beta.9", + "fs-extra": "^10.0.0" + }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" diff --git a/packages/docusaurus-theme-search-algolia/src/index.js b/packages/docusaurus-theme-search-algolia/src/index.ts similarity index 70% rename from packages/docusaurus-theme-search-algolia/src/index.js rename to packages/docusaurus-theme-search-algolia/src/index.ts index e0988535f20a..dba9b8e2c32a 100644 --- a/packages/docusaurus-theme-search-algolia/src/index.js +++ b/packages/docusaurus-theme-search-algolia/src/index.ts @@ -5,26 +5,33 @@ * LICENSE file in the root directory of this source tree. */ -const path = require('path'); -const fs = require('fs'); -const eta = require('eta'); -const {normalizeUrl, getSwizzledComponent} = require('@docusaurus/utils'); -const openSearchTemplate = require('./templates/opensearch'); -const {validateThemeConfig} = require('./validateThemeConfig'); -const {memoize} = require('lodash'); +import path from 'path'; +import fs from 'fs'; +import {defaultConfig, compile} from 'eta'; +import {normalizeUrl, getSwizzledComponent} from '@docusaurus/utils'; +import openSearchTemplate from './templates/opensearch'; +import {memoize} from 'lodash'; + +import type {DocusaurusContext, Plugin} from '@docusaurus/types'; const getCompiledOpenSearchTemplate = memoize(() => { - return eta.compile(openSearchTemplate.trim()); + return compile(openSearchTemplate.trim()); }); -function renderOpenSearchTemplate(data) { +function renderOpenSearchTemplate(data: { + title: string; + url: string; + favicon: string | null; +}) { const compiled = getCompiledOpenSearchTemplate(); - return compiled(data, eta.defaultConfig); + return compiled(data, defaultConfig); } const OPEN_SEARCH_FILENAME = 'opensearch.xml'; -function theme(context) { +export default function theme( + context: DocusaurusContext & {baseUrl: string}, +): Plugin { const { baseUrl, siteConfig: {title, url, favicon}, @@ -37,12 +44,16 @@ function theme(context) { return { name: 'docusaurus-theme-search-algolia', + getPathsToWatch() { + return [pagePath]; + }, + getThemePath() { return path.resolve(__dirname, './theme'); }, - getPathsToWatch() { - return [pagePath]; + getTypeScriptThemePath() { + return path.resolve(__dirname, '..', 'src', 'theme'); }, async contentLoaded({actions: {addRoute}}) { @@ -87,6 +98,4 @@ function theme(context) { }; } -module.exports = theme; - -theme.validateThemeConfig = validateThemeConfig; +export {validateThemeConfig} from './validateThemeConfig'; diff --git a/packages/docusaurus-theme-search-algolia/src/templates/opensearch.js b/packages/docusaurus-theme-search-algolia/src/templates/opensearch.ts similarity index 97% rename from packages/docusaurus-theme-search-algolia/src/templates/opensearch.js rename to packages/docusaurus-theme-search-algolia/src/templates/opensearch.ts index 75e974f44c66..04295bdde55e 100644 --- a/packages/docusaurus-theme-search-algolia/src/templates/opensearch.js +++ b/packages/docusaurus-theme-search-algolia/src/templates/opensearch.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -module.exports = ` +export default ` diff --git a/packages/docusaurus-theme-search-algolia/src/theme-search-algolia.d.ts b/packages/docusaurus-theme-search-algolia/src/theme-search-algolia.d.ts new file mode 100644 index 000000000000..e93073b41f65 --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/src/theme-search-algolia.d.ts @@ -0,0 +1,49 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +declare module '@docusaurus/theme-search-algolia' { + export type Options = never; +} + +declare module '@theme/hooks/useSearchQuery' { + export interface SearchQuery { + searchQuery: string; + + setSearchQuery(newSearchQuery: string): void; + + generateSearchPageLink(targetSearchQuery: string): string; + } + + export default function useSearchQuery(): SearchQuery; +} + +declare module '@theme/hooks/useAlgoliaContextualFacetFilters' { + export type useAlgoliaContextualFacetFiltersReturns = [string, string[]]; + + export default function useAlgoliaContextualFacetFilters(): useAlgoliaContextualFacetFiltersReturns; +} + +declare module '@theme/SearchPage' { + const SearchPage: () => JSX.Element; + export default SearchPage; +} + +declare module '@theme/SearchMetadata' { + export type SearchMetadataProps = { + readonly locale?: string; + readonly version?: string; + readonly tag?: string; + }; + + const SearchMetadata: (props: SearchMetadataProps) => JSX.Element; + export default SearchMetadata; +} + +declare module '@theme/SearchBar' { + const SearchBar: () => JSX.Element; + export default SearchBar; +} diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx similarity index 72% rename from packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js rename to packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx index b61c86f418fa..9cd2e6cb0949 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import React, {useState, useRef, useCallback, useMemo} from 'react'; import {createPortal} from 'react-dom'; @@ -19,13 +20,42 @@ import useAlgoliaContextualFacetFilters from '@theme/hooks/useAlgoliaContextualF import {translate} from '@docusaurus/Translate'; import styles from './styles.module.css'; -let DocSearchModal = null; +import type { + DocSearchModal as DocSearchModalType, + DocSearchModalProps, +} from '@docsearch/react'; +import type { + InternalDocSearchHit, + StoredDocSearchHit, +} from '@docsearch/react/dist/esm/types'; +import type {AutocompleteState} from '@algolia/autocomplete-core'; -function Hit({hit, children}) { +type DocSearchProps = Omit< + DocSearchModalProps, + 'onClose' | 'initialScrollY' +> & { + contextualSearch?: string; + externalUrlRegex?: string; +}; + +let DocSearchModal: typeof DocSearchModalType | null = null; + +function Hit({ + hit, + children, +}: { + hit: InternalDocSearchHit | StoredDocSearchHit; + children: React.ReactNode; +}) { return {children}; } -function ResultsFooter({state, onClose}) { +type ResultsFooterProps = { + state: AutocompleteState; + onClose: () => void; +}; + +function ResultsFooter({state, onClose}: ResultsFooterProps) { const {generateSearchPageLink} = useSearchQuery(); return ( @@ -35,7 +65,11 @@ function ResultsFooter({state, onClose}) { ); } -function DocSearch({contextualSearch, externalUrlRegex, ...props}) { +function DocSearch({ + contextualSearch, + externalUrlRegex, + ...props +}: DocSearchProps) { const {siteMetadata} = useDocusaurusContext(); const contextualSearchFacetFilters = useAlgoliaContextualFacetFilters(); @@ -56,10 +90,12 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) { const {withBaseUrl} = useBaseUrlUtils(); const history = useHistory(); - const searchContainer = useRef(null); - const searchButtonRef = useRef(null); + const searchContainer = useRef(null); + const searchButtonRef = useRef(null); const [isOpen, setIsOpen] = useState(false); - const [initialQuery, setInitialQuery] = useState(null); + const [initialQuery, setInitialQuery] = useState( + undefined, + ); const importDocSearchModalIfNeeded = useCallback(() => { if (DocSearchModal) { @@ -67,7 +103,9 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) { } return Promise.all([ + // @ts-ignore import('@docsearch/react/modal'), + // @ts-ignore import('@docsearch/react/style'), import('./styles.css'), ]).then(([{DocSearchModal: Modal}]) => { @@ -88,7 +126,7 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) { const onClose = useCallback(() => { setIsOpen(false); - searchContainer.current.remove(); + searchContainer.current?.remove(); }, [setIsOpen]); const onInput = useCallback( @@ -102,35 +140,38 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) { ); const navigator = useRef({ - navigate({itemUrl}) { + navigate({itemUrl}: {itemUrl?: string}) { // Algolia results could contain URL's from other domains which cannot // be served through history and should navigate with window.location if (isRegexpStringMatch(externalUrlRegex, itemUrl)) { - window.location.href = itemUrl; + window.location.href = itemUrl!; } else { - history.push(itemUrl); + history.push(itemUrl!); } }, }).current; - const transformItems = useRef((items) => { - return items.map((item) => { - // If Algolia contains a external domain, we should navigate without relative URL - if (isRegexpStringMatch(externalUrlRegex, item.url)) { - return item; - } + const transformItems = useRef( + (items) => { + return items.map((item) => { + // If Algolia contains a external domain, we should navigate without relative URL + if (isRegexpStringMatch(externalUrlRegex, item.url)) { + return item; + } - // We transform the absolute URL into a relative URL. - const url = new URL(item.url); - return { - ...item, - url: withBaseUrl(`${url.pathname}${url.hash}`), - }; - }); - }).current; + // We transform the absolute URL into a relative URL. + const url = new URL(item.url); + return { + ...item, + url: withBaseUrl(`${url.pathname}${url.hash}`), + }; + }); + }, + ).current; const resultsFooterComponent = useMemo( - () => (footerProps) => , + () => (footerProps: ResultsFooterProps) => + , [onClose], ); @@ -188,6 +229,8 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) { {isOpen && + DocSearchModal && + searchContainer.current && createPortal( ; } diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchMetadata/index.js b/packages/docusaurus-theme-search-algolia/src/theme/SearchMetadata/index.tsx similarity index 80% rename from packages/docusaurus-theme-search-algolia/src/theme/SearchMetadata/index.js rename to packages/docusaurus-theme-search-algolia/src/theme/SearchMetadata/index.tsx index 584ce2a9c3c2..1984d7221f5c 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchMetadata/index.js +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchMetadata/index.tsx @@ -8,9 +8,14 @@ import React from 'react'; import Head from '@docusaurus/Head'; +import type {SearchMetadataProps} from '@theme/SearchMetadata'; // Override default/agnostic SearchMetas to use Algolia-specific metadata -export default function AlgoliaSearchMetadata({locale, version, tag}) { +function SearchMetadata({ + locale, + version, + tag, +}: SearchMetadataProps): JSX.Element { // Seems safe to consider here the locale is the language, // as the existing docsearch:language filter is afaik a regular string-based filter const language = locale; @@ -23,3 +28,5 @@ export default function AlgoliaSearchMetadata({locale, version, tag}) { ); } + +export default SearchMetadata; diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.js b/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx similarity index 91% rename from packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.js rename to packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx index b89e233f7891..18e9231d5dd2 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.js +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx @@ -6,6 +6,7 @@ */ /* eslint-disable jsx-a11y/no-autofocus */ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import React, {useEffect, useState, useReducer, useRef} from 'react'; @@ -32,7 +33,7 @@ import styles from './styles.module.css'; // Very simple pluralization: probably good enough for now function useDocumentsFoundPlural() { const {selectMessage} = usePluralForm(); - return (count) => + return (count: number) => selectMessage( count, translate( @@ -52,14 +53,19 @@ function useDocsSearchVersionsHelpers() { // State of the version select menus / algolia facet filters // docsPluginId -> versionName map - const [searchVersions, setSearchVersions] = useState(() => { - return Object.entries(allDocsData).reduce((acc, [pluginId, pluginData]) => { - return {...acc, [pluginId]: pluginData.versions[0].name}; - }, {}); - }); + const [searchVersions, setSearchVersions] = useState>( + () => { + return Object.entries(allDocsData).reduce( + (acc, [pluginId, pluginData]) => { + return {...acc, [pluginId]: pluginData.versions[0].name}; + }, + {}, + ); + }, + ); // Set the value of a single select menu - const setSearchVersion = (pluginId, searchVersion) => + const setSearchVersion = (pluginId: string, searchVersion: string) => setSearchVersions((s) => ({...s, [pluginId]: searchVersion})); const versioningEnabled = Object.values(allDocsData).some( @@ -75,7 +81,11 @@ function useDocsSearchVersionsHelpers() { } // We want to display one select per versioned docs plugin instance -const SearchVersionSelectList = ({docsSearchVersionsHelpers}) => { +const SearchVersionSelectList = ({ + docsSearchVersionsHelpers, +}: { + docsSearchVersionsHelpers: ReturnType; +}) => { const versionedPluginEntries = Object.entries( docsSearchVersionsHelpers.allDocsData, ) @@ -118,10 +128,32 @@ const SearchVersionSelectList = ({docsSearchVersionsHelpers}) => { ); }; -function SearchPage() { +type ResultDispatcherState = { + items: { + title: string; + url: string; + summary: string; + breadcrumbs: string[]; + }[]; + query: string | null; + totalResults: number | null; + totalPages: number | null; + lastPage: number | null; + hasMore: boolean | null; + loading: boolean | null; +}; + +type ResultDispatcher = + | {type: 'reset'; value?: undefined} + | {type: 'loading'; value?: undefined} + | {type: 'update'; value: ResultDispatcherState} + | {type: 'advance'; value?: undefined}; + +function SearchPage(): JSX.Element { const { siteConfig: { themeConfig: { + // @ts-ignore algolia: {appId, apiKey, indexName, externalUrlRegex}, }, }, @@ -131,7 +163,7 @@ function SearchPage() { const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers(); const {searchQuery, setSearchQuery} = useSearchQuery(); - const initialSearchResultState = { + const initialSearchResultState: ResultDispatcherState = { items: [], query: null, totalResults: null, @@ -141,8 +173,8 @@ function SearchPage() { loading: null, }; const [searchResultState, searchResultStateDispatcher] = useReducer( - (prevState, {type, value: state}) => { - switch (type) { + (prevState: ResultDispatcherState, data: ResultDispatcher) => { + switch (data.type) { case 'reset': { return initialSearchResultState; } @@ -150,24 +182,24 @@ function SearchPage() { return {...prevState, loading: true}; } case 'update': { - if (searchQuery !== state.query) { + if (searchQuery !== data.value.query) { return prevState; } return { - ...state, + ...data.value, items: - state.lastPage === 0 - ? state.items - : prevState.items.concat(state.items), + data.value.lastPage === 0 + ? data.value.items + : prevState.items.concat(data.value.items), }; } case 'advance': { - const hasMore = prevState.totalPages > prevState.lastPage + 1; + const hasMore = prevState.totalPages! > prevState.lastPage! + 1; return { ...prevState, - lastPage: hasMore ? prevState.lastPage + 1 : prevState.lastPage, + lastPage: hasMore ? prevState.lastPage! + 1 : prevState.lastPage, hasMore, }; } @@ -193,7 +225,7 @@ function SearchPage() { return; } - const sanitizeValue = (value) => { + const sanitizeValue = (value: string) => { return value.replace( /algolia-docsearch-suggestion--highlight/g, 'search-result-match', @@ -212,7 +244,7 @@ function SearchPage() { }); return { - title: titles.pop(), + title: titles.pop()!, url: isRegexpStringMatch(externalUrlRegex, parsedURL.href) ? parsedURL.href : parsedURL.pathname + parsedURL.hash, @@ -239,7 +271,7 @@ function SearchPage() { }, ); - const [loaderRef, setLoaderRef] = useState(null); + const [loaderRef, setLoaderRef] = useState(null); const prevY = useRef(0); const observer = useRef( ExecutionEnvironment.canUseDOM && @@ -278,7 +310,7 @@ function SearchPage() { description: 'The search page title for empty query', }); - const makeSearch = useDynamicCallback((page = 0) => { + const makeSearch = useDynamicCallback((page: number = 0) => { algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default'); algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale); @@ -299,9 +331,11 @@ function SearchPage() { return undefined; } const currentObserver = observer.current; - - currentObserver.observe(loaderRef); - return () => currentObserver.unobserve(loaderRef); + if (currentObserver) { + currentObserver.observe(loaderRef); + return () => currentObserver.unobserve(loaderRef); + } + return () => true; }, [loaderRef]); useEffect(() => { diff --git a/packages/docusaurus-theme-search-algolia/src/theme/hooks/useAlgoliaContextualFacetFilters.js b/packages/docusaurus-theme-search-algolia/src/theme/hooks/useAlgoliaContextualFacetFilters.ts similarity index 66% rename from packages/docusaurus-theme-search-algolia/src/theme/hooks/useAlgoliaContextualFacetFilters.js rename to packages/docusaurus-theme-search-algolia/src/theme/hooks/useAlgoliaContextualFacetFilters.ts index 3ee15f665d16..a1ca093d0ab9 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/hooks/useAlgoliaContextualFacetFilters.js +++ b/packages/docusaurus-theme-search-algolia/src/theme/hooks/useAlgoliaContextualFacetFilters.ts @@ -5,10 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import useContextualSearchFilters from '@theme/hooks/useContextualSearchFilters'; +import type {useAlgoliaContextualFacetFiltersReturns} from '@theme/hooks/useAlgoliaContextualFacetFilters'; +import {useContextualSearchFilters} from '@docusaurus/theme-common'; // Translate search-engine agnostic search filters to Algolia search filters -export default function useAlgoliaContextualFacetFilters() { +export default function useAlgoliaContextualFacetFilters(): useAlgoliaContextualFacetFiltersReturns { const {locale, tags} = useContextualSearchFilters(); // seems safe to convert locale->language, see AlgoliaSearchMetadata comment diff --git a/packages/docusaurus-theme-search-algolia/src/theme/hooks/useSearchQuery.js b/packages/docusaurus-theme-search-algolia/src/theme/hooks/useSearchQuery.ts similarity index 89% rename from packages/docusaurus-theme-search-algolia/src/theme/hooks/useSearchQuery.js rename to packages/docusaurus-theme-search-algolia/src/theme/hooks/useSearchQuery.ts index 5789251fa814..7667f4de2360 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/hooks/useSearchQuery.js +++ b/packages/docusaurus-theme-search-algolia/src/theme/hooks/useSearchQuery.ts @@ -8,10 +8,11 @@ import {useHistory} from '@docusaurus/router'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import {useCallback, useEffect, useState} from 'react'; +import type {SearchQuery} from '@theme/hooks/useSearchQuery'; const SEARCH_PARAM_QUERY = 'q'; -function useSearchQuery() { +function useSearchQuery(): SearchQuery { const history = useHistory(); const { siteConfig: {baseUrl}, @@ -28,7 +29,7 @@ function useSearchQuery() { }, []); const setSearchQuery = useCallback( - (newSearchQuery) => { + (newSearchQuery: string) => { const searchParams = new URLSearchParams(window.location.search); if (newSearchQuery) { @@ -46,7 +47,7 @@ function useSearchQuery() { ); const generateSearchPageLink = useCallback( - (targetSearchQuery) => { + (targetSearchQuery: string) => { // Refer to https://github.com/facebook/docusaurus/pull/2838 return `${baseUrl}search?q=${encodeURIComponent(targetSearchQuery)}`; }, diff --git a/packages/docusaurus-theme-search-algolia/src/types.d.ts b/packages/docusaurus-theme-search-algolia/src/types.d.ts new file mode 100644 index 000000000000..04ccef3c4442 --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/src/types.d.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/// +/// +/// diff --git a/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js b/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.ts similarity index 78% rename from packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js rename to packages/docusaurus-theme-search-algolia/src/validateThemeConfig.ts index 8cfe4bfed9e3..b2afb4c1aa84 100644 --- a/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js +++ b/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.ts @@ -5,7 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -const {Joi} = require('@docusaurus/utils-validation'); +import {Joi} from '@docusaurus/utils-validation'; +import type {ThemeConfig, Validate, ValidationResult} from '@docusaurus/types'; const DEFAULT_CONFIG = { contextualSearch: false, // future: maybe we want to enable this by default @@ -18,7 +19,7 @@ const DEFAULT_CONFIG = { }; exports.DEFAULT_CONFIG = DEFAULT_CONFIG; -const Schema = Joi.object({ +export const Schema = Joi.object({ algolia: Joi.object({ // Docusaurus attributes contextualSearch: Joi.boolean().default(DEFAULT_CONFIG.contextualSearch), @@ -35,11 +36,13 @@ const Schema = Joi.object({ .required() .unknown(), // DocSearch 3 is still alpha: don't validate the rest for now }); -exports.Schema = Schema; -exports.validateThemeConfig = function validateThemeConfig({ +export function validateThemeConfig({ validate, themeConfig, -}) { +}: { + validate: Validate; + themeConfig: ThemeConfig; +}): ValidationResult { return validate(Schema, themeConfig); -}; +} diff --git a/packages/docusaurus-theme-search-algolia/tsconfig.browser.json b/packages/docusaurus-theme-search-algolia/tsconfig.browser.json new file mode 100644 index 000000000000..3af8257cdf8d --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/tsconfig.browser.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "esnext", + "jsx": "react-native" + }, + "include": ["src/theme/", "src/*.d.ts"] +} diff --git a/packages/docusaurus-theme-search-algolia/tsconfig.json b/packages/docusaurus-theme-search-algolia/tsconfig.json new file mode 100644 index 000000000000..4b7c4f42a698 --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "lib": ["DOM", "ES2019"], + "rootDir": "src", + "baseUrl": "src", + "outDir": "lib" + } +} diff --git a/packages/docusaurus-theme-search-algolia/tsconfig.server.json b/packages/docusaurus-theme-search-algolia/tsconfig.server.json new file mode 100644 index 000000000000..3faf6c3e997b --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/tsconfig.server.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/*.ts", "src/templates/*.ts"] +}