Skip to content

Commit

Permalink
feat(core): siteConfig.headTags API to render extra tags in document …
Browse files Browse the repository at this point in the history
…head (#8151)

Co-authored-by: Sébastien Lorber <slorber@users.noreply.github.com>
  • Loading branch information
johnnyreilly and slorber committed Oct 28, 2022
1 parent 824a0f3 commit 38e65f4
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 2 deletions.
9 changes: 8 additions & 1 deletion packages/docusaurus-types/src/config.d.ts
Expand Up @@ -8,7 +8,7 @@
import type {RuleSetRule} from 'webpack';
import type {Required as RequireKeys, DeepPartial} from 'utility-types';
import type {I18nConfig} from './i18n';
import type {PluginConfig, PresetConfig} from './plugin';
import type {PluginConfig, PresetConfig, HtmlTagObject} from './plugin';

export type ReportingSeverity = 'ignore' | 'log' | 'warn' | 'throw';

Expand Down Expand Up @@ -192,6 +192,13 @@ export type DocusaurusConfig = {
* @default ["static"]
*/
staticDirectories: string[];
/**
* An array of tags that will be inserted in the HTML `<head>`.
*
* @see https://docusaurus.io/docs/api/docusaurus-config#head
* @default []
*/
headTags: HtmlTagObject[];
/**
* An array of scripts to load. The values can be either strings or plain
* objects of attribute-value maps. The `<script>` tags will be inserted in
Expand Down
Expand Up @@ -7,6 +7,7 @@ exports[`loadSiteConfig website with .cjs siteConfig 1`] = `
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
Expand Down Expand Up @@ -44,6 +45,7 @@ exports[`loadSiteConfig website with valid async config 1`] = `
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
Expand Down Expand Up @@ -83,6 +85,7 @@ exports[`loadSiteConfig website with valid async config creator function 1`] = `
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
Expand Down Expand Up @@ -122,6 +125,7 @@ exports[`loadSiteConfig website with valid config creator function 1`] = `
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
Expand Down Expand Up @@ -164,6 +168,7 @@ exports[`loadSiteConfig website with valid siteConfig 1`] = `
],
"customFields": {},
"favicon": "img/docusaurus.ico",
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
Expand Down
Expand Up @@ -71,6 +71,7 @@ exports[`load loads props for site with custom i18n path 1`] = `
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {
Expand Down
68 changes: 68 additions & 0 deletions packages/docusaurus/src/server/__tests__/configValidation.test.ts
Expand Up @@ -351,6 +351,74 @@ describe('normalizeConfig', () => {
`);
});

it('accepts headTags with tagName and attributes', () => {
expect(() => {
normalizeConfig({
headTags: [
{
tagName: 'link',
attributes: {
rel: 'icon',
href: 'img/docusaurus.png',
},
},
],
});
}).not.toThrow();
});

it("throws error if headTags doesn't have tagName", () => {
expect(() => {
normalizeConfig({
headTags: [
{
attributes: {
rel: 'icon',
href: 'img/docusaurus.png',
},
},
],
});
}).toThrowErrorMatchingInlineSnapshot(`
""headTags[0].tagName" is required
"
`);
});

it("throws error if headTags doesn't have attributes", () => {
expect(() => {
normalizeConfig({
headTags: [
{
tagName: 'link',
},
],
});
}).toThrowErrorMatchingInlineSnapshot(`
""headTags[0].attributes" is required
"
`);
});

it("throws error if headTags doesn't have string attributes", () => {
expect(() => {
normalizeConfig({
headTags: [
{
tagName: 'link',
attributes: {
rel: false,
href: 'img/docusaurus.png',
},
},
],
});
}).toThrowErrorMatchingInlineSnapshot(`
""headTags[0].attributes.rel" must be a string
"
`);
});

it("throws error if css doesn't have href", () => {
expect(() => {
normalizeConfig({
Expand Down
16 changes: 16 additions & 0 deletions packages/docusaurus/src/server/configValidation.ts
Expand Up @@ -33,6 +33,7 @@ export const DEFAULT_CONFIG: Pick<
| 'plugins'
| 'themes'
| 'presets'
| 'headTags'
| 'stylesheets'
| 'scripts'
| 'clientModules'
Expand All @@ -51,6 +52,7 @@ export const DEFAULT_CONFIG: Pick<
plugins: [],
themes: [],
presets: [],
headTags: [],
stylesheets: [],
scripts: [],
clientModules: [],
Expand Down Expand Up @@ -222,6 +224,20 @@ export const ConfigSchema = Joi.object<DocusaurusConfig>({
})
.default(DEFAULT_CONFIG.scripts),
ssrTemplate: Joi.string(),
headTags: Joi.array()
.items(
Joi.object({
tagName: Joi.string().required(),
attributes: Joi.object()
.pattern(/[\w-]+/, Joi.string())
.required(),
}).unknown(),
)
.messages({
'array.includes':
'{#label} is invalid. A headTag must be an object with at least a "tagName" and an "attributes" property.',
})
.default(DEFAULT_CONFIG.headTags),
stylesheets: Joi.array()
.items(
Joi.string(),
Expand Down
3 changes: 2 additions & 1 deletion packages/docusaurus/src/server/plugins/synthetic.ts
Expand Up @@ -21,6 +21,7 @@ export function createBootstrapPlugin({
const {
stylesheets,
scripts,
headTags,
clientModules: siteConfigClientModules,
} = siteConfig;
return {
Expand Down Expand Up @@ -58,7 +59,7 @@ export function createBootstrapPlugin({
},
);
return {
headTags: [...stylesheetsTags, ...scriptsTags],
headTags: [...headTags, ...stylesheetsTags, ...scriptsTags],
};
},
};
Expand Down
24 changes: 24 additions & 0 deletions website/docs/api/docusaurus.config.js.md
Expand Up @@ -429,6 +429,30 @@ module.exports = {
};
```

### `headTags` {#headTags}

An array of tags that will be inserted in the HTML `<head>`. The values must be objects that contain two properties; `tagName` and `attributes`. `tagName` must be a string that determines the tag being created; eg `"link"`. `attributes` must be an attribute-value map.

- Type: `{ tagName: string; attributes: Object; }[]`

Example:

```js title="docusaurus.config.js"
module.exports = {
headTags: [
{
tagName: 'link',
attributes: {
rel: 'icon',
href: '/img/docusaurus.png',
},
},
],
};
```

This would become `<link rel="icon" href="img/docusaurus.png" />` in the generated HTML.

### `scripts` {#scripts}

An array of scripts to load. The values can be either strings or plain objects of attribute-value maps. The `<script>` tags will be inserted in the HTML `<head>`. If you use a plain object, the only required attribute is `src`, and any other attributes are permitted (each one should have boolean/string values).
Expand Down

0 comments on commit 38e65f4

Please sign in to comment.