Skip to content

Commit 85124ca

Browse files
committed
fix(remark-extend): support md files with frontmatter
1 parent 87a1cb4 commit 85124ca

File tree

4 files changed

+105
-11
lines changed

4 files changed

+105
-11
lines changed

.changeset/purple-ties-complain.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'remark-extend': patch
3+
---
4+
5+
support md files with frontmatter

packages-node/remark-extend/src/remarkExtend.js

+23-5
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,31 @@ const path = require('path');
1919
* // execute function
2020
* virtualMod.fn();
2121
*
22-
* @param {*} src
23-
* @param {*} filename
22+
* @param {string} src
23+
* @param {string} filename
24+
* @returns {object}
2425
*/
2526
function requireFromString(src, filename = 'tmp.js') {
2627
const srcWithPath = `const path = require('path');\n${src}`;
28+
// @ts-expect-error
2729
const m = new module.constructor();
30+
// @ts-expect-error
2831
m.paths = module.paths;
2932
m._compile(srcWithPath, filename);
3033
return m.exports;
3134
}
3235

36+
/**
37+
* Frontmatter is problematic when traversing md files with
38+
* unist-util-select: https://github.com/syntax-tree/unist-util-select/blob/main/index.js.
39+
* So we remove it before parsing
40+
* @param {string} mdFileString
41+
* @returns {string}
42+
*/
43+
function stripFrontMatter(mdFileString) {
44+
return mdFileString.replace(/^\s*---(.|\n)*?---/, '');
45+
}
46+
3347
let toInsertNodes = [];
3448

