Skip to content

Commit

Permalink
feat: add mdx support
Browse files Browse the repository at this point in the history
48/mdx support
  • Loading branch information
bmuenzenmeyer authored Dec 21, 2023
2 parents af1d5a1 + 2060155 commit d09edae
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v16.19.0
v20.10.0
8 changes: 4 additions & 4 deletions CONTRIBUTING.md → CONTRIBUTING.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

This project builds with node version:

<!-- CODEBLOCK_START {"value": ".nvmrc", "hideValue": true} -->
<!-- prettier-ignore -->
{/* CODEBLOCK_START {"value": ".nvmrc", "hideValue": true} */}
{/* prettier-ignore */}
~~~~~~~~~~bash
v16.19.0
v20.10.0
~~~~~~~~~~

<!-- CODEBLOCK_END -->
{/* CODEBLOCK_END */}

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

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Options:

<!-- CODEBLOCK_END -->

`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:
`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:

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

Expand All @@ -84,7 +84,7 @@ Options:
~~~~~~~~~~bash
File: .nvmrc
v16.19.0
v20.10.0
~~~~~~~~~~
<!-- CODEBLOCK_END -->
Expand Down
110 changes: 107 additions & 3 deletions src/__tests__/md-inject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ describe('Markdown injection', () => {
isPr: false,
}))

jest.clearAllMocks()

process.env = originalProcessEnv
})

