-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add FragmentPlugin that enables the embedding of pages into pages using the syntax :fragment{src="path-to-fragment"}
- Loading branch information
Showing
69 changed files
with
781 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
--- | ||
title: Fragments | ||
layout: DetailTechnical | ||
sidebar: | ||
priority: 2 | ||
--- | ||
|
||
# {meta.title} | ||
|
||
Fragments, also known as content fragments, are powerful tools that allow you to incorporate content from other pages into your documentation. By creating an MDX file and using the [generic directives](https://talk.commonmark.org/t/generic-directives-plugins-syntax/444) syntax `:fragment{src="path-to-fragment"}`, you can easily render the fragment in another file, providing modularity and reusability to your content. | ||
|
||
## Use Cases | ||
|
||
Fragments offer various use cases, such as: | ||
|
||
**Consistent Content**: Use fragments to maintain consistent content across multiple pages. For instance, if you have a table or a tile that appears on multiple pages, you can create a fragment for it and include it in all relevant files. This ensures that any updates made to the fragment automatically reflect across the entire documentation. | ||
|
||
**Reusable Components**: Fragments enable the creation of reusable components or sections. You can define a complex or commonly used section once and then include it in multiple pages as needed. This approach saves time and effort, as you only need to update the fragment file to propagate changes throughout your documentation. | ||
|
||
**Modular Documentation**: With fragments, you can break down your documentation into smaller, manageable pieces. Each fragment represents a specific topic or section, allowing you to organize and structure your content more efficiently. This modular approach simplifies maintenance and makes it easier to navigate and update your documentation. | ||
|
||
## Usage | ||
|
||
Firstly, enable the Fragment Plugin by adding the following to your plugins in `mosaic.config.js`. | ||
|
||
``` | ||
{ | ||
modulePath: '@jpmorganchase/mosaic-plugins/FragmentPlugin', | ||
options: {} | ||
} | ||
``` | ||
|
||
To include a fragment in your content, follow these steps: | ||
|
||
Create an MDX file for the fragment you want to reuse. Remember to set the sidebar property of your fragment's frontmatter to exclude: true if you don't want the fragment to appear in the vertical navigation menu. | ||
|
||
``` | ||
--- | ||
title: Fragment Title | ||
sidebar: | ||
exclude: true | ||
--- | ||
``` | ||
|
||
In the target file where you want to include the fragment, use the remark directive syntax `:fragment{src="path-to-fragment"}`. | ||
|
||
### Markdown Content Example | ||
|
||
This is the contents of a fragment located at `../fragments/content-fragment.mdx`: | ||
|
||
``` | ||
--- | ||
title: Content Fragment | ||
sidebar: | ||
exclude: true | ||
--- | ||
#### Fragment Title | ||
This is an example fragment of markdown content, being pulled from `../fragments/content-fragment.mdx`. | ||
``` | ||
|
||
The below code snippet will render the content from the content-fragment.mdx file in your target file: | ||
|
||
``` | ||
:fragment{src="../fragments/content-fragment.mdx"} | ||
``` | ||
|
||
Example output: | ||
|
||
:fragment{src="../fragments/content-fragment.mdx"} | ||
|
||
### Component Example | ||
|
||
Here is another example, where the fragment files each contain a `<Tile>` component. | ||
|
||
``` | ||
:fragment{src="../fragments/tile-a.mdx"} :fragment{src="../fragments/tile-b.mdx"} | ||
``` | ||
|
||
The above code will render the content from tile-a.mdx and tile-b.mdx files, demonstrated below: | ||
|
||
:fragment{src="../fragments/tile-a.mdx"} :fragment{src="../fragments/tile-b.mdx"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
--- | ||
title: Content Fragment | ||
sidebar: | ||
exclude: true | ||
--- | ||
|
||
#### Fragment Title | ||
|
||
This is an example fragment of markdown content, being pulled from `../fragments/content-fragment.mdx`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
title: Fragments | ||
layout: DetailTechnical | ||
--- | ||
|
||
# {meta.title} | ||
|
||
This folder contains example fragments that are referenced and rendered in the Fragments docs page. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
title: Tile A | ||
sidebar: | ||
exclude: true | ||
--- | ||
|
||
<TileContent title="Tile A" description="Tile A description" eyebrow="Eyebrow" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
title: Tile B | ||
sidebar: | ||
exclude: true | ||
--- | ||
|
||
<TileContent title="Tile B" description="Tile B description" eyebrow="Eyebrow" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import path from 'path'; | ||
import type { Plugin as PluginType } from '@jpmorganchase/mosaic-types'; | ||
import { escapeRegExp } from 'lodash-es'; | ||
import { remark } from 'remark'; | ||
import remarkDirective from 'remark-directive'; | ||
import { visitParents } from 'unist-util-visit-parents'; | ||
|
||
interface FragmentPluginPage { | ||
fullPath: string; | ||
content: string; | ||
} | ||
|
||
const createPageTest = (ignorePages, pageExtensions) => { | ||
const extTest = new RegExp(`${pageExtensions.map(ext => escapeRegExp(ext)).join('|')}$`); | ||
const ignoreTest = new RegExp(`${ignorePages.map(ignore => escapeRegExp(ignore)).join('|')}$`); | ||
return file => | ||
!ignoreTest.test(file) && extTest.test(file) && !path.basename(file).startsWith('.'); | ||
}; | ||
|
||
function getFullPath(fullPath: string, relativePath: string): string { | ||
const pathSegments = fullPath.split('/'); | ||
const relativeSegments = relativePath.split('/'); | ||
|
||
pathSegments.pop(); | ||
|
||
for (const segment of relativeSegments) { | ||
if (segment === '..') { | ||
pathSegments.pop(); | ||
} else if (segment !== '.') { | ||
pathSegments.push(segment); | ||
} | ||
} | ||
return pathSegments.join('/'); | ||
} | ||
|
||
async function processTree(tree, serialiser, mutableFilesystem, fullPath, isNonHiddenPage) { | ||
const nodesToProcess = []; | ||
|
||
visitParents(tree, (node, ancestors) => { | ||
if (node.type === 'code') { | ||
return; | ||
} | ||
|
||
const match = node.name === 'fragment' && node.attributes.src; | ||
if (match) { | ||
const parent = ancestors[ancestors.length - 1]; | ||
const index = parent.children.indexOf(node); | ||
nodesToProcess.push({ node, parent, index }); | ||
} | ||
}); | ||
|
||
for (const { node, parent, index } of nodesToProcess) { | ||
const fragmentFullPath = getFullPath(fullPath, node.attributes.src); | ||
if (!isNonHiddenPage(fragmentFullPath)) { | ||
console.warn(`Invalid file reference: '${node.attributes.src}'. Skipping.`); | ||
} else { | ||
const fragmentPage = await serialiser.deserialise( | ||
fragmentFullPath, | ||
await mutableFilesystem.promises.readFile(fragmentFullPath) | ||
); | ||
|
||
// Create a new node with the content from fragmentPage.content | ||
const newNode = { | ||
type: 'html', | ||
value: fragmentPage.content | ||
}; | ||
|
||
// Replace the original node with the newNode in the tree | ||
parent.children.splice(index, 1, newNode); | ||
} | ||
} | ||
return tree; | ||
} | ||
|
||
const FragmentPlugin: PluginType<FragmentPluginPage, unknown, unknown> = { | ||
async $beforeSend(mutableFilesystem, { serialiser, ignorePages, pageExtensions }) { | ||
const pages = await Promise.all( | ||
( | ||
(await mutableFilesystem.promises.glob('**', { | ||
onlyFiles: true, | ||
ignore: ignorePages.map(ignore => `**/${ignore}`), | ||
cwd: '/' | ||
})) as string[] | ||
).map(async pagePath => { | ||
const deserialisedPage = await serialiser.deserialise( | ||
pagePath, | ||
await mutableFilesystem.promises.readFile(pagePath) | ||
); | ||
return deserialisedPage; | ||
}) | ||
); | ||
|
||
const isNonHiddenPage = createPageTest(ignorePages, pageExtensions); | ||
|
||
for (const page of pages) { | ||
const fullPath = page.fullPath; | ||
if (!isNonHiddenPage(fullPath)) { | ||
continue; | ||
} | ||
|
||
const tree = remark().use(remarkDirective).parse(page.content); | ||
const processedTree = await processTree( | ||
tree, | ||
serialiser, | ||
mutableFilesystem, | ||
fullPath, | ||
isNonHiddenPage | ||
); | ||
|
||
page.content = remark() | ||
.data('settings', { fences: true }) | ||
.use(remarkDirective) | ||
.stringify(processedTree); | ||
|
||
const updatedFileData = await serialiser.serialise(fullPath, page); | ||
await mutableFilesystem.promises.writeFile(fullPath, updatedFileData); | ||
} | ||
} | ||
}; | ||
|
||
export default FragmentPlugin; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
fragments.mdx |
Oops, something went wrong.