From 8197ece6127e84199f334b59dc9e7c2b44b0348d Mon Sep 17 00:00:00 2001
From: btea <2356281422@qq.com>
Date: Mon, 26 Oct 2026 22:03:50 +0800
Subject: [PATCH] fix(legacy-html): keep `$` sequences in template values as-is
---
.changeset/dollar-sign-replacement.md | 7 ++
.../__tests__/replaceTemplateValues.test.mjs | 66 +++++++++++++++++++
.../utils/replaceTemplateValues.mjs | 25 +++----
packages/node/src/man-page/generate.mjs | 4 +-
packages/react/src/sitemap/generate.mjs | 10 +--
5 files changed, 93 insertions(+), 19 deletions(-)
create mode 100644 .changeset/dollar-sign-replacement.md
create mode 100644 packages/node-legacy/src/legacy-html/utils/__tests__/replaceTemplateValues.test.mjs
diff --git a/.changeset/dollar-sign-replacement.md b/.changeset/dollar-sign-replacement.md
new file mode 100644
index 000000000..5be598ca8
--- /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 000000000..61102c24d
--- /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 4d434ea76..592800a94 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 1b75fc9d2..e6a75054a 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 8b87b9ed7..dc6695fda 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');