├── .github
└── FUNDING.yml
├── .vscode
├── settings.json
├── extensions.json
└── cSpell.json
├── src
├── tsconfig.json
├── manifest
│ ├── tsconfig.json
│ └── index.ts
├── dev.d.ts
├── theme
│ ├── DocItem.tsx
│ ├── MDXPage.tsx
│ ├── BlogPostPage.tsx
│ └── useFrontMatter.tsx
└── types.d.ts
├── .gitignore
├── .editorconfig
├── .npmignore
├── CHANGELOG.md
├── LICENSE
├── tsconfig.json
├── package.json
└── README.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [roydukkey]
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.exclude": {
3 | "**/node_modules": true,
4 | "package-lock.json": true,
5 | "yarn.lock": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "rootDir": "./",
5 | },
6 | "exclude": [
7 | "manifest"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/src/manifest/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "files": [
4 | "./index.ts"
5 | ],
6 | "compilerOptions": {
7 | "module": "commonjs",
8 | "lib": [
9 | "ESNext"
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "editorconfig.editorconfig",
6 | "streetsidesoftware.code-spell-checker"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/src/dev.d.ts:
--------------------------------------------------------------------------------
1 | // ================================================================= //
2 | // Copyright (c) roydukkey. All rights reserved. //
3 | // ================================================================= //
4 |
5 |
6 | declare module '@theme-init/*';
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # User-specific files
2 | .DS_Store
3 | *.vs
4 | *.user
5 |
6 | # Package Managers
7 | node_modules
8 | npm-debug.log
9 |
10 | # Build Artifacts
11 | *.lock.json
12 | *-lock.json
13 | *.lock
14 | *.backup
15 | *.cache
16 | *.jest
17 | *.tgz
18 | dist
19 | package
20 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_size = 2
7 | indent_style = tab
8 | tab_width = 2
9 | trim_trailing_whitespace = true
10 | end_of_line = lf
11 | insert_final_newline = true
12 |
13 | [*.{md,mdx}]
14 | indent_style = space
15 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # User-specific files
2 | .DS_Store
3 | *.editorconfig
4 | *.env
5 | *.user
6 | *.vs
7 | *.vscode
8 | *.github
9 |
10 | # Package Managers
11 | node_modules
12 | npm-debug.log
13 |
14 | # Build Artifacts
15 | *.lock.json
16 | *-lock.json
17 | *.backup
18 | *.cache
19 | *.tgz
20 |
21 | # Source Code
22 | tsconfig*
23 |
--------------------------------------------------------------------------------
/.vscode/cSpell.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2",
3 | "ignorePaths": [
4 | "node_modules",
5 | ".git",
6 | ".jest",
7 | ".docusaurus",
8 | "**/.DS_Store",
9 | "*.lock.json",
10 | "*-lock.json",
11 | "*.lock",
12 | "*.tgz",
13 | "*.svg"
14 | ],
15 | "dictionaries": [
16 | "fullstack"
17 | ],
18 | // Before adding a word, see if there is already a dictionary in which it is contained.
19 | // http://github.com/streetsidesoftware/cspell-dicts
20 | "words": [
21 | "graymatter",
22 | "roydukkey",
23 | "streetsidesoftware",
24 | "subcomponents",
25 | "tsdoc"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/src/manifest/index.ts:
--------------------------------------------------------------------------------
1 | // ================================================================= //
2 | // Copyright (c) roydukkey. All rights reserved. //
3 | // ================================================================= //
4 |
5 | import type { Plugin } from '@docusaurus/types';
6 | import path from 'path';
7 |
8 |
9 | export default (): Plugin => ({
10 |
11 | name: 'docusaurus-theme-frontmatter',
12 |
13 | getThemePath (): string {
14 | return path.resolve(__dirname, './theme');
15 | },
16 |
17 | getTypeScriptThemePath (): string {
18 | return path.resolve(__dirname, '..', 'src', 'theme');
19 | }
20 |
21 | });
22 |
--------------------------------------------------------------------------------
/src/theme/DocItem.tsx:
--------------------------------------------------------------------------------
1 | // ================================================================= //
2 | // Copyright (c) roydukkey. All rights reserved. //
3 | // ================================================================= //
4 |
5 | import { Context } from '@theme/useFrontMatter';
6 | import DocItemInit from '@theme-init/DocItem';
7 | import type { Props } from '@theme/DocItem';
8 | import React from 'react';
9 |
10 |
11 | export default function DocItem (props: Props): JSX.Element {
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/theme/MDXPage.tsx:
--------------------------------------------------------------------------------
1 | // ================================================================= //
2 | // Copyright (c) roydukkey. All rights reserved. //
3 | // ================================================================= //
4 |
5 | import { Context } from '@theme/useFrontMatter';
6 | import MDXPageInit from '@theme-init/MDXPage';
7 | import type { Props } from '@theme/MDXPage';
8 | import React from 'react';
9 |
10 |
11 | export default function MDXPage (props: Props): JSX.Element {
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/theme/BlogPostPage.tsx:
--------------------------------------------------------------------------------
1 | // ================================================================= //
2 | // Copyright (c) roydukkey. All rights reserved. //
3 | // ================================================================= //
4 |
5 | import BlogPostPageInit from '@theme-init/BlogPostPage';
6 | import { Context } from '@theme/useFrontMatter';
7 | import type { Props } from '@theme/BlogPostPage';
8 | import React from 'react';
9 |
10 |
11 | export default function BlogPostPage (props: Props): JSX.Element {
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/theme/useFrontMatter.tsx:
--------------------------------------------------------------------------------
1 | // ================================================================= //
2 | // Copyright (c) roydukkey. All rights reserved. //
3 | // ================================================================= //
4 |
5 | import type { FrontMatter } from '@theme/useFrontMatter';
6 | import { createContext, useContext } from 'react';
7 |
8 |
9 | export const Context = createContext(null);
10 | Context.displayName = 'FrontMatterContext';
11 |
12 |
13 | export default function useFrontMatter (): T {
14 | const frontMatter = useContext(Context) as T | null;
15 |
16 | if (frontMatter === null) {
17 | throw new TypeError('No front matter context is available for `useFrontMatter()`.');
18 | }
19 |
20 | return frontMatter;
21 | }
22 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 |
4 |
5 |
6 | ## [1.3.0](https://github.com/roydukkey/docusaurus-theme-frontmatter/compare/v1.2.0...1.3.0)
7 |
8 | * Breaking: renamed `BlogPostItemFrontMatter` to `BlogPostPageFrontMatter`
9 | * Fix typing changes for [docusaurus v2.0.0-beta.22](https://github.com/facebook/docusaurus/releases/tag/v2.0.0-beta.22)
10 | * Moved FrontMatter context provider from `BlogPostItem` to `BlogPostPage`
11 |
12 | ## [1.2.0](https://github.com/roydukkey/docusaurus-theme-frontmatter/compare/v1.1.0...v1.2.0)
13 |
14 | * Fix typing changes for [docusaurus v2.0.0-beta.19](https://github.com/facebook/docusaurus/releases/tag/v2.0.0-beta.19)
15 |
16 | ## [1.1.0](https://github.com/roydukkey/docusaurus-theme-frontmatter/compare/v1.0.0...v1.1.0)
17 |
18 | * Improve debugging experience
19 |
20 | ## 1.0.0
21 |
22 | * Initial release!
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 roydukkey
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/types.d.ts:
--------------------------------------------------------------------------------
1 | // ================================================================= //
2 | // Copyright (c) roydukkey. All rights reserved. //
3 | // ================================================================= //
4 | /* eslint-disable @typescript-eslint/no-type-alias */
5 |
6 |
7 | declare module '@theme/useFrontMatter' {
8 |
9 | import type { Props as BlogPostPageProps } from '@theme/BlogPostPage';
10 | import type { Context } from 'react';
11 | import type { Props as DocItemProps } from '@theme/DocItem';
12 | import type { Props as MDXPageProps } from '@theme/MDXPage';
13 |
14 | /**
15 | * Returns the front matter for the current context.
16 | */
17 | export default function (): T;
18 |
19 | /**
20 | * The current front matter context.
21 | */
22 | export const Context: Context;
23 |
24 |
25 | export interface FrontMatter {
26 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
27 | readonly [key: string]: any;
28 | }
29 |
30 | export type DocItemFrontMatter = DocItemProps['content']['frontMatter'];
31 | export type BlogPostPageFrontMatter = BlogPostPageProps['content']['frontMatter'];
32 | export type MDXPageFrontMatter = MDXPageProps['content']['frontMatter'];
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./dist",
4 |
5 | "target": "ES2019",
6 | "module": "esnext",
7 | "lib": [
8 | "ESNext",
9 | "DOM"
10 | ],
11 | "jsx": "react",
12 | "sourceMap": false,
13 | "declaration": false,
14 | "declarationMap": false,
15 |
16 | "types": [
17 | "node",
18 | "@docusaurus/module-type-aliases",
19 | "@docusaurus/plugin-content-docs",
20 | "@docusaurus/plugin-content-pages",
21 | "@docusaurus/theme-classic"
22 | ],
23 |
24 | /* Strict Type-Checking Options */
25 | "strict": true,
26 | "strictNullChecks": true,
27 | "strictFunctionTypes": true,
28 | "strictBindCallApply": true,
29 | "strictPropertyInitialization": true,
30 | "noImplicitThis": true,
31 | "noImplicitAny": true, // Added
32 | "alwaysStrict": true,
33 |
34 | /* Additional Checks */
35 | "noUnusedLocals": false, // ensured by eslint, should not block compilation
36 | "noImplicitReturns": true,
37 | "noFallthroughCasesInSwitch": true,
38 |
39 | /* Disabled on purpose (handled by ESLint, should not block compilation) */
40 | "noUnusedParameters": true,
41 |
42 | /* Module Resolution Options */
43 | "moduleResolution": "node",
44 | "allowSyntheticDefaultImports": true,
45 | "esModuleInterop": true,
46 | "isolatedModules": true,
47 |
48 | /* Advanced Options */
49 | "resolveJsonModule": true,
50 | "skipLibCheck": true, // @types/webpack and webpack/types.d.ts are not the same thing
51 |
52 | /* Use tslib */
53 | "importHelpers": true,
54 | "noEmitHelpers": true
55 | },
56 | "exclude": [
57 | "node_modules",
58 | "dist"
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docusaurus-theme-frontmatter",
3 | "description": "Docusaurus theme plugin to expose front matter through a component hook",
4 | "version": "1.3.0",
5 | "author": {
6 | "name": "roydukkey",
7 | "email": "contact@changelog.me",
8 | "url": "http://changelog.me"
9 | },
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/roydukkey/docusaurus-theme-frontmatter.git"
14 | },
15 | "homepage": "https://github.com/roydukkey/docusaurus-theme-frontmatter#readme",
16 | "bugs": {
17 | "url": "https://github.com/roydukkey/docusaurus-theme-frontmatter/issues"
18 | },
19 | "keywords": [
20 | "docusaurus",
21 | "docusaurus-plugin",
22 | "front-matter",
23 | "frontmatter",
24 | "front",
25 | "matter",
26 | "gray-matter",
27 | "graymatter",
28 | "gray",
29 | "matter"
30 | ],
31 | "main": "./dist/index.js",
32 | "types": "./dist/types.d.ts",
33 | "engines": {
34 | "node": ">=14",
35 | "npm": "please-use-yarn",
36 | "yarn": ">=1.22.0"
37 | },
38 | "devDependencies": {
39 | "@docusaurus/module-type-aliases": "2.0.0-beta.22",
40 | "@docusaurus/theme-classic": "2.0.0-beta.22",
41 | "@roydukkey/eslint-config": "^1.0.10",
42 | "@typescript-eslint/eslint-plugin": "^5.9.0",
43 | "@typescript-eslint/parser": "^5.9.0",
44 | "clean-package": "^2.1.1",
45 | "cspell": "^5.15.1",
46 | "eslint": "^8.6.0",
47 | "eslint-plugin-tsdoc": "^0.2.14",
48 | "react": "^16.14.0",
49 | "react-dom": "^16.14.0",
50 | "tslib": "^2.3.1",
51 | "typescript": "^4.5.4",
52 | "webpack": "^5.65.0"
53 | },
54 | "peerDependencies": {
55 | "@docusaurus/plugin-content-docs": "^2.0.0-beta.22",
56 | "@docusaurus/plugin-content-pages": "^2.0.0-beta.22",
57 | "@docusaurus/theme-classic": "^2.0.0-beta.22"
58 | },
59 | "peerDependenciesMeta": {
60 | "@docusaurus/plugin-content-docs": {
61 | "optional": true
62 | },
63 | "@docusaurus/plugin-content-pages": {
64 | "optional": true
65 | },
66 | "@docusaurus/theme-classic": {
67 | "optional": true
68 | }
69 | },
70 | "scripts": {
71 | "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true })\"",
72 | "lint": "yarn lint:spelling && yarn lint:es",
73 | "lint:es": "eslint --ext js,jsx,ts,tsx ./",
74 | "lint:spelling": "cspell --config './.vscode/cSpell.json' --no-progress '**/{.*/**/,.*/**/.,,.}*'",
75 | "build": "yarn clean && yarn build:theme && yarn build:manifest && yarn build:types",
76 | "build:theme": "tsc --project ./src/tsconfig.json",
77 | "build:manifest": "tsc --project ./src/manifest/tsconfig.json",
78 | "build:types": "node -e \"fs.copyFileSync('./src/types.d.ts', './dist/types.d.ts')\"",
79 | "prepack": "yarn lint && yarn build && clean-package",
80 | "new:pack": "yarn pack && clean-package restore",
81 | "new:publish": "yarn publish && clean-package restore"
82 | },
83 | "clean-package": {
84 | "extends": "clean-package/common",
85 | "indent": "\t",
86 | "remove": [
87 | "scripts",
88 | "engines.npm",
89 | "engines.yarn"
90 | ]
91 | },
92 | "eslintConfig": {
93 | "extends": [
94 | "@roydukkey/eslint-config"
95 | ],
96 | "ignorePatterns": [
97 | "dist"
98 | ],
99 | "overrides": [
100 | {
101 | "files": [
102 | "*.{j,t}s?(x)"
103 | ],
104 | "rules": {
105 | "@typescript-eslint/naming-convention": "off"
106 | }
107 | }
108 | ]
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # docusaurus-theme-frontmatter
2 |
3 | This package enhances the Docusaurus classic theme by exposing the [docs](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-docs#markdown-front-matter), [blog](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog#markdown-front-matter), and [pages](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-pages) front matter to the following components and their children:
4 |
5 | * [BlogPostPage](https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-theme-classic/src/theme/BlogPostPage)
6 | * [DocItem](https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-theme-classic/src/theme/DocItem)
7 | * [MDXPage](https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-theme-classic/src/theme/MDXPage)
8 |
9 | Furthermore, this allows you to define and access ***custom*** front matter.
10 |
11 | [](https://www.npmjs.com/package/docusaurus-theme-frontmatter)
12 | [](https://opensource.org/licenses/MIT)
13 |
14 | ## Install
15 |
16 | ```sh
17 | yarn add docusaurus-theme-frontmatter
18 | ```
19 |
20 | Then, include the plugin in your `docusaurus.config.js` file.
21 |
22 | ```diff
23 | // docusaurus.config.js
24 | module.exports = {
25 | ...
26 | + themes: ['docusaurus-theme-frontmatter'],
27 | ...
28 | }
29 | ```
30 |
31 | ### TypeScript support
32 |
33 | To enable TypeScript support, the TypeScript configuration should be updated to add the `docusaurus-theme-frontmatter` type definitions. This can be done in the `tsconfig.json` file:
34 |
35 | ```diff
36 | {
37 | "extends": "@tsconfig/docusaurus/tsconfig.json",
38 | "compilerOptions": {
39 | ...
40 | + "types": ["docusaurus-theme-frontmatter"]
41 | }
42 | }
43 | ```
44 |
45 | ## How to use
46 |
47 | The `useFrontMatter()` hook is made available to any of your components through the `@theme/useFrontMatter` import. For example, you might have a component that creates a gallery of images.
48 |
49 | ```mdx
50 | ---
51 | title: Miniature fairy doors of NYC
52 | hide_table_of_contents: true
53 | gallery:
54 | - /images/117-first-avenue.jpg
55 | - /images/lower-east-side.jpg
56 | - /images/my-guinness.jpg
57 | - /images/hundred-years.jpg
58 | ---
59 | import Galley from '@theme/Galley';
60 |
61 |
62 | ```
63 |
64 | ```jsx
65 | import React from 'react';
66 | import ImageGallery from 'react-image-gallery';
67 | import useFrontMatter from '@theme/useFrontMatter';
68 |
69 | export default function GalleyComponent () {
70 | const { gallery } = useFrontMatter();
71 |
72 | if (Array.isArray(gallery)) {
73 | const images = gallery.map((image) => ({
74 | original: image
75 | }));
76 |
77 | return ;
78 | }
79 |
80 | return null;
81 | }
82 | ```
83 |
84 | ## Public API
85 |
86 | ### `useFrontMatter()`
87 |
88 | Returns the front matter for the current context.
89 |
90 | ```ts
91 | import useFrontMatter from '@theme/useFrontMatter';
92 |
93 | interface CustomFrontMatter {
94 | gallery?: string[];
95 | }
96 |
97 | const MyComponent = () => {
98 | const { gallery } = useFrontMatter();
99 | return (<... />);
100 | }
101 | ```
102 |
103 | ### `Context`
104 |
105 | The current front matter context.
106 |
107 | Generally, this is something to be left alone and operates behind the scenes. This is how it is used to [enhance DocItem](https://github.com/roydukkey/docusaurus-theme-frontmatter/blob/master/src/theme/DocItem.tsx) scaffolding the hook:
108 |
109 | ```js
110 | import { Context } from '@theme/useFrontMatter';
111 | import DocItem from '@theme-init/DocItem';
112 | import React from 'react';
113 |
114 | export default (props) =>
115 |
116 | ;
117 | ```
118 |
119 | ### `FrontMatter`, `DocItemFrontMatter`, `BlogPostPageFrontMatter`, `MDXPageFrontMatter`
120 |
121 | These types are provided to assist in describing the values returned by the `useFrontMatter()` hook.
122 |
123 | ```ts
124 | import useFrontMatter from '@theme/useFrontMatter';
125 | import type { DocItemFrontMatter } from '@theme/useFrontMatter';
126 |
127 | const MyComponent = () => {
128 | const { id, title, keywords } = useFrontMatter();
129 | return (<... />);
130 | }
131 | ```
132 |
133 | ## Project Longevity
134 |
135 | This project was originally created to provide a useful feature that was lacking in Docusaurus v2. Since the release of this plugin, the Docusaurus team has began a [plan to expose FrontMatter](https://github.com/facebook/docusaurus/issues/6885) and other data through hooks. So long their resulting work provides access to **custom** front matter, this project is likely to deprecate. However until that day comes, I will do my best to keep this project up-to-date with upstream changes.
136 |
137 | Here are some issues to review if you want to see where all this is headed:
138 |
139 | * [RFC: allow routes to declare a React context](https://github.com/facebook/docusaurus/issues/6885)
140 | * [refactor(docs,theme): split DocItem comp, useDoc hook](https://github.com/facebook/docusaurus/pull/7644)
141 | * [refactor(theme): split BlogPostItem into smaller theme subcomponents](https://github.com/facebook/docusaurus/pull/7716)
142 |
--------------------------------------------------------------------------------