diff --git a/.changeset/unlucky-games-double.md b/.changeset/unlucky-games-double.md new file mode 100644 index 000000000..6efe589ae --- /dev/null +++ b/.changeset/unlucky-games-double.md @@ -0,0 +1,5 @@ +--- +'remark-extend': patch +--- + +feat: allow globalReplaceFunction in remark-extend diff --git a/docs/fundamentals/node-tools/remark-extend/overview.md b/docs/fundamentals/node-tools/remark-extend/overview.md index 50d16e246..5ac7116df 100644 --- a/docs/fundamentals/node-tools/remark-extend/overview.md +++ b/docs/fundamentals/node-tools/remark-extend/overview.md @@ -184,7 +184,7 @@ This function gets called with these nodes in order. #### Adjustment Result ```md -# Below you will not find any read +# Below you will not find any 'red' ### Green @@ -193,6 +193,21 @@ green is the fire --- +It's also possible to globally define a replace function. +The example below shows the config object that is provided to the `remarkExtend` function. +Note that local replacements are run after global replacements. + +```js +{ globalReplaceFunction: node => { + if (node.type === 'link') { + // All internal links to '/systems/' (like '(../../fundamentals/systems/overlays/configuration.md)'), + // will be changed into '/web-systems/' ('(../../fundamentals/web-systems/overlays/configuration.md)'). + node.url = node.url.replace(/\systems\/g, '/web-systems/'); + } + return node; +}}; +``` + ## Import selection For `::import` you can provide a start and end selector based on [unist-util-select](https://github.com/syntax-tree/unist-util-select#support). diff --git a/packages-node/remark-extend/src/remarkExtend.js b/packages-node/remark-extend/src/remarkExtend.js index a30f15d07..5fbe657c7 100644 --- a/packages-node/remark-extend/src/remarkExtend.js +++ b/packages-node/remark-extend/src/remarkExtend.js @@ -36,6 +36,7 @@ function handleImportedFile({ startSelector, endSelector, userFunction, + globalReplaceFunction, filePath, missingEndSelectorMeansUntilEndOfFile = false, }) { @@ -73,6 +74,9 @@ function handleImportedFile({ } if (insertIt) { + if (globalReplaceFunction) { + node = globalReplaceFunction(node, { index, parent, tree }); + } if (userFunction) { node = userFunction(node, { index, parent, tree }); } @@ -87,7 +91,7 @@ function handleImportedFile({ // unified expect direct // eslint-disable-next-line consistent-return -function remarkExtend({ rootDir = process.cwd(), page } = {}) { +function remarkExtend({ rootDir = process.cwd(), page, globalReplaceFunction } = {}) { return tree => { visit(tree, (node, index, parent) => { if ( @@ -178,6 +182,7 @@ function remarkExtend({ rootDir = process.cwd(), page } = {}) { startSelector, endSelector, userFunction, + globalReplaceFunction, filePath, fileImport, missingEndSelectorMeansUntilEndOfFile, diff --git a/packages-node/remark-extend/test-node/remark-extend.test.js b/packages-node/remark-extend/test-node/remark-extend.test.js index 434923354..7c1b246f2 100644 --- a/packages-node/remark-extend/test-node/remark-extend.test.js +++ b/packages-node/remark-extend/test-node/remark-extend.test.js @@ -28,11 +28,15 @@ async function expectThrowsAsync(method, { errorMatch, errorMessage } = {}) { } } -async function execute(input) { +async function execute(input, { globalReplaceFunction } = {}) { const parser = unified() // .use(markdown) - .use(remarkExtend, { rootDir: __dirname, page: { inputPath: 'test-file.md' } }) + .use(remarkExtend, { + rootDir: __dirname, + page: { inputPath: 'test-file.md' }, + globalReplaceFunction, + }) .use(mdStringify); const result = await parser.process(input); return result.contents; @@ -147,6 +151,36 @@ describe('remarkExtend', () => { ); }); + it('can do global replacements, that run before local replacements', async () => { + const result = await execute( + [ + // + '# Red', + "```js ::import('./fixtures/three-sections-red.md', 'heading:has([value=More Red])', 'heading:has([value=More Red]) ~ heading[depth=2]')", + 'module.exports.replaceSection = (node) => {', + ' if (node.value) {', + " node.value = node.value.replace(/green/g, 'blue').replace(/Red/g, 'Blue');", + ' }', + ' return node;', + '};', + '```', + ].join('\n'), + { + globalReplaceFunction: node => { + if (node.value) { + // eslint-disable-next-line no-param-reassign + node.value = node.value.replace(/red/g, 'green'); + } + return node; + }, + }, + ); + + expect(result).to.equal( + ['

Red

', '

More Blue

', '

the sun can get blue

', ''].join('\n'), + ); + }); + it('throws if an import file does not exist', async () => { await expectThrowsAsync(() => execute("```js ::import('./fixtures/not-available.md')\n```"), { errorMatch: