diff --git a/packages/docusaurus/src/server/__tests__/__fixtures__/configs/configAsync.config.js b/packages/docusaurus/src/server/__tests__/__fixtures__/configs/configAsync.config.js new file mode 100644 index 000000000000..86ac8a3af109 --- /dev/null +++ b/packages/docusaurus/src/server/__tests__/__fixtures__/configs/configAsync.config.js @@ -0,0 +1,15 @@ +/** + * 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. + */ + +module.exports = Promise.resolve({ + title: 'Hello', + tagline: 'Hello World', + organizationName: 'endiliey', + projectName: 'hello', + baseUrl: '/', + url: 'https://docusaurus.io', +}); diff --git a/packages/docusaurus/src/server/__tests__/__fixtures__/configs/createConfig.config.js b/packages/docusaurus/src/server/__tests__/__fixtures__/configs/createConfig.config.js new file mode 100644 index 000000000000..1d423bd45140 --- /dev/null +++ b/packages/docusaurus/src/server/__tests__/__fixtures__/configs/createConfig.config.js @@ -0,0 +1,17 @@ +/** + * 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. + */ + +module.exports = function createConfig() { + return { + title: 'Hello', + tagline: 'Hello World', + organizationName: 'endiliey', + projectName: 'hello', + baseUrl: '/', + url: 'https://docusaurus.io', + }; +}; diff --git a/packages/docusaurus/src/server/__tests__/__fixtures__/configs/createConfigAsync.config.js b/packages/docusaurus/src/server/__tests__/__fixtures__/configs/createConfigAsync.config.js new file mode 100644 index 000000000000..34b4bbc36f1e --- /dev/null +++ b/packages/docusaurus/src/server/__tests__/__fixtures__/configs/createConfigAsync.config.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. + */ + +module.exports = async function createConfig() { + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); + return { + title: 'Hello', + tagline: 'Hello World', + organizationName: 'endiliey', + projectName: 'hello', + baseUrl: '/', + url: 'https://docusaurus.io', + }; +}; diff --git a/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap b/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap index 21b070a9b41e..5256204af034 100644 --- a/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap +++ b/packages/docusaurus/src/server/__tests__/__snapshots__/config.test.ts.snap @@ -11,6 +11,102 @@ If you still want these fields to be in your configuration, put them in the \\"c See https://docusaurus.io/docs/docusaurus.config.js/#customfields" `; +exports[`loadConfig website with valid async config 1`] = ` +Object { + "baseUrl": "/", + "baseUrlIssueBanner": true, + "customFields": Object {}, + "i18n": Object { + "defaultLocale": "en", + "localeConfigs": Object {}, + "locales": Array [ + "en", + ], + }, + "noIndex": false, + "onBrokenLinks": "throw", + "onBrokenMarkdownLinks": "warn", + "onDuplicateRoutes": "warn", + "organizationName": "endiliey", + "plugins": Array [], + "presets": Array [], + "projectName": "hello", + "staticDirectories": Array [ + "static", + ], + "tagline": "Hello World", + "themeConfig": Object {}, + "themes": Array [], + "title": "Hello", + "titleDelimiter": "|", + "url": "https://docusaurus.io", +} +`; + +exports[`loadConfig website with valid async config creator function 1`] = ` +Object { + "baseUrl": "/", + "baseUrlIssueBanner": true, + "customFields": Object {}, + "i18n": Object { + "defaultLocale": "en", + "localeConfigs": Object {}, + "locales": Array [ + "en", + ], + }, + "noIndex": false, + "onBrokenLinks": "throw", + "onBrokenMarkdownLinks": "warn", + "onDuplicateRoutes": "warn", + "organizationName": "endiliey", + "plugins": Array [], + "presets": Array [], + "projectName": "hello", + "staticDirectories": Array [ + "static", + ], + "tagline": "Hello World", + "themeConfig": Object {}, + "themes": Array [], + "title": "Hello", + "titleDelimiter": "|", + "url": "https://docusaurus.io", +} +`; + +exports[`loadConfig website with valid config creator function 1`] = ` +Object { + "baseUrl": "/", + "baseUrlIssueBanner": true, + "customFields": Object {}, + "i18n": Object { + "defaultLocale": "en", + "localeConfigs": Object {}, + "locales": Array [ + "en", + ], + }, + "noIndex": false, + "onBrokenLinks": "throw", + "onBrokenMarkdownLinks": "warn", + "onDuplicateRoutes": "warn", + "organizationName": "endiliey", + "plugins": Array [], + "presets": Array [], + "projectName": "hello", + "staticDirectories": Array [ + "static", + ], + "tagline": "Hello World", + "themeConfig": Object {}, + "themes": Array [], + "title": "Hello", + "titleDelimiter": "|", + "url": "https://docusaurus.io", +} +`; + exports[`loadConfig website with valid siteConfig 1`] = ` Object { "baseUrl": "/", diff --git a/packages/docusaurus/src/server/__tests__/config.test.ts b/packages/docusaurus/src/server/__tests__/config.test.ts index 15481372ecbb..f31654598a8c 100644 --- a/packages/docusaurus/src/server/__tests__/config.test.ts +++ b/packages/docusaurus/src/server/__tests__/config.test.ts @@ -16,45 +16,75 @@ describe('loadConfig', () => { 'simple-site', 'docusaurus.config.js', ); - const config = loadConfig(siteDir); + const config = await loadConfig(siteDir); expect(config).toMatchSnapshot(); expect(config).not.toEqual({}); }); - test('website with incomplete siteConfig', () => { + test('website with valid config creator function', async () => { + const siteDir = path.join( + __dirname, + '__fixtures__', + 'configs', + 'createConfig.config.js', + ); + const config = await loadConfig(siteDir); + expect(config).toMatchSnapshot(); + expect(config).not.toEqual({}); + }); + + test('website with valid async config', async () => { + const siteDir = path.join( + __dirname, + '__fixtures__', + 'configs', + 'configAsync.config.js', + ); + const config = await loadConfig(siteDir); + expect(config).toMatchSnapshot(); + expect(config).not.toEqual({}); + }); + + test('website with valid async config creator function', async () => { + const siteDir = path.join( + __dirname, + '__fixtures__', + 'configs', + 'createConfigAsync.config.js', + ); + const config = await loadConfig(siteDir); + expect(config).toMatchSnapshot(); + expect(config).not.toEqual({}); + }); + + test('website with incomplete siteConfig', async () => { const siteDir = path.join( __dirname, '__fixtures__', 'bad-site', 'docusaurus.config.js', ); - expect(() => { - loadConfig(siteDir); - }).toThrowErrorMatchingSnapshot(); + await expect(loadConfig(siteDir)).rejects.toThrowErrorMatchingSnapshot(); }); - test('website with useless field (wrong field) in siteConfig', () => { + test('website with useless field (wrong field) in siteConfig', async () => { const siteDir = path.join( __dirname, '__fixtures__', 'wrong-site', 'docusaurus.config.js', ); - expect(() => { - loadConfig(siteDir); - }).toThrowErrorMatchingSnapshot(); + await expect(loadConfig(siteDir)).rejects.toThrowErrorMatchingSnapshot(); }); - test('website with no siteConfig', () => { + test('website with no siteConfig', async () => { const siteDir = path.join( __dirname, '__fixtures__', 'nonExisting', 'docusaurus.config.js', ); - expect(() => { - loadConfig(siteDir); - }).toThrowError( + await expect(loadConfig(siteDir)).rejects.toThrowError( /Config file at "(.*?)__fixtures__[/\\]nonExisting[/\\]docusaurus.config.js" not found.$/, ); }); diff --git a/packages/docusaurus/src/server/config.ts b/packages/docusaurus/src/server/config.ts index 72d2200e4f17..b698e0d724fb 100644 --- a/packages/docusaurus/src/server/config.ts +++ b/packages/docusaurus/src/server/config.ts @@ -10,11 +10,23 @@ import importFresh from 'import-fresh'; import {DocusaurusConfig} from '@docusaurus/types'; import {validateConfig} from './configValidation'; -export default function loadConfig(configPath: string): DocusaurusConfig { +export default async function loadConfig( + configPath: string, +): Promise { if (!fs.existsSync(configPath)) { throw new Error(`Config file at "${configPath}" not found.`); } - const loadedConfig = importFresh(configPath) as Partial; + const importedConfig = importFresh(configPath) as + | Partial + | Promise> + | (() => Partial) + | (() => Promise>); + + const loadedConfig = + importedConfig instanceof Function + ? await importedConfig() + : await importedConfig; + return validateConfig(loadedConfig); } diff --git a/website/docs/api/docusaurus.config.js.md b/website/docs/api/docusaurus.config.js.md index dcff063ce392..ea5b73cd050f 100644 --- a/website/docs/api/docusaurus.config.js.md +++ b/website/docs/api/docusaurus.config.js.md @@ -11,6 +11,41 @@ slug: /api/docusaurus-config `docusaurus.config.js` contains configurations for your site and is placed in the root directory of your site. +It usually exports a site configuration object: + +```js title="docusaurus.config.js" +module.exports = { + // site config... +}; +``` + +
+Config files also support config creator functions and async code. + +```js title="docusaurus.config.js" +module.exports = function configCreator() { + return { + // site config... + }; +}; +``` + +```js title="docusaurus.config.js" +module.exports = async function configCreatorAsync() { + return { + // site config... + }; +}; +``` + +```js title="docusaurus.config.js" +module.exports = Promise.resolve({ + // site config... +}); +``` + +
+ ## Required fields {#required-fields} ### `title` {#title} diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index c1f4c6faba48..4c1279be271f 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -515,4 +515,12 @@ const config = { }), }; -module.exports = config; +// TODO temporary dogfood async config, remove soon +async function createConfig() { + await new Promise((resolve) => { + setTimeout(resolve, 0); + }); + return config; +} + +module.exports = createConfig;