Skip to content

Commit d09edae

Browse files
feat: add mdx support
48/mdx support
2 parents af1d5a1 + 2060155 commit d09edae

File tree

6 files changed

+154
-19
lines changed

6 files changed

+154
-19
lines changed

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v16.19.0
1+
v20.10.0

CONTRIBUTING.md renamed to CONTRIBUTING.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
This project builds with node version:
44

5-
<!-- CODEBLOCK_START {"value": ".nvmrc", "hideValue": true} -->
6-
<!-- prettier-ignore -->
5+
{/* CODEBLOCK_START {"value": ".nvmrc", "hideValue": true} */}
6+
{/* prettier-ignore */}
77
~~~~~~~~~~bash
8-
v16.19.0
8+
v20.10.0
99
~~~~~~~~~~
1010

11-
<!-- CODEBLOCK_END -->
11+
{/* CODEBLOCK_END */}
1212

1313
After cloning the repository, install dependencies and build the project:
1414

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Options:
6767

6868
<!-- CODEBLOCK_END -->
6969

70-
`markdown-inject` expands a given glob for markdown files. Then it discovers the below `CODEBLOCK` HTML comments within each markdown file, performs the appropriate action (in this case, reading another local file), and writes content back into the markdown file:
70+
`markdown-inject` expands a given glob for markdown files. Then it discovers the below `CODEBLOCK` HTML or MDX (as shown in [CONTRIBUTING.mdx](./CONTRIBUTING.mdx)) comments within each markdown file, performs the appropriate action (in this case, reading another local file), and writes content back into the markdown file:
7171

7272
<!-- CODEBLOCK_START_USAGE {"ignore": true} -->
7373

@@ -84,7 +84,7 @@ Options:
8484
~~~~~~~~~~bash
8585
File: .nvmrc
8686
87-
v16.19.0
87+
v20.10.0
8888
~~~~~~~~~~
8989
9090
<!-- CODEBLOCK_END -->

src/__tests__/md-inject.test.ts

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ describe('Markdown injection', () => {
5353
isPr: false,
5454
}))
5555

56+
jest.clearAllMocks()
57+
5658
process.env = originalProcessEnv
5759
})
5860

