diff --git a/packages/docusaurus-theme-search-algolia/package.json b/packages/docusaurus-theme-search-algolia/package.json index 2ae636ca55..ba0db4ccd2 100644 --- a/packages/docusaurus-theme-search-algolia/package.json +++ b/packages/docusaurus-theme-search-algolia/package.json @@ -9,6 +9,7 @@ "license": "MIT", "dependencies": { "@docsearch/react": "^1.0.0-alpha.24", + "@hapi/joi": "^17.1.1", "algoliasearch": "^4.0.0", "algoliasearch-helper": "^3.1.1", "clsx": "^1.1.1", diff --git a/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.js b/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.js new file mode 100644 index 0000000000..cb842e8233 --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.js @@ -0,0 +1,91 @@ +/** + * 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 {validateThemeConfig, DEFAULT_CONFIG} = require('../validateThemeConfig'); + +function testValidateThemeConfig(themeConfig) { + function validate(schema, cfg) { + const {value, error} = schema.validate(cfg, { + convert: false, + }); + if (error) { + throw error; + } + return value; + } + + return validateThemeConfig({themeConfig, validate}); +} + +describe('validateThemeConfig', () => { + test('minimal config', () => { + const algolia = { + indexName: 'index', + apiKey: 'apiKey', + }; + expect(testValidateThemeConfig({algolia})).toEqual({ + algolia: { + ...DEFAULT_CONFIG, + ...algolia, + }, + }); + }); + + test('unknown attributes', () => { + const algolia = { + indexName: 'index', + apiKey: 'apiKey', + unknownKey: 'unknownKey', + }; + expect(testValidateThemeConfig({algolia})).toEqual({ + algolia: { + ...DEFAULT_CONFIG, + ...algolia, + }, + }); + }); + + test('undefined config', () => { + const algolia = undefined; + expect(() => + testValidateThemeConfig({algolia}), + ).toThrowErrorMatchingInlineSnapshot( + `"\\"themeConfig.algolia\\" is required"`, + ); + }); + + test('undefined config 2', () => { + expect(() => + testValidateThemeConfig({}), + ).toThrowErrorMatchingInlineSnapshot( + `"\\"themeConfig.algolia\\" is required"`, + ); + }); + + test('empty config', () => { + const algolia = {}; + expect(() => + testValidateThemeConfig({algolia}), + ).toThrowErrorMatchingInlineSnapshot(`"\\"algolia.apiKey\\" is required"`); + }); + + test('missing indexName config', () => { + const algolia = {apiKey: 'apiKey'}; + expect(() => + testValidateThemeConfig({algolia}), + ).toThrowErrorMatchingInlineSnapshot( + `"\\"algolia.indexName\\" is required"`, + ); + }); + + test('missing apiKey config', () => { + const algolia = {indexName: 'indexName'}; + expect(() => + testValidateThemeConfig({algolia}), + ).toThrowErrorMatchingInlineSnapshot(`"\\"algolia.apiKey\\" is required"`); + }); +}); diff --git a/packages/docusaurus-theme-search-algolia/src/index.js b/packages/docusaurus-theme-search-algolia/src/index.js index 634108bded..958c0d6f81 100644 --- a/packages/docusaurus-theme-search-algolia/src/index.js +++ b/packages/docusaurus-theme-search-algolia/src/index.js @@ -10,10 +10,11 @@ const fs = require('fs'); const eta = require('eta'); const {normalizeUrl} = require('@docusaurus/utils'); const openSearchTemplate = require('./templates/opensearch'); +const {validateThemeConfig} = require('./validateThemeConfig'); const OPEN_SEARCH_FILENAME = 'opensearch.xml'; -module.exports = function (context) { +function theme(context) { const { baseUrl, siteConfig: {title, url, favicon}, @@ -70,4 +71,8 @@ module.exports = function (context) { }; }, }; -}; +} + +module.exports = theme; + +theme.validateThemeConfig = validateThemeConfig; diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js index 7596f5ce13..3c0e3c8c1d 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js @@ -128,17 +128,7 @@ function DocSearch(props) { } function SearchBar() { - const {siteConfig = {}} = useDocusaurusContext(); - - if (!siteConfig.themeConfig.algolia) { - // eslint-disable-next-line no-console - console.warn(`DocSearch requires an \`algolia\` field in your \`themeConfig\`. - -See: https://v2.docusaurus.io/docs/search/#using-algolia-docsearch`); - - return null; - } - + const {siteConfig} = useDocusaurusContext(); return ; } diff --git a/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js b/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js new file mode 100644 index 0000000000..adda5956c5 --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js @@ -0,0 +1,34 @@ +/** + * 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 Joi = require('@hapi/joi'); + +const DEFAULT_CONFIG = { + // By default, all Docusaurus sites are using the same AppId + // This has been designed on purpose with Algolia. + appId: 'BH4D9OD16A', +}; +exports.DEFAULT_CONFIG = DEFAULT_CONFIG; + +const Schema = Joi.object({ + algolia: Joi.object({ + appId: Joi.string().default(DEFAULT_CONFIG.appId), + apiKey: Joi.string().required(), + indexName: Joi.string().required(), + }) + .label('themeConfig.algolia') + .required() + .unknown(), // DocSearch 3 is still alpha: don't validate the rest for now +}); +exports.Schema = Schema; + +exports.validateThemeConfig = function validateThemeConfig({ + validate, + themeConfig, +}) { + return validate(Schema, themeConfig); +};