Repository: graphql-markdown/graphql-markdown
Pull request #1022 (2023-09-30 12:27:17)

✨  add page metadata support
# Description

Add page metadata support (#1015) through a new option `metatags`.

```js
plugins: [
    [
      "@graphql-markdown/docusaurus",
       {
        // ...
        metatags: [
          { name: "robots", content: "noindex" }, // <meta name="robots" content="noindex" />
          { charset: "utf-8" }, // <meta charset="utf-8" />
        ],
      },
    ],
  ],
```


# Checklist

- [x] My changes follow the [contributing guidelines](https://github.com/graphql-markdown/graphql-markdown/blob/main/CONTRIBUTING.md) of this project.
- [x] I have performed a self-review of my code.
- [x] I have made corresponding changes to the documentation.
- [x] I have added tests that prove my fix is effective or that my changes work.
- [x] New and existing unit tests pass locally with my changes.


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=graphql-markdown_graphql-markdown&pullRequest=1022)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=graphql-markdown_graphql-markdown&pullRequest=1022) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=graphql-markdown_graphql-markdown&pullRequest=1022&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=graphql-markdown_graphql-markdown&pullRequest=1022&metric=new_duplicated_lines_density&view=list)


codesee-maps[bot]: <details open>
<summary><b>👇 <a href="https://app.codesee.io/r/reviews?pr=1022&src=https%3A%2F%2Fgithub.com%2Fgraphql-markdown%2Fgraphql-markdown">Click on the image</a> for a new way to code review</b>
</summary>

####

[![Review these changes using an interactive CodeSee Map](https://s3.us-east-2.amazonaws.com/maps.codesee.io/images/github/graphql-markdown/graphql-markdown/1022/7fb28d70/81f0c0f5d8973cbf5c3ea03a2b2b19c6933309b9.svg)](https://app.codesee.io/r/reviews?pr=1022&src=https%3A%2F%2Fgithub.com%2Fgraphql-markdown%2Fgraphql-markdown)

#### Legend
<img src="https://app.codesee.io/Legend_v3.png" alt="CodeSee Map legend" width="220" height="156">
</details>
coveralls: 
[![Coverage Status](https://coveralls.io/builds/63002426/badge)](https://coveralls.io/builds/63002426)

coverage: 93.953% (-0.008%) from 93.961% when pulling **81f0c0f5d8973cbf5c3ea03a2b2b19c6933309b9 on feat/1015-add-metadata** into **701169e032a866864d03751a8bf3b4ddd0c555fe on main**.

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=graphql-markdown_graphql-markdown&pullRequest=1022)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=graphql-markdown_graphql-markdown&pullRequest=1022) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=graphql-markdown_graphql-markdown&pullRequest=1022&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=graphql-markdown_graphql-markdown&pullRequest=1022&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=graphql-markdown_graphql-markdown&pullRequest=1022)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=graphql-markdown_graphql-markdown&pullRequest=1022&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=graphql-markdown_graphql-markdown&pullRequest=1022) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=graphql-markdown_graphql-markdown&pullRequest=1022&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=graphql-markdown_graphql-markdown&pullRequest=1022&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index e96648fb..5e124ff0 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -45,6 +45,7 @@
     "melvey",
     "memfs",
     "Metafile",
+    "metatags",
     "MGLT",
     "mimo84",
     "mockfs",