@@ -140,7 +142,9 @@ describe('Markdown injection', () => {
140142
)
141143
expect(logger.error).toHaveBeenCalledWith(
142144
expect.objectContaining({
143-
message: expect.stringContaining('Unexpected token'),
145+
message: expect.stringMatching(
146+
/Unexpected token|Expected property name/
147+
),
144148
})
145149
)
146150
expect(process.exitCode).toBe(1)
@@ -274,6 +278,9 @@ Foo
274278
Foo
275279
<!-- CODEBLOCK_END -->`,
276280
],
281+
[
282+
`{/* CODEBLOCK_START {"type": "command", "value": "some arbitrary command"} */} Foo {/* CODEBLOCK_END */}`,
283+
],
277284
])('handles wonky formatting', async (markdownContent) => {
278285
glob.mockResolvedValue(['foo.md'])
279286
fs.readFile.mockResolvedValue(markdownContent)
@@ -312,6 +319,89 @@ The output of some arbitrary command
312319
expect(fs.writeFile).toHaveBeenCalledWith('foo.md', outFile)
313320
})
314321

322+
it('writes to the markdown document (command) with mdx syntax', async () => {
323+
mock({
324+
mockFileName: 'foo.mdx',
325+
config: {
326+
type: 'command',
327+
value: 'some arbitrary command',
328+
},
329+
mockResponse: 'The output of some arbitrary command',
330+
})
331+
332+
await injectMarkdown()
333+
334+
const outFile = `
335+
{/* CODEBLOCK_START {"type":"command","value":"some arbitrary command"} */}
336+
{/* prettier-ignore */}
337+
~~~~~~~~~~bash
338+
$ some arbitrary command
339+
340+
The output of some arbitrary command
341+
~~~~~~~~~~
342+
343+
{/* CODEBLOCK_END */}`
344+
expect(fs.writeFile).toHaveBeenCalledWith('foo.mdx', outFile)
345+
})
346+
347+
it('fails to write to the markdown document (command) with mixed syntax', async () => {
348+
const inFile = `
349+
{/* CODEBLOCK_START {"type":"command","value":"some arbitrary command"} */}
350+
351+
{/* CODEBLOCK_END */}`
352+
353+
const inFileName = `<!-- prettier-ignore -->
354+
~~~~~~~~~~bash
355+
$ some arbitrary command
356+
357+
The output of some arbitrary command
358+
~~~~~~~~~~`
359+
360+
glob.mockResolvedValue([inFileName])
361+
362+
fs.readFile.mockImplementation(async (fileName) => {
363+
if (fileName === inFileName) {
364+
return inFile
365+
}
366+
throw new Error('Unexpected file name passed')
367+
})
368+
369+
await injectMarkdown()
370+
371+
expect(fs.readFile).toHaveBeenCalledWith(inFileName, { encoding: 'utf-8' })
372+
373+
expect(fs.writeFile).not.toHaveBeenCalled()
374+
})
375+
376+
it('does not write to the markdown document (command) because of bad syntax', async () => {
377+
const inFile = `
378+
<!-- CODEBLOCK_START {"type":"command","value":"some arbitrary command"} */}
379+
380+
<!-- CODEBLOCK_END */}`
381+
382+
const inFileName = `<!-- prettier-ignore -->
383+
~~~~~~~~~~bash
384+
$ some arbitrary command
385+
386+
The output of some arbitrary command
387+
~~~~~~~~~~`
388+
389+
glob.mockResolvedValue([inFileName])
390+
391+
fs.readFile.mockImplementation(async (fileName) => {
392+
if (fileName === inFileName) {
393+
return inFile
394+
}
395+
throw new Error('Unexpected file name passed')
396+
})
397+
398+
await injectMarkdown()
399+
400+
expect(fs.readFile).toHaveBeenCalledWith(inFileName, { encoding: 'utf-8' })
401+
402+
expect(fs.writeFile).not.toHaveBeenCalled()
403+
})
404+
315405
it('writes to the markdown document (file)', async () => {
316406
mock({
317407
config: {
@@ -688,7 +778,7 @@ echo "Hello World"
688778
},
689779
blockContents: `
690780
<!-- CODEBLOCK_END -->
691-
<!-- CODEBLOCK_END -->
781+
{/* CODEBLOCK_END */}
692782
<!-- CODEBLOCK_END -->
693783
<!-- CODEBLOCK_END -->
694784
`,
@@ -793,6 +883,15 @@ console.log('Hello World')
793883
-->
794884
<!-- CODEBLOCK_END -->
795885
886+
{/*
887+
CODEBLOCK_START
888+
{
889+
"type": "command",
890+
"value": "npm view foo"
891+
}
892+
*/}
893+
{/* CODEBLOCK_END */}
894+
796895
# Bar Package
797896
798897
<!--
@@ -1015,7 +1114,12 @@ const mock = ({
10151114

10161115
fs.readFile.mockImplementation(async (fileName) => {
10171116
if (fileName === mockFileName) {
1018-
return `
1117+
return fileName.includes('mdx')
1118+
? `
1119+
{/* CODEBLOCK_START${name} ${JSON.stringify(config)} */}
1120+
${includePrettierIgnore ? '{/* prettier-ignore */}\n' : ''}${blockContents}
1121+
{/* CODEBLOCK_END${name} */}`
1122+
: `
10191123
<!-- CODEBLOCK_START${name} ${JSON.stringify(config)} -->
10201124
${includePrettierIgnore ? '<!-- prettier-ignore -->\n' : ''}${blockContents}
10211125
<!-- CODEBLOCK_END${name} -->`