3549
function handleImportedFile({
@@ -39,11 +53,12 @@ function handleImportedFile({
3953
globalReplaceFunction,
4054
filePath,
4155
missingEndSelectorMeansUntilEndOfFile = false,
56+
currentFile,
4257
}) {
4358
return tree => {
4459
const start = select(startSelector, tree);
4560
if (!start) {
46-
const msg = `The start selector "${startSelector}" could not find a matching node in "${filePath}".`;
61+
const msg = `The start selector "${startSelector}", imported in "${currentFile}", could not find a matching node in "${filePath}".`;
4762
throw new Error(msg);
4863
}
4964
const startIsNode = { ...start };
@@ -54,7 +69,7 @@ function handleImportedFile({
5469
const end = select(endSelector, tree);
5570
if (!end) {
5671
if (missingEndSelectorMeansUntilEndOfFile === false) {
57-
const msg = `The end selector "${endSelector}" could not find a matching node in "${filePath}".`;
72+
const msg = `The end selector "${endSelector}", imported in "${currentFile}", could not find a matching node in "${filePath}".`;
5873
throw new Error(msg);
5974
}
6075
} else {
@@ -92,6 +107,8 @@ function handleImportedFile({
92107
// unified expect direct
93108
// eslint-disable-next-line consistent-return
94109
function remarkExtend({ rootDir = process.cwd(), page, globalReplaceFunction } = {}) {
110+
const currentFile = path.resolve(rootDir, page.inputPath);
111+
95112
return tree => {
96113
visit(tree, (node, index, parent) => {
97114
if (
@@ -186,11 +203,12 @@ function remarkExtend({ rootDir = process.cwd(), page, globalReplaceFunction } =
186203
filePath,
187204
fileImport,
188205
missingEndSelectorMeansUntilEndOfFile,
206+
currentFile,
189207
})
190208
.use(function plugin() {
191209
this.Compiler = () => '';
192210
});
193-
parser.processSync(importFileContent.toString());
211+
parser.processSync(stripFrontMatter(importFileContent.toString()));
194212

195213
if (node.type === 'root') {
196214
node.children.splice(0, 0, ...toInsertNodes);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
parts:
3+
- API Table
4+
- Form
5+
- Systems
6+
title: 'Form: API Table'
7+
eleventyNavigation:
8+
key: API Table >> Form >> Systems
9+
title: API Table
10+
order: 90
11+
parent: Systems >> Form
12+
---
13+
14+
# Red
15+
16+
red is the fire
17+
18+
## More Red
19+
20+
the sun can get red
21+
22+
## Additional Red
23+
24+
the red sea

packages-node/remark-extend/test-node/remark-extend.test.mjs

+53-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { expect } from 'chai';
44
import unified from 'unified';
55
import markdown from 'remark-parse';
66
import mdStringify from 'remark-html';
7+
import gfm from 'remark-gfm';
78

89
import { remarkExtend } from '../src/remarkExtend.js';
910

@@ -22,6 +23,9 @@ async function expectThrowsAsync(method, { errorMatch, errorMessage } = {}) {
2223
} catch (err) {
2324
error = err;
2425
}
26+
27+
console.debug(error);
28+
2529
expect(error).to.be.an('Error', 'No error was thrown');
2630
if (errorMatch) {
2731
expect(error.message).to.match(errorMatch);
@@ -31,18 +35,40 @@ async function expectThrowsAsync(method, { errorMatch, errorMessage } = {}) {
3135
}
3236
}
3337

34-
async function execute(input, { globalReplaceFunction } = {}) {
38+
/**
39+
*
40+
* @param {string} input
41+
* @param {{shouldStringify?: boolean; globalReplaceFunction?: Function;}} param1
42+
*/
43+
async function execute(input, { shouldStringify = true, globalReplaceFunction } = {}) {
3544
const parser = unified()
3645
//
3746
.use(markdown)
47+
.use(gfm)
3848
.use(remarkExtend, {
3949
rootDir: __dirname,
4050
page: { inputPath: 'test-file.md' },
4151
globalReplaceFunction,
52+
});
53+
54+
if (shouldStringify) {
55+
parser.use(mdStringify);
56+
const result = await parser.process(input);
57+
return result.contents;
58+
}
59+
60+
let tree;
61+
parser
62+
.use(() => _tree => {
63+
tree = _tree;
4264
})
43-
.use(mdStringify);
44-
const result = await parser.process(input);
45-
return result.contents;
65+
// @ts-expect-error
66+
.use(function plugin() {
67+
this.Compiler = () => '';
68+
});
69+
70+
await parser.process(input);
71+
return tree;
4672
}
4773

4874
describe('remarkExtend', () => {
@@ -196,7 +222,7 @@ describe('remarkExtend', () => {
196222
"```js ::import('./fixtures/three-sections-red.md', 'heading:has([value=Does not exit])')\n```";
197223
await expectThrowsAsync(() => execute(input), {
198224
errorMatch:
199-
/The start selector "heading:has\(\[value=Does not exit\]\)" could not find a matching node in ".*"\.$/,
225+
/The start selector "heading:has\(\[value=Does not exit\]\)", imported in ".*", could not find a matching node in ".*"\.$/,
200226
});
201227
});
202228

@@ -205,7 +231,7 @@ describe('remarkExtend', () => {
205231
"```js ::import('./fixtures/three-sections-red.md', 'heading:has([value=More Red])', 'heading:has([value=Does not exit])')\n```";
206232
await expectThrowsAsync(() => execute(input), {
207233
errorMatch:
208-
/The end selector "heading:has\(\[value=Does not exit\]\)" could not find a matching node in ".*"\./,
234+
/The end selector "heading:has\(\[value=Does not exit\]\)", imported in ".*", could not find a matching node in ".*"\./,
209235
});
210236
});
211237

@@ -404,4 +430,25 @@ describe('remarkExtend', () => {
404430
].join('\n'),
405431
);
406432
});
433+
434+
it.only('supports files with frontmatter', async () => {
435+
const result = await execute(
436+
[
437+
//
438+
'### Static Headline',
439+
"```js ::importBlock('./fixtures/three-sections-red-with-frontmatter.md', '## More Red')",
440+
'```',
441+
].join('\n'),
442+
);
443+
444+
expect(result).to.equal(
445+
[
446+
//
447+
'<h3>Static Headline</h3>',
448+
'<h2>More Red</h2>',
449+
'<p>the sun can get red</p>',
450+
'',
451+
].join('\n'),
452+
);
453+
});
407454
});

0 commit comments

Comments
 (0)