diff --git a/docs/settings.md b/docs/settings.md
index a98b6eba..949f6fba 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -129,6 +129,41 @@ GraphQL schema loaders to use (see [schema loading](/docs/advanced/schema-loadin
 | --------- | --------------- | ------- |
 | `loaders` | _not supported_ | `{ }`   |
 
+## `metatags`
+
+Set page metadata  in `<html>`, `<head>` using [Docusaurus head metadata](https://docusaurus.io/docs/markdown-features/head-metadata).
+
+Meta tags are provided as list of metadata objects, eg `[{ name: "robots", content: "noindex" }]` for `<meta name="robots" content="noindex" />`.
+
+| Setting   | CLI flag        | Default |
+| --------- | --------------- | ------- |
+| `metatags` | _not supported_ | `[]`   |
+
+<br/>
+
+```js title="docusaurus.config.js"
+plugins: [
+    [
+      "@graphql-markdown/docusaurus",
+       {
+        schema: "./schema/swapi.graphql",
+        rootPath: "./docs",
+        baseURL: "swapi",
+        homepage: "./docs/swapi.md",
+        // highlight-start
+        metatags: [
+          { name: "robots", content: "noindex" }, // <meta name="robots" content="noindex" />
+          { charset: "utf-8" }, // <meta charset="utf-8" />
+        ],
+        // highlight-end
+        loaders: {
+          GraphQLFileLoader: "@graphql-tools/graphql-file-loader" // local file schema
+        }
+      },
+    ],
+  ],
+```
+
 ## `onlyDocDirective`
 
 The schema directive/s used for selecting types to be rendered in the documentation.
diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts
index 2d2facb4..788ffb9c 100644
--- a/packages/core/src/config.ts
+++ b/packages/core/src/config.ts
@@ -40,11 +40,15 @@ export const ASSET_HOMEPAGE_LOCATION = join(
 );
 
 export const DEFAULT_OPTIONS: Required<
-  Omit<ConfigOptions, "customDirective" | "groupByDirective" | "loaders">
+  Omit<
+    ConfigOptions,
+    "customDirective" | "groupByDirective" | "loaders" | "metatags"
+  >
 > & {
-  groupByDirective: Maybe<GroupByDirectiveOptions>;
   customDirective: Maybe<CustomDirective>;
+  groupByDirective: Maybe<GroupByDirectiveOptions>;
   loaders: Maybe<LoaderOption>;
+  metatags: Record<string, string>[];
 } = {
   id: "default",
   baseURL: "schema",
@@ -59,6 +63,7 @@ export const DEFAULT_OPTIONS: Required<
   homepage: ASSET_HOMEPAGE_LOCATION,
   linkRoot: "/",
   loaders: undefined,
+  metatags: [] as Record<string, string>[],
   pretty: false,
   printer: "@graphql-markdown/printer-legacy" as PackageName,
   printTypeOptions: {
@@ -310,6 +315,7 @@ export const buildConfig = async (
     id: id ?? DEFAULT_OPTIONS.id,
     linkRoot: cliOpts.link ?? config.linkRoot ?? DEFAULT_OPTIONS.linkRoot,
     loaders: config.loaders,
+    metatags: config.metatags ?? DEFAULT_OPTIONS.metatags,
     onlyDocDirective,
     outputDir: join(cliOpts.root ?? config.rootPath, baseURL),
     prettify: cliOpts.pretty ?? config.pretty ?? DEFAULT_OPTIONS.pretty,
diff --git a/packages/core/src/generator.ts b/packages/core/src/generator.ts
index a75904ac..eaeb0c02 100644
--- a/packages/core/src/generator.ts
+++ b/packages/core/src/generator.ts
@@ -35,6 +35,7 @@ export const generateDocFromSchema = async ({
   linkRoot,
   loaders: loadersList,
   loggerModule,
+  metatags,
   onlyDocDirective,
   outputDir,
   prettify,
@@ -105,6 +106,7 @@ export const generateDocFromSchema = async ({
       onlyDocDirectives,
       printTypeOptions,
       skipDocDirectives,
+      metatags,
     },
   );
   const renderer = new Renderer(printer, outputDir, baseURL, groups, prettify, {
diff --git a/packages/core/src/printer.ts b/packages/core/src/printer.ts
index 0571b020..a1ea3861 100644
--- a/packages/core/src/printer.ts
+++ b/packages/core/src/printer.ts
@@ -13,13 +13,11 @@ export const getPrinter = async (
   options?: Maybe<PrinterOptions>,
 ): Promise<Printer> => {
   if (typeof printerModule !== "string") {
-    throw new Error(
-      'Invalid printer module name in "printTypeOptions" settings.',
-    );
+    throw new Error("Invalid printer module name.");
   }
 
   if (!config) {
-    throw new Error('Invalid printer config in "printTypeOptions" settings.');
+    throw new Error("Invalid printer config.");
   }
 
   try {
@@ -32,8 +30,6 @@ export const getPrinter = async (
 
     return Printer;
   } catch (error: unknown) {
-    throw new Error(
-      `Cannot find module '${printerModule}' defined in "printTypeOptions" settings.`,
-    );
+    throw new Error(`Cannot find module '${printerModule}'.`);
   }
 };
diff --git a/packages/printer-legacy/src/const/options.ts b/packages/printer-legacy/src/const/options.ts
index 7f5c7d99..c4e8e81a 100644
--- a/packages/printer-legacy/src/const/options.ts
+++ b/packages/printer-legacy/src/const/options.ts
@@ -21,6 +21,7 @@ export const PRINT_TYPE_DEFAULT_OPTIONS: Required<PrinterConfigPrintTypeOptions>
   {
     codeSection: true,
     deprecated: "default",
+    metatags: [],
     parentTypePrefix: true,
     relatedTypeSection: true,
     typeBadges: true,
@@ -53,6 +54,7 @@ export const DEFAULT_OPTIONS: Required<
   groups: undefined,
   header: { toc: true, pagination: true } as Required<PrintTypeHeaderOptions>,
   level: undefined,
+  metatags: [],
   onlyDocDirectives: [],
   parentType: undefined,
   schema: undefined,
diff --git a/packages/printer-legacy/src/printer.ts b/packages/printer-legacy/src/printer.ts
index 4cda3116..a2e838b5 100644
--- a/packages/printer-legacy/src/printer.ts
+++ b/packages/printer-legacy/src/printer.ts
@@ -83,6 +83,7 @@ export class Printer implements IPrinter {
       onlyDocDirectives,
       printTypeOptions,
       skipDocDirectives,
+      metatags,
     }: {
       customDirectives?: CustomDirectiveMap;
       deprecated?: TypeDeprecatedOption;
@@ -90,12 +91,14 @@ export class Printer implements IPrinter {
       onlyDocDirectives?: GraphQLDirective[];
       printTypeOptions?: PrinterConfigPrintTypeOptions;
       skipDocDirectives?: GraphQLDirective[];
+      metatags?: Record<string, string>[];
     } = {
       customDirectives: undefined,
       groups: undefined,
       onlyDocDirectives: [],
       printTypeOptions: PRINT_TYPE_DEFAULT_OPTIONS,
       skipDocDirectives: [],
+      metatags: [],
     },
   ): void {
     if (typeof Printer.options !== "undefined") {
@@ -122,6 +125,7 @@ export class Printer implements IPrinter {
       skipDocDirectives: skipDocDirectives ?? [],
       typeBadges:
         printTypeOptions?.typeBadges ?? PRINT_TYPE_DEFAULT_OPTIONS.typeBadges,
+      metatags: metatags ?? [],
     };
   }
 
@@ -228,6 +232,25 @@ export class Printer implements IPrinter {
     return printRelations(type, options);
   };
 
+  static printMetaTags = (
+    _type: unknown,
+    { metatags }: PrintTypeOptions,
+  ): MDXString | string => {
+    if (!metatags || metatags.length < 1) {
+      return "";
+    }
+
+    const meta = metatags.map((tag) => {
+      const props = Object.entries(tag).map(([name, value]) => {
+        return `${name}="${value}"`;
+      });
+
+      return `<meta ${props.join(" ")} />`;
+    });
+
+    return ["<head>", ...meta, "</head>"].join(MARKDOWN_EOL);
+  };
+
   static printType = (
     name: Maybe<string>,
     type: unknown,
@@ -248,6 +271,7 @@ export class Printer implements IPrinter {
       getTypeName(type),
       printTypeOptions,
     );
+    const metatags = Printer.printMetaTags(type, printTypeOptions);
     const description = Printer.printDescription(type, printTypeOptions);
     const code = Printer.printCode(type, printTypeOptions);
     const customDirectives = Printer.printCustomDirectives(
@@ -260,6 +284,7 @@ export class Printer implements IPrinter {
 
     return [
       header,
+      metatags,
       mdx,
       tags,
       description,
diff --git a/packages/types/src/core.d.ts b/packages/types/src/core.d.ts
index 4175e116..7c940c12 100644
--- a/packages/types/src/core.d.ts
+++ b/packages/types/src/core.d.ts
@@ -35,6 +35,7 @@ export interface ConfigOptions {
   id?: Maybe<string>;
   linkRoot?: Maybe<string>;
   loaders?: Maybe<LoaderOption>;
+  metatags?: Record<string, string>[];
   pretty?: Maybe<boolean>;
   printer?: Maybe<PackageName>;
   printTypeOptions?: Maybe<ConfigPrintTypeOptions>;
diff --git a/packages/types/src/printer.d.ts b/packages/types/src/printer.d.ts
index f67e5e79..2da2aa87 100644
--- a/packages/types/src/printer.d.ts
+++ b/packages/types/src/printer.d.ts
@@ -28,6 +28,7 @@ export type RootTypeLocale = {
 export interface PrinterConfigPrintTypeOptions {
   codeSection?: boolean;
   deprecated?: TypeDeprecatedOption;
+  metatags?: Record<string, string>[];
   parentTypePrefix?: boolean;
   relatedTypeSection?: boolean;
   typeBadges?: boolean;
@@ -52,6 +53,7 @@ export interface PrintTypeOptions {
   groups?: Maybe<SchemaEntitiesGroupMap>;
   header?: Maybe<PrintTypeHeaderOptions>;
   level?: Maybe<SectionLevelValue>;
+  metatags?: Maybe<Record<string, string>[]>;
   onlyDocDirectives?: GraphQLDirective[];
   parentType?: Maybe<string>;
   parentTypePrefix?: boolean;
@@ -149,6 +151,7 @@ export interface PrinterOptions {
   customDirectives?: Maybe<CustomDirectiveMap>;
   deprecated?: Maybe<TypeDeprecatedOption>;
   groups?: Maybe<SchemaEntitiesGroupMap>;
+  metatags?: Maybe<Record<string, string>[]>;
   onlyDocDirectives?: GraphQLDirective[];
   printTypeOptions?: Maybe<ConfigPrintTypeOptions>;
   skipDocDirectives?: GraphQLDirective[];


Test patch:
diff --git a/packages/core/tests/unit/config.test.ts b/packages/core/tests/unit/config.test.ts
index 521461c1..0ecc5297 100644
--- a/packages/core/tests/unit/config.test.ts
+++ b/packages/core/tests/unit/config.test.ts
@@ -181,6 +181,7 @@ describe("config", () => {
           id: DEFAULT_OPTIONS.id,
           linkRoot: DEFAULT_OPTIONS.linkRoot,
           loaders: DEFAULT_OPTIONS.loaders,
+          metatags: DEFAULT_OPTIONS.metatags,
           outputDir: join(DEFAULT_OPTIONS.rootPath, DEFAULT_OPTIONS.baseURL),
           onlyDocDirective: DEFAULT_OPTIONS.onlyDocDirective,
           prettify: DEFAULT_OPTIONS.pretty,
@@ -251,6 +252,7 @@ describe("config", () => {
         id: DEFAULT_OPTIONS.id,
         linkRoot: configFileOpts.linkRoot,
         loaders: configFileOpts.loaders,
+        metatags: DEFAULT_OPTIONS.metatags,
         outputDir: join(configFileOpts.rootPath!, configFileOpts.baseURL!),
         onlyDocDirective: DEFAULT_OPTIONS.onlyDocDirective,
         prettify: configFileOpts.pretty,
@@ -322,6 +324,7 @@ describe("config", () => {
         homepageLocation: cliOpts.homepage,
         linkRoot: cliOpts.link,
         loaders: configFileOpts.loaders,
+        metatags: DEFAULT_OPTIONS.metatags,
         outputDir: join(cliOpts.root!, cliOpts.base!),
         prettify: cliOpts.pretty,
         schemaLocation: cliOpts.schema,
@@ -368,6 +371,7 @@ describe("config", () => {
         id: DEFAULT_OPTIONS.id,
         linkRoot: DEFAULT_OPTIONS.linkRoot,
         loaders: configFileOpts.loaders,
+        metatags: DEFAULT_OPTIONS.metatags,
         onlyDocDirective: DEFAULT_OPTIONS.onlyDocDirective,
         outputDir: join(DEFAULT_OPTIONS.rootPath, configFileOpts.baseURL!),
         prettify: cliOpts.pretty,
@@ -396,6 +400,7 @@ describe("config", () => {
         id: DEFAULT_OPTIONS.id,
         linkRoot: DEFAULT_OPTIONS.linkRoot,
         loaders: {},
+        metatags: DEFAULT_OPTIONS.metatags,
         onlyDocDirective: DEFAULT_OPTIONS.onlyDocDirective,
         outputDir: join(DEFAULT_OPTIONS.rootPath, DEFAULT_OPTIONS.baseURL),
         prettify: DEFAULT_OPTIONS.pretty,
diff --git a/packages/core/tests/unit/printer.test.ts b/packages/core/tests/unit/printer.test.ts
index 792951d5..0e0a0978 100644
--- a/packages/core/tests/unit/printer.test.ts
+++ b/packages/core/tests/unit/printer.test.ts
@@ -58,9 +58,7 @@ describe("generator", () => {
               printTypeOptions: {},
             },
           ),
-        ).rejects.toThrow(
-          `Invalid printer module name in "printTypeOptions" settings.`,
-        );
+        ).rejects.toThrow("Invalid printer module name.");
       },
     );
 
@@ -76,9 +74,7 @@ describe("generator", () => {
             printTypeOptions: {},
           },
         ),
-      ).rejects.toThrow(
-        `Invalid printer config in "printTypeOptions" settings.`,
-      );
+      ).rejects.toThrow("Invalid printer config.");
     });
 
     test("throws exception if no printer module not found", async () => {
@@ -97,9 +93,7 @@ describe("generator", () => {
             printTypeOptions: {},
           },
         ),
-      ).rejects.toThrow(
-        `Cannot find module 'foobar' defined in "printTypeOptions" settings.`,
-      );
+      ).rejects.toThrow("Cannot find module 'foobar'.");
     });
   });
 });
diff --git a/packages/docusaurus/tests/__data__/.graphqlrc b/packages/docusaurus/tests/__data__/.graphqlrc
index b8eaa9f1..09ef15f3 100644
--- a/packages/docusaurus/tests/__data__/.graphqlrc
+++ b/packages/docusaurus/tests/__data__/.graphqlrc
@@ -16,4 +16,8 @@ projects:
           typeBadges: true
           deprecated: group
         skipDocDirective:
-          - "@noDoc"
+        - "@noDoc"
+        metatags:
+        - name: googlebot
+          content: noindex
+        - charset: utf-8
diff --git a/packages/docusaurus/tests/__data__/docusaurus2-graphql-doc-generator.config.js b/packages/docusaurus/tests/__data__/docusaurus2-graphql-doc-generator.config.js
index 5b284bc2..4b74491f 100644
--- a/packages/docusaurus/tests/__data__/docusaurus2-graphql-doc-generator.config.js
+++ b/packages/docusaurus/tests/__data__/docusaurus2-graphql-doc-generator.config.js
@@ -11,4 +11,5 @@ module.exports = {
       options: { method: "POST" },
     },
   },
+  metatags: [{ name: "googlebot", content: "noindex" }, { charset: "utf-8" }],
 };
diff --git a/packages/printer-legacy/tests/unit/printer.test.ts b/packages/printer-legacy/tests/unit/printer.test.ts
index b61171d2..1d11d09c 100644
--- a/packages/printer-legacy/tests/unit/printer.test.ts
+++ b/packages/printer-legacy/tests/unit/printer.test.ts
@@ -176,6 +176,7 @@ describe("Printer", () => {
             "toc": true,
           },
           "level": undefined,
+          "metatags": [],
           "onlyDocDirectives": [],
           "parentType": undefined,
           "parentTypePrefix": true,
@@ -232,6 +233,7 @@ describe("Printer", () => {
             "toc": true,
           },
           "level": undefined,
+          "metatags": [],
           "onlyDocDirectives": [],
           "parentType": undefined,
           "parentTypePrefix": false,
@@ -459,4 +461,32 @@ describe("Printer", () => {
       expect(printedType).toBeUndefined();
     });
   });
+
+  describe("printMetaTags", () => {
+    test.each([
+      { options: {} },
+      { options: { metatags: undefined } },
+      { options: { metatags: [] } },
+    ])("return empty string if no metatags set", ({ options }) => {
+      expect.assertions(1);
+
+      expect(Printer.printMetaTags({}, options as PrintTypeOptions)).toBe("");
+    });
+
+    test("returns a formatted head tag with meta tags", () => {
+      expect.assertions(1);
+
+      const metatags = [
+        { charSet: "utf-8" },
+        { name: "robot", contents: "none" },
+      ];
+
+      expect(
+        Printer.printMetaTags({}, { metatags } as unknown as PrintTypeOptions),
+      ).toBe(`<head>
+<meta charSet="utf-8" />
+<meta name="robot" contents="none" />
+</head>`);
+    });
+  });
 });