Expand Down Expand Up @@ -140,7 +142,9 @@ describe('Markdown injection', () => {
)
expect(logger.error).toHaveBeenCalledWith(
expect.objectContaining({
message: expect.stringContaining('Unexpected token'),
message: expect.stringMatching(
/Unexpected token|Expected property name/
),
})
)
expect(process.exitCode).toBe(1)
Expand Down Expand Up @@ -274,6 +278,9 @@ Foo
Foo
<!-- CODEBLOCK_END -->`,
],
[
`{/* CODEBLOCK_START {"type": "command", "value": "some arbitrary command"} */} Foo {/* CODEBLOCK_END */}`,
],
])('handles wonky formatting', async (markdownContent) => {
glob.mockResolvedValue(['foo.md'])
fs.readFile.mockResolvedValue(markdownContent)
Expand Down Expand Up @@ -312,6 +319,89 @@ The output of some arbitrary command
expect(fs.writeFile).toHaveBeenCalledWith('foo.md', outFile)
})

it('writes to the markdown document (command) with mdx syntax', async () => {
mock({
mockFileName: 'foo.mdx',
config: {
type: 'command',
value: 'some arbitrary command',
},
mockResponse: 'The output of some arbitrary command',
})

await injectMarkdown()

const outFile = `
{/* CODEBLOCK_START {"type":"command","value":"some arbitrary command"} */}
{/* prettier-ignore */}
~~~~~~~~~~bash
$ some arbitrary command
The output of some arbitrary command
~~~~~~~~~~
{/* CODEBLOCK_END */}`
expect(fs.writeFile).toHaveBeenCalledWith('foo.mdx', outFile)
})

it('fails to write to the markdown document (command) with mixed syntax', async () => {
const inFile = `
{/* CODEBLOCK_START {"type":"command","value":"some arbitrary command"} */}
{/* CODEBLOCK_END */}`

const inFileName = `<!-- prettier-ignore -->
~~~~~~~~~~bash
$ some arbitrary command
The output of some arbitrary command
~~~~~~~~~~`

glob.mockResolvedValue([inFileName])

fs.readFile.mockImplementation(async (fileName) => {
if (fileName === inFileName) {
return inFile
}
throw new Error('Unexpected file name passed')
})

await injectMarkdown()

expect(fs.readFile).toHaveBeenCalledWith(inFileName, { encoding: 'utf-8' })

expect(fs.writeFile).not.toHaveBeenCalled()
})

it('does not write to the markdown document (command) because of bad syntax', async () => {
const inFile = `
<!-- CODEBLOCK_START {"type":"command","value":"some arbitrary command"} */}
<!-- CODEBLOCK_END */}`

const inFileName = `<!-- prettier-ignore -->
~~~~~~~~~~bash
$ some arbitrary command
The output of some arbitrary command
~~~~~~~~~~`

glob.mockResolvedValue([inFileName])

fs.readFile.mockImplementation(async (fileName) => {
if (fileName === inFileName) {
return inFile
}
throw new Error('Unexpected file name passed')
})

await injectMarkdown()

expect(fs.readFile).toHaveBeenCalledWith(inFileName, { encoding: 'utf-8' })

expect(fs.writeFile).not.toHaveBeenCalled()
})

it('writes to the markdown document (file)', async () => {
mock({
config: {
Expand Down Expand Up @@ -688,7 +778,7 @@ echo "Hello World"
},
blockContents: `
<!-- CODEBLOCK_END -->
<!-- CODEBLOCK_END -->
{/* CODEBLOCK_END */}
<!-- CODEBLOCK_END -->
<!-- CODEBLOCK_END -->
`,
Expand Down Expand Up @@ -793,6 +883,15 @@ console.log('Hello World')
-->
<!-- CODEBLOCK_END -->
{/*
CODEBLOCK_START
{
"type": "command",
"value": "npm view foo"
}
*/}
{/* CODEBLOCK_END */}
# Bar Package
<!--
Expand Down Expand Up @@ -1015,7 +1114,12 @@ const mock = ({

fs.readFile.mockImplementation(async (fileName) => {
if (fileName === mockFileName) {
return `
return fileName.includes('mdx')
? `
{/* CODEBLOCK_START${name} ${JSON.stringify(config)} */}
${includePrettierIgnore ? '{/* prettier-ignore */}\n' : ''}${blockContents}
{/* CODEBLOCK_END${name} */}`
: `
<!-- CODEBLOCK_START${name} ${JSON.stringify(config)} -->
${includePrettierIgnore ? '<!-- prettier-ignore -->\n' : ''}${blockContents}
<!-- CODEBLOCK_END${name} -->`
Expand Down
46 changes: 38 additions & 8 deletions src/md-inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,50 @@ const main = async (

let modifiedFileContents = originalFileContents

let codeblockMatch
let codeblockMatch: RegExpExecArray
let blocksChanged = 0
let blocksIgnored = 0
let totalBlocks = 0

const comment = {
html: {
start: '<!-{2,}',
end: '-{2,}>',
},
mdx: {
start: '\\{\\s*/\\*',
end: '\\*/\\s*\\}',
},
} as const
const codeblockRegex = new RegExp(
`(?<start_pragma><!--\\s*${blockPrefix}_START(?<name_ext>\\w*)\\s+(?<config>\\{(?:.|\\n)+?\\})\\s*-->)(?:.|\\s)*?(?<end_pragma><!--\\s*${blockPrefix}_END\\k<name_ext>\\s*-->)`,
'g'
Object.entries(comment)
.map(
([commentType, { start: commentStart, end: commentEnd }]) =>
`(?<${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})`
)
.join('|'),
'gs'
)

while ((codeblockMatch = codeblockRegex.exec(modifiedFileContents))) {
const matchGroups = Object.fromEntries(
Object.entries(codeblockMatch.groups)
.filter(([groupName]) =>
groupName.startsWith(
codeblockMatch.groups.html_config ? 'html_' : 'mdx_'
)
)
.map(([groupName, groupValue]) => [
groupName.replace(/^(html|mdx)_/, ''),
groupValue,
])
)
try {
let inputConfig: BlockInputOptions
try {
inputConfig = JSON.parse(codeblockMatch.groups.config)
inputConfig = JSON.parse(matchGroups.config)
} catch (err) {
logger.error(`Error parsing config:\n${codeblockMatch.groups.config}`)
logger.error(`Error parsing config:\n${matchGroups.config}`)
throw err
}

Expand Down Expand Up @@ -139,8 +166,8 @@ const main = async (
}

const [originalBlock] = codeblockMatch
const startPragma = codeblockMatch.groups.start_pragma
const endPragma = codeblockMatch.groups.end_pragma
const startPragma = matchGroups.start_pragma
const endPragma = matchGroups.end_pragma

let out: string

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

const prettierIgnore = '<!-- prettier-ignore -->'
const checkFileName = fileName
const prettierIgnore = checkFileName.includes('mdx')
? '{/* prettier-ignore */}'
: '<!-- prettier-ignore -->'

if (trim) {
out = out.trim()
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"resolveJsonModule": true,
"lib": ["ES2019"],
"module": "commonjs",
"target": "ES2019"
"target": "ES2019",
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["**/*.test.*"]
Expand Down

0 comments on commit d09edae

Please sign in to comment.