diff --git a/.changeset/dollar-sign-replacement.md b/.changeset/dollar-sign-replacement.md new file mode 100644 index 00000000..5be598ca --- /dev/null +++ b/.changeset/dollar-sign-replacement.md @@ -0,0 +1,7 @@ +--- +'@node-core/doc-kit-legacy': patch +'@node-core/doc-kit': patch +'@doc-kit/generator-react': patch +--- + +Fix template value replacement treating `$` sequences (such as `$$` in Makefile snippets) as special replacement patterns, which corrupted the rendered output. diff --git a/packages/node-legacy/src/legacy-html/utils/__tests__/replaceTemplateValues.test.mjs b/packages/node-legacy/src/legacy-html/utils/__tests__/replaceTemplateValues.test.mjs new file mode 100644 index 00000000..61102c24 --- /dev/null +++ b/packages/node-legacy/src/legacy-html/utils/__tests__/replaceTemplateValues.test.mjs @@ -0,0 +1,66 @@ +'use strict'; + +import assert from 'node:assert/strict'; +import { before, describe, it } from 'node:test'; + +import { setConfig } from '@doc-kit/core/utils/configuration/index.mjs'; + +import { replaceTemplateValues } from '../replaceTemplateValues.mjs'; + +const template = '
__CONTENT__
'; + +const config = { version: { version: '1.0.0' }, changelog: [] }; + +const createValues = overrides => ({ + path: '/deprecations', + api: 'deprecations', + added: 'v1.0.0', + section: 'Deprecations', + toc: '', + nav: '', + content: '', + ...overrides, +}); + +describe('replaceTemplateValues', () => { + before(() => setConfig({})); + + it('inserts content containing `$$` as-is', () => { + const content = 'dirname "$$(command -v node)"'; + + const output = replaceTemplateValues( + template, + createValues({ content }), + config, + { skipGitHub: true } + ); + + assert.strictEqual(output, `
${content}
`); + }); + + it("inserts content containing `$&`, `$'` and `` $` `` as-is", () => { + const content = "x $& y $' z $` w"; + + const output = replaceTemplateValues( + template, + createValues({ content }), + config, + { skipGitHub: true } + ); + + assert.strictEqual(output, `
${content}
`); + }); + + it('inserts nav containing `$` sequences as-is', () => { + const nav = "$'"; + + const output = replaceTemplateValues( + template, + createValues({ nav }), + config, + { skipGitHub: true } + ); + + assert.strictEqual(output, `
`); + }); +}); diff --git a/packages/node-legacy/src/legacy-html/utils/replaceTemplateValues.mjs b/packages/node-legacy/src/legacy-html/utils/replaceTemplateValues.mjs index 4d434ea7..592800a9 100644 --- a/packages/node-legacy/src/legacy-html/utils/replaceTemplateValues.mjs +++ b/packages/node-legacy/src/legacy-html/utils/replaceTemplateValues.mjs @@ -25,18 +25,19 @@ export const replaceTemplateValues = ( { skipGitHub = false, skipGtocPicker = false } = {} ) => { return apiTemplate - .replace('__ID__', api) - .replace(/__FILENAME__/g, api) - .replace('__SECTION__', section) - .replace(/__VERSION__/g, `v${config.version.version}`) - .replace(/__TOC__/g, tableOfContents.wrapToC(toc)) - .replace(/__GTOC__/g, nav) - .replace('__CONTENT__', content) - .replace(/__TOC_PICKER__/g, buildToC(toc)) - .replace(/__GTOC_PICKER__/g, skipGtocPicker ? '' : buildNavigation(nav)) - .replace('__ALTDOCS__', buildVersions(path, added, config.changelog)) - .replace( - '__EDIT_ON_GITHUB__', + .replace('__ID__', () => api) + .replace(/__FILENAME__/g, () => api) + .replace('__SECTION__', () => section) + .replace(/__VERSION__/g, () => `v${config.version.version}`) + .replace(/__TOC__/g, () => tableOfContents.wrapToC(toc)) + .replace(/__GTOC__/g, () => nav) + .replace('__CONTENT__', () => content) + .replace(/__TOC_PICKER__/g, () => buildToC(toc)) + .replace(/__GTOC_PICKER__/g, () => + skipGtocPicker ? '' : buildNavigation(nav) + ) + .replace('__ALTDOCS__', () => buildVersions(path, added, config.changelog)) + .replace('__EDIT_ON_GITHUB__', () => skipGitHub ? '' : buildGitHub(populate(config.editURL, { ...config, path })) diff --git a/packages/node/src/man-page/generate.mjs b/packages/node/src/man-page/generate.mjs index 1b75fc9d..e6a75054 100644 --- a/packages/node/src/man-page/generate.mjs +++ b/packages/node/src/man-page/generate.mjs @@ -68,8 +68,8 @@ export async function generate(input) { const template = await readFile(config.templatePath, 'utf-8'); const filledTemplate = template - .replace('__OPTIONS__', output.options) - .replace('__ENVIRONMENT__', output.env); + .replace('__OPTIONS__', () => output.options) + .replace('__ENVIRONMENT__', () => output.env); if (config.output) { await writeFile(join(config.output, config.fileName), filledTemplate); diff --git a/packages/react/src/sitemap/generate.mjs b/packages/react/src/sitemap/generate.mjs index 8b87b9ed..dc6695fd 100644 --- a/packages/react/src/sitemap/generate.mjs +++ b/packages/react/src/sitemap/generate.mjs @@ -50,14 +50,14 @@ export async function generate(entries) { const urlset = apiPages .map(page => entryTemplate - .replace('__LOC__', page.loc) - .replace('__LASTMOD__', page.lastmod) - .replace('__CHANGEFREQ__', page.changefreq) - .replace('__PRIORITY__', page.priority) + .replace('__LOC__', () => page.loc) + .replace('__LASTMOD__', () => page.lastmod) + .replace('__CHANGEFREQ__', () => page.changefreq) + .replace('__PRIORITY__', () => page.priority) ) .join(''); - const sitemap = template.replace('__URLSET__', urlset); + const sitemap = template.replace('__URLSET__', () => urlset); if (config.output) { await writeFile(join(config.output, 'sitemap.xml'), sitemap, 'utf-8');