Skip to content
This repository was archived by the owner on Feb 8, 2025. It is now read-only.

Commit 465e0e4

Browse files
authored
feat(contented): ability to customize unified plugins (#629)
#### What this PR does / why we need it: Make it easier to customize content processor with subset of plugins.
1 parent a39b7dd commit 465e0e4

File tree

5 files changed

+91
-12
lines changed

5 files changed

+91
-12
lines changed

packages/contented-example/docs/04-markdown.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ processor
1515
.use(remarkParse)
1616
.use(remarkLink)
1717
.use(remarkDirective)
18+
.use(remarkDirectiveRehypeCodeblockHeader)
19+
.use(remarkDirectiveRehypeCodeblockGroup)
1820
.use(remarkDirectiveRehype)
19-
.use(collectFields)
20-
.use(resolveFields)
21-
.use(validateFields)
21+
.use(remarkFrontmatterCollect)
22+
.use(remarkFrontmatterResolve)
23+
.use(remarkFrontmatterValidate)
2224
.use(options.remarks)
2325
.use(remarkRehype)
2426
.use(options.rehypes)
@@ -28,7 +30,7 @@ processor
2830
.use(rehypeToc)
2931
.use(rehypeHeading)
3032
.use(rehypeMermaid)
31-
.use(rehypeShiki, { highlighter })
33+
.use(rehypeShiki)
3234
.use(rehypeStringify)
3335
.use(options.after);
3436
```

packages/contented-pipeline-md/src/MarkdownPipeline.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import console from 'node:console';
22
import { join } from 'node:path';
33

4-
import { ContentedPipeline, FileContent, FileIndex } from '@contentedjs/contented-pipeline';
4+
import { ContentedPipeline, FileContent, FileIndex, Pipeline } from '@contentedjs/contented-pipeline';
55
import { read } from 'to-vfile';
66
import { Processor, unified } from 'unified';
77
import { VFile } from 'vfile';
@@ -13,7 +13,7 @@ export class MarkdownPipeline extends ContentedPipeline {
1313
protected readonly processor: Processor = unified();
1414

1515
async init() {
16-
await initProcessor(this.processor);
16+
initProcessor(this.processor);
1717
}
1818

1919
protected override async processFileIndex(
@@ -56,6 +56,23 @@ export class MarkdownPipeline extends ContentedPipeline {
5656
fields: contented.fields,
5757
};
5858
}
59+
60+
/**
61+
* Create a new MarkdownPipeline with a custom processor.
62+
* This is useful for only using a subset of the plugins that you need.
63+
* @param processor is a function that takes a Processor and adds plugins to it.
64+
*/
65+
static withProcessor(
66+
processor: (processor: Processor) => void,
67+
): new (rootPath: string, pipeline: Pipeline) => ContentedPipeline {
68+
class WithProcessor extends MarkdownPipeline {
69+
async init(): Promise<void> {
70+
processor(this.processor);
71+
}
72+
}
73+
74+
return WithProcessor;
75+
}
5976
}
6077

6178
export class MarkdownVFile extends VFile {

packages/contented-pipeline-md/src/MarkdownPipeline.unit.ts

+31
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { join } from 'node:path';
22

33
import { MarkdownPipeline } from './MarkdownPipeline';
4+
import { rehypeStringify, remarkParse, remarkRehype } from './UnifiedProcessor';
45

56
const rootPath = join(__dirname, '../fixtures');
67

@@ -95,3 +96,33 @@ describe('Without Config', () => {
9596
]);
9697
});
9798
});
99+
100+
describe('Custom Pipeline', () => {
101+
const pipeline = new (MarkdownPipeline.withProcessor((processor) => {
102+
processor.use(remarkParse).use(remarkRehype).use(rehypeStringify);
103+
}))(__dirname, {
104+
type: 'Markdown',
105+
pattern: '/See.Nothing.md',
106+
processor: 'md',
107+
});
108+
109+
beforeAll(async () => {
110+
await pipeline.init();
111+
});
112+
113+
it('should process See.Nothing.md', async () => {
114+
const content = await pipeline.process(rootPath, 'See.Nothing.md');
115+
expect(content).toStrictEqual([
116+
{
117+
type: 'Markdown',
118+
fields: {},
119+
headings: [],
120+
path: '/see-nothing',
121+
sections: [],
122+
fileId: expect.stringMatching(/[0-f]{64}/),
123+
modifiedDate: expect.any(Number),
124+
html: '<h1>Nothing To See</h1>\n<p>Markdown for testing <code>MarkdownPipeline.unit.ts</code>.</p>',
125+
},
126+
]);
127+
});
128+
});

packages/contented-pipeline-md/src/UnifiedProcessor.ts

+32-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ import {
1818
remarkDirectiveRehypeCodeblockHeader,
1919
} from './plugins/RemarkCodeblock.js';
2020
import { remarkDirectiveRehype } from './plugins/RemarkDirectiveRehype.js';
21-
import { collectFields, resolveFields, validateFields } from './plugins/RemarkFrontmatter.js';
21+
import {
22+
remarkFrontmatterCollect,
23+
remarkFrontmatterResolve,
24+
remarkFrontmatterValidate,
25+
} from './plugins/RemarkFrontmatter.js';
2226
import { remarkLink } from './plugins/RemarkLink.js';
2327

2428
export interface UnifiedOptions {
@@ -28,7 +32,7 @@ export interface UnifiedOptions {
2832
after?: Plugin[];
2933
}
3034

31-
export async function initProcessor(processor: Processor, options?: UnifiedOptions) {
35+
export function initProcessor(processor: Processor, options?: UnifiedOptions): Processor {
3236
options?.before?.forEach((plugin) => {
3337
processor.use(plugin);
3438
});
@@ -43,7 +47,7 @@ export async function initProcessor(processor: Processor, options?: UnifiedOptio
4347
.use(remarkDirectiveRehypeCodeblockGroup)
4448
.use(remarkDirectiveRehype);
4549

46-
processor.use(collectFields).use(resolveFields).use(validateFields);
50+
processor.use(remarkFrontmatterCollect).use(remarkFrontmatterResolve).use(remarkFrontmatterValidate);
4751

4852
options?.remarks?.forEach((plugin) => {
4953
processor.use(plugin);
@@ -69,4 +73,29 @@ export async function initProcessor(processor: Processor, options?: UnifiedOptio
6973
options?.after?.forEach((plugin) => {
7074
processor.use(plugin);
7175
});
76+
77+
return processor;
7278
}
79+
80+
export {
81+
rehypeAutolinkHeadings,
82+
rehypeExternalLinks,
83+
rehypeHeading,
84+
rehypeMermaid,
85+
rehypeShiki,
86+
rehypeSlug,
87+
rehypeStringify,
88+
rehypeToc,
89+
remarkDirective,
90+
remarkDirectiveRehype,
91+
remarkDirectiveRehypeCodeblockGroup,
92+
remarkDirectiveRehypeCodeblockHeader,
93+
remarkFrontmatter,
94+
remarkFrontmatterCollect,
95+
remarkFrontmatterResolve,
96+
remarkFrontmatterValidate,
97+
remarkGfm,
98+
remarkLink,
99+
remarkParse,
100+
remarkRehype,
101+
};

packages/contented-pipeline-md/src/plugins/RemarkFrontmatter.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { VFile } from 'vfile';
99

1010
import { UnifiedContented } from './Plugin.js';
1111

12-
export function collectFields(): Transformer<Parent> {
12+
export function remarkFrontmatterCollect(): Transformer<Parent> {
1313
return (tree: Parent, file) => {
1414
const node = tree.children?.[0];
1515
if (node?.type === 'yaml') {
@@ -49,7 +49,7 @@ function visitDescription(file: VFile): (node: Paragraph) => void {
4949
};
5050
}
5151

52-
export function resolveFields(): Transformer<Parent> {
52+
export function remarkFrontmatterResolve(): Transformer<Parent> {
5353
return async (tree: Parent, file) => {
5454
const contented = file.data.contented as UnifiedContented;
5555
const entries = Object.entries(contented.pipeline.fields ?? {});
@@ -70,7 +70,7 @@ export function resolveFields(): Transformer<Parent> {
7070
};
7171
}
7272

73-
export function validateFields(): Transformer<Parent> {
73+
export function remarkFrontmatterValidate(): Transformer<Parent> {
7474
return async (tree: Parent, file) => {
7575
const contented = file.data.contented as UnifiedContented;
7676
const entries = Object.entries(contented.pipeline.fields ?? {});

0 commit comments

Comments
 (0)