src/md-inject.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,23 +76,50 @@ const main = async (
7676

7777
let modifiedFileContents = originalFileContents
7878

79-
let codeblockMatch
79+
let codeblockMatch: RegExpExecArray
8080
let blocksChanged = 0
8181
let blocksIgnored = 0
8282
let totalBlocks = 0
8383

84+
const comment = {
85+
html: {
86+
start: '<!-{2,}',
87+
end: '-{2,}>',
88+
},
89+
mdx: {
90+
start: '\\{\\s*/\\*',
91+
end: '\\*/\\s*\\}',
92+
},
93+
} as const
8494
const codeblockRegex = new RegExp(
85-
`(?<start_pragma><!--\\s*${blockPrefix}_START(?<name_ext>\\w*)\\s+(?<config>\\{(?:.|\\n)+?\\})\\s*-->)(?:.|\\s)*?(?<end_pragma><!--\\s*${blockPrefix}_END\\k<name_ext>\\s*-->)`,
86-
'g'
95+
Object.entries(comment)
96+
.map(
97+
([commentType, { start: commentStart, end: commentEnd }]) =>
98+
`(?<${commentType}_start_pragma>${commentStart}\\s*${blockPrefix}_START(?<${commentType}_name_ext>\\w*)\\s+(?<${commentType}_config>\\{(?:.|\\n)+?\\})\\s*${commentEnd}).*?(?<${commentType}_end_pragma>${commentStart}\\s*${blockPrefix}_END\\k<${commentType}_name_ext>\\s*${commentEnd})`
99+
)
100+
.join('|'),
101+
'gs'
87102
)
88103

89104
while ((codeblockMatch = codeblockRegex.exec(modifiedFileContents))) {
105+
const matchGroups = Object.fromEntries(
106+
Object.entries(codeblockMatch.groups)
107+
.filter(([groupName]) =>
108+
groupName.startsWith(
109+
codeblockMatch.groups.html_config ? 'html_' : 'mdx_'
110+
)
111+
)
112+
.map(([groupName, groupValue]) => [
113+
groupName.replace(/^(html|mdx)_/, ''),
114+
groupValue,
115+
])
116+
)
90117
try {
91118
let inputConfig: BlockInputOptions
92119
try {
93-
inputConfig = JSON.parse(codeblockMatch.groups.config)
120+
inputConfig = JSON.parse(matchGroups.config)
94121
} catch (err) {
95-
logger.error(`Error parsing config:\n${codeblockMatch.groups.config}`)
122+
logger.error(`Error parsing config:\n${matchGroups.config}`)
96123
throw err
97124
}
98125

@@ -139,8 +166,8 @@ const main = async (
139166
}
140167

141168
const [originalBlock] = codeblockMatch
142-
const startPragma = codeblockMatch.groups.start_pragma
143-
const endPragma = codeblockMatch.groups.end_pragma
169+
const startPragma = matchGroups.start_pragma
170+
const endPragma = matchGroups.end_pragma
144171

145172
let out: string
146173

@@ -180,7 +207,10 @@ const main = async (
180207
// https://github.github.com/gfm/#example-94
181208
const codeblockFence = '~~~~~~~~~~'
182209

183-
const prettierIgnore = '<!-- prettier-ignore -->'
210+
const checkFileName = fileName
211+
const prettierIgnore = checkFileName.includes('mdx')
212+
? '{/* prettier-ignore */}'
213+
: '<!-- prettier-ignore -->'
184214

185215
if (trim) {
186216
out = out.trim()

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"resolveJsonModule": true,
77
"lib": ["ES2019"],
88
"module": "commonjs",
9-
"target": "ES2019"
9+
"target": "ES2019",
10+
"sourceMap": true
1011
},
1112
"include": ["src/**/*"],
1213
"exclude": ["**/*.test.*"]

0 commit comments

Comments
 (0)