From 1c413928e37f8bbabf4cf884e3a35a20a6560c8c Mon Sep 17 00:00:00 2001 From: NathanYi Date: Tue, 12 Dec 2023 15:38:32 -0600 Subject: [PATCH 01/11] chore(mdx support): updated regex to include different syntax for comments --- .eslintrc.js | 2 +- .nvmrc | 2 +- CONTRIBUTING.md | 2 +- README.md | 6 ++--- src/__tests__/md-inject.test.ts | 42 +++++++++++++++++++++++++++++++-- src/md-inject.ts | 42 +++++++++++++++++++++++++++------ tsconfig.json | 3 ++- 7 files changed, 83 insertions(+), 16 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index af7c2f2..dc7a922 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -14,7 +14,7 @@ module.exports = { ], rules: { // VS Code linting will not respect the `.prettierrc` options unless injected here: - 'prettier/prettier': ['error', prettierrc], + 'prettier/prettier': ['warn', prettierrc], '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/no-explicit-any': 'off', }, diff --git a/.nvmrc b/.nvmrc index d60d573..016efd8 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.19.0 \ No newline at end of file +v20.10.0 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4d9d242..4c76c57 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ This project builds with node version: ~~~~~~~~~~bash -v16.19.0 +v20.10.0 ~~~~~~~~~~ diff --git a/README.md b/README.md index d235b74..daf1f62 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Options: `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: - +{/* CODEBLOCK_START_USAGE {"ignore": true} */} ``` @@ -79,12 +79,12 @@ Options: ``` - +{/* CODEBLOCK_START {"value": ".nvmrc"} --> ~~~~~~~~~~bash File: .nvmrc -v16.19.0 +v20.10.0 ~~~~~~~~~~ diff --git a/src/__tests__/md-inject.test.ts b/src/__tests__/md-inject.test.ts index e820556..0814391 100644 --- a/src/__tests__/md-inject.test.ts +++ b/src/__tests__/md-inject.test.ts @@ -140,7 +140,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) @@ -274,6 +276,9 @@ Foo Foo `, ], + [ + `{/* CODEBLOCK_START {"type": "command", "value": "some arbitrary command"} */} Foo {/* CODEBLOCK_END */}`, + ], ])('handles wonky formatting', async (markdownContent) => { glob.mockResolvedValue(['foo.md']) fs.readFile.mockResolvedValue(markdownContent) @@ -312,6 +317,30 @@ The output of some arbitrary command expect(fs.writeFile).toHaveBeenCalledWith('foo.md', outFile) }) + it('writes to the markdown document (command) even though bad syntax', async () => { + mock({ + 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"} --> + +~~~~~~~~~~bash +$ some arbitrary command + +The output of some arbitrary command +~~~~~~~~~~ + + - + {/* CODEBLOCK_END */} `, @@ -793,6 +822,15 @@ console.log('Hello World') --> +{/* + CODEBLOCK_START + { + "type": "command", + "value": "npm view foo" + } +*/} +{/* CODEBLOCK_END */} + # Bar Package )(?:.|\\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, + ]) + ) + console.log(matchGroups) 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 } @@ -139,8 +167,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 diff --git a/tsconfig.json b/tsconfig.json index 9e82193..e1fed90 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,8 @@ "resolveJsonModule": true, "lib": ["ES2019"], "module": "commonjs", - "target": "ES2019" + "target": "ES2019", + "sourceMap": true }, "include": ["src/**/*"], "exclude": ["**/*.test.*"] From ad125da54a71c661307a00d148dad0dede7053d3 Mon Sep 17 00:00:00 2001 From: NathanYi Date: Wed, 13 Dec 2023 14:27:25 -0600 Subject: [PATCH 02/11] chore(mdx support): added test to make sure bad comment syntax not working --- src/__tests__/md-inject.test.ts | 46 +++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/__tests__/md-inject.test.ts b/src/__tests__/md-inject.test.ts index 0814391..8fc4215 100644 --- a/src/__tests__/md-inject.test.ts +++ b/src/__tests__/md-inject.test.ts @@ -53,6 +53,8 @@ describe('Markdown injection', () => { isPr: false, })) + jest.clearAllMocks() + process.env = originalProcessEnv }) @@ -317,19 +319,9 @@ The output of some arbitrary command expect(fs.writeFile).toHaveBeenCalledWith('foo.md', outFile) }) - it('writes to the markdown document (command) even though bad syntax', async () => { - mock({ - config: { - type: 'command', - value: 'some arbitrary command', - }, - mockResponse: 'The output of some arbitrary command', - }) - - await injectMarkdown() - + it('does not write to the markdown document (command) because of bad syntax', async () => { const outFile = ` -{/* CODEBLOCK_START {"type":"command","value":"some arbitrary command"} --> + ~~~~~~~~~~bash $ some arbitrary command @@ -338,7 +330,35 @@ The output of some arbitrary command ~~~~~~~~~~ + + ~~~~~~~~~~bash $ some arbitrary command The output of some arbitrary command -~~~~~~~~~~ +~~~~~~~~~~` - - -~~~~~~~~~~bash -v20.10.0 -~~~~~~~~~~ - - - -After cloning the repository, install dependencies and build the project: - -``` -npm ci -``` - -Build the library and watch for changes: - -``` -npm start -``` - -Link your local copy: - -``` -npm link -``` - -`markdown-inject` commands in any terminal will now run using your local copy. - -### Validation - -This app ships with a local suite of [jest](https://jestjs.io/) tests, [eslint](https://eslint.org/) + [prettier](https://prettier.io/) configurations for code consistency and formatting, and [TypeScript](https://www.typescriptlang.org/) type validation. Each of these features can be validated using... - -```bash -npm test -npm run lint -npm run build -``` - -A `validate` utility script chains these calls together, and is called on every commit. - -```bash -npm run validate -``` diff --git a/README.md b/README.md index daf1f62..5461a8e 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,9 @@ Options: -`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 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} */} + ``` @@ -79,7 +79,7 @@ Options: ``` -{/* CODEBLOCK_START {"value": ".nvmrc"} --> + ~~~~~~~~~~bash File: .nvmrc diff --git a/src/md-inject.ts b/src/md-inject.ts index 3fce94f..62b5291 100644 --- a/src/md-inject.ts +++ b/src/md-inject.ts @@ -114,7 +114,6 @@ const main = async ( groupValue, ]) ) - console.log(matchGroups) try { let inputConfig: BlockInputOptions try { From 62fb3745ad67749e60d8fe9c1208a6cea8657669 Mon Sep 17 00:00:00 2001 From: NathanYi Date: Mon, 18 Dec 2023 11:40:26 -0600 Subject: [PATCH 05/11] chore(mdx support): added altered CONTRIBUTING file from .md to .mdx --- CONTRIBUTING.mdx | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 CONTRIBUTING.mdx diff --git a/CONTRIBUTING.mdx b/CONTRIBUTING.mdx new file mode 100644 index 0000000..c70aeb3 --- /dev/null +++ b/CONTRIBUTING.mdx @@ -0,0 +1,47 @@ +### Local Development + +This project builds with node version: + +{/* CODEBLOCK_START {"value": ".nvmrc", "hideValue": true} */} +{/* prettier-ignore */} +~~~~~~~~~~bash +v20.10.0 +~~~~~~~~~~ + +{/* CODEBLOCK_END */} + +After cloning the repository, install dependencies and build the project: + +``` +npm ci +``` + +Build the library and watch for changes: + +``` +npm start +``` + +Link your local copy: + +``` +npm link +``` + +`markdown-inject` commands in any terminal will now run using your local copy. + +### Validation + +This app ships with a local suite of [jest](https://jestjs.io/) tests, [eslint](https://eslint.org/) + [prettier](https://prettier.io/) configurations for code consistency and formatting, and [TypeScript](https://www.typescriptlang.org/) type validation. Each of these features can be validated using... + +```bash +npm test +npm run lint +npm run build +``` + +A `validate` utility script chains these calls together, and is called on every commit. + +```bash +npm run validate +``` From c266d72abb88ee7924984d75d2f61f33333e4f2d Mon Sep 17 00:00:00 2001 From: NathanYi Date: Mon, 18 Dec 2023 14:16:17 -0600 Subject: [PATCH 06/11] added link to CONTRIBUTING.mdx in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5461a8e..d285f56 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Options: -`markdown-inject` expands a given glob for markdown files. Then it discovers the below `CODEBLOCK` HTML or 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: +`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: From def27b1a1cea1aa635e59f689abea267a5061df5 Mon Sep 17 00:00:00 2001 From: NathanYi Date: Mon, 18 Dec 2023 14:45:21 -0600 Subject: [PATCH 07/11] failing test for mdx syntax --- src/__tests__/md-inject.test.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/__tests__/md-inject.test.ts b/src/__tests__/md-inject.test.ts index 5c73e2a..d748be1 100644 --- a/src/__tests__/md-inject.test.ts +++ b/src/__tests__/md-inject.test.ts @@ -319,6 +319,30 @@ 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({ + 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.md', outFile) + }) + it('does not write to the markdown document (command) because of bad syntax', async () => { const inFile = ` ~~~~~~~~~~bash $ some arbitrary command @@ -340,7 +340,7 @@ The output of some arbitrary command ~~~~~~~~~~ {/* CODEBLOCK_END */}` - expect(fs.writeFile).toHaveBeenCalledWith('foo.md', outFile) + expect(fs.writeFile).toHaveBeenCalledWith('foo.mdx', outFile) }) it('does not write to the markdown document (command) because of bad syntax', async () => { @@ -1103,3 +1103,42 @@ ${includePrettierIgnore ? '\n' : ''}${blockContents} }) } } + +const mocktwo = ({ + name = '', + mockFileName = 'foo.mdx', + config, + includePrettierIgnore = true, + blockContents = '', + mockResponse = '', +}: { + name?: string + mockFileName?: string + config: any + includePrettierIgnore?: boolean + blockContents?: string + mockResponse?: string +}) => { + glob.mockResolvedValue([mockFileName]) + + fs.readFile.mockImplementation(async (fileName) => { + if (fileName === mockFileName) { + return ` +{/* CODEBLOCK_START${name} ${JSON.stringify(config)} */} +${includePrettierIgnore ? '\n' : ''}${blockContents} +{/* CODEBLOCK_END${name} */}` + } + + if (config.type !== 'command' && fileName.includes(config.value)) { + return mockResponse + } + throw new Error('Unexpected file name passed') + }) + + if (config.type === 'command') { + exec.mockImplementation((...args) => { + const cb = args.pop() + cb(null, mockResponse) + }) + } +} From 4260737b0bb7f8f1e7186640fb05a9f9a62d6106 Mon Sep 17 00:00:00 2001 From: NathanYi Date: Mon, 18 Dec 2023 16:34:36 -0600 Subject: [PATCH 09/11] updated mock with mdx option for syntax --- src/__tests__/md-inject.test.ts | 46 +++++---------------------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/src/__tests__/md-inject.test.ts b/src/__tests__/md-inject.test.ts index d8efa8e..8d11b3b 100644 --- a/src/__tests__/md-inject.test.ts +++ b/src/__tests__/md-inject.test.ts @@ -320,7 +320,8 @@ The output of some arbitrary command }) it('writes to the markdown document (command) with mdx syntax', async () => { - mocktwo({ + mock({ + mockFileName: 'foo.mdx', config: { type: 'command', value: 'some arbitrary command', @@ -1083,50 +1084,17 @@ const mock = ({ glob.mockResolvedValue([mockFileName]) fs.readFile.mockImplementation(async (fileName) => { - if (fileName === mockFileName) { + if (fileName === mockFileName && fileName.includes('mdx')) { return ` - +{/* CODEBLOCK_START${name} ${JSON.stringify(config)} */} ${includePrettierIgnore ? '\n' : ''}${blockContents} -` - } - - if (config.type !== 'command' && fileName.includes(config.value)) { - return mockResponse +{/* CODEBLOCK_END${name} */}` } - throw new Error('Unexpected file name passed') - }) - - if (config.type === 'command') { - exec.mockImplementation((...args) => { - const cb = args.pop() - cb(null, mockResponse) - }) - } -} - -const mocktwo = ({ - name = '', - mockFileName = 'foo.mdx', - config, - includePrettierIgnore = true, - blockContents = '', - mockResponse = '', -}: { - name?: string - mockFileName?: string - config: any - includePrettierIgnore?: boolean - blockContents?: string - mockResponse?: string -}) => { - glob.mockResolvedValue([mockFileName]) - - fs.readFile.mockImplementation(async (fileName) => { if (fileName === mockFileName) { return ` -{/* CODEBLOCK_START${name} ${JSON.stringify(config)} */} + ${includePrettierIgnore ? '\n' : ''}${blockContents} -{/* CODEBLOCK_END${name} */}` +` } if (config.type !== 'command' && fileName.includes(config.value)) { From 55931ef1b7c28cbb74b3956b5b831a27956923aa Mon Sep 17 00:00:00 2001 From: NathanYi Date: Mon, 18 Dec 2023 16:59:17 -0600 Subject: [PATCH 10/11] updated if statement and changed prettier comment to mdx syntax --- src/__tests__/md-inject.test.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/__tests__/md-inject.test.ts b/src/__tests__/md-inject.test.ts index 8d11b3b..b968ac7 100644 --- a/src/__tests__/md-inject.test.ts +++ b/src/__tests__/md-inject.test.ts @@ -1084,14 +1084,13 @@ const mock = ({ glob.mockResolvedValue([mockFileName]) fs.readFile.mockImplementation(async (fileName) => { - if (fileName === mockFileName && fileName.includes('mdx')) { - return ` + if (fileName === mockFileName) { + return fileName.includes('mdx') + ? ` {/* CODEBLOCK_START${name} ${JSON.stringify(config)} */} -${includePrettierIgnore ? '\n' : ''}${blockContents} +${includePrettierIgnore ? '{/* prettier-ignore */}\n' : ''}${blockContents} {/* CODEBLOCK_END${name} */}` - } - if (fileName === mockFileName) { - return ` + : ` ${includePrettierIgnore ? '\n' : ''}${blockContents} ` From 2060155f90216520be7e165861ed717d6de15f86 Mon Sep 17 00:00:00 2001 From: NathanYi Date: Tue, 19 Dec 2023 11:03:35 -0600 Subject: [PATCH 11/11] chore(mdx support): updated prettierIgnore with mdx style and added test to check --- src/__tests__/md-inject.test.ts | 31 ++++++++++++++++++++++++++++++- src/md-inject.ts | 5 ++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/__tests__/md-inject.test.ts b/src/__tests__/md-inject.test.ts index b968ac7..2c77c14 100644 --- a/src/__tests__/md-inject.test.ts +++ b/src/__tests__/md-inject.test.ts @@ -333,7 +333,7 @@ The output of some arbitrary command const outFile = ` {/* CODEBLOCK_START {"type":"command","value":"some arbitrary command"} */} - +{/* prettier-ignore */} ~~~~~~~~~~bash $ some arbitrary command @@ -344,6 +344,35 @@ The output of some arbitrary command 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 = ` +~~~~~~~~~~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 = ` ' + const checkFileName = fileName + const prettierIgnore = checkFileName.includes('mdx') + ? '{/* prettier-ignore */}' + : '' if (trim) { out = out.trim()