This is an automated email from the ASF dual-hosted git repository.
lhotari pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pulsar-site.git
The following commit(s) were added to refs/heads/main by this push:
new e25dc4bac21 Replace scripts/replace.js with a Docusaurus markdown
preprocessor
e25dc4bac21 is described below
commit e25dc4bac21d7f5d4826e672f5ae7d3bcbf95ac2
Author: Lari Hotari <[email protected]>
AuthorDate: Wed Apr 22 15:33:13 2026 +0300
Replace scripts/replace.js with a Docusaurus markdown preprocessor
Move the @pulsar:...@ token expansion and the {@inject:...} link expansion
out of a standalone post-processing script / inline config block into two
focused preprocessor modules under src/server/markdownPreprocessors/. The
new pulsarVariables preprocessor scopes itself by filePath to docs/ and
versioned_docs/version-*/, preserving the exact scope of the old script
while making replacements visible during `yarn start` (previously only
applied in CI). Computed version/URL helpers are centralized in
src/config/pulsarVariables.ts so React components can consume them
directly without needing a separate replacement pass. Drops the
replace-in-file dependency and the node invocation from site_builder.py.
Also adds @pulsar:version:adapters@.
BUILD_ALL_VERSION=1
---
README.md | 15 ++
docs/reference-configuration.md | 2 +-
docusaurus.config.ts | 109 +-------
package.json | 1 -
scripts/replace.js | 291 ---------------------
src/config/pulsarVariables.ts | 192 ++++++++++++++
src/server/markdownPreprocessors/inject.ts | 84 ++++++
.../markdownPreprocessors/pulsarVariables.ts | 24 ++
tools/pytools/lib/execute/site_builder.py | 2 -
.../version-3.0.x/reference-configuration.md | 2 +-
.../version-4.0.x/reference-configuration.md | 2 +-
.../version-4.2.x/reference-configuration.md | 2 +-
yarn.lock | 82 +-----
13 files changed, 333 insertions(+), 475 deletions(-)
diff --git a/README.md b/README.md
index 2267704ceac..454d680c325 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,21 @@ After committing the changes for the `docs` directory, you
can use the `docs-too
./scripts/docs-tool.sh apply_changes_to_versioned_docs
```
+## Markdown placeholders
+
+Markdown files under `docs/` and `versioned_docs/version-*/` are run through
+two preprocessors at build time:
+
+- `@pulsar:version@`, `@pulsar:rpm:client@`, `@pulsar:apidoc:python@`, etc. —
+ version-aware tokens. The values come from `versions.json`,
`site-baseurls.js`,
+ and `data/release-*.js`. See `src/config/pulsarVariables.ts` for the full
list.
+- `{@inject:javadoc:Name:org/...}`, `{@inject:endpoint|GET|/admin/...}` — link
+ expansion for Javadoc, GitHub, REST endpoints. See
+ `src/server/markdownPreprocessors/inject.ts`.
+
+To reference the same values from React components, import from
+`@site/src/config/pulsarVariables` — no placeholder is needed in `.js`/`.tsx`.
+
## More information
* [Pulsar Website contribution
guide](https://pulsar.apache.org/contribute/site-intro/)
diff --git a/docs/reference-configuration.md b/docs/reference-configuration.md
index e60d9b6348a..653a28e5b92 100644
--- a/docs/reference-configuration.md
+++ b/docs/reference-configuration.md
@@ -4,4 +4,4 @@ title: Pulsar Configuration
sidebar_label: "Pulsar Configuration"
---
-For a complete list of Pulsar configuration, visit the [Pulsar
Reference](pathname:///reference/#/@pulsar:version_reference@/) website.
+For a complete list of Pulsar configuration, visit the <a
href="pathname:///reference/#/@pulsar:version_reference@/" target="_blank"
rel="noopener noreferrer">Pulsar Reference</a> website.
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index b9b5592fb5e..0e67aa81706 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -28,99 +28,15 @@ try {
}
const {
- siteUrl,
- javadocUrl,
- restApiUrl,
- functionsApiUrl,
- sourceApiUrl,
- sinkApiUrl,
- packagesApiUrl,
- transactionsApiUrl,
- lookupApiUrl,
- githubUrl,
githubSiteUrl,
baseUrl,
- restApiBaseUrlMapping
+ restApiBaseUrlMapping,
} = require("./site-baseurls");
-const injectLinkParse = (prefix, name, path) => {
- if (prefix == "javadoc") {
- return {
- link: javadocUrl + path,
- text: name,
- };
- } else if (prefix == "github") {
- return {
- link: githubUrl + "/tree/master/" + path,
- text: name,
- };
- } else if (prefix == "rest") {
- return {
- link: restApiUrl + "#" + path,
- text: name,
- };
- } else if (prefix == "functions") {
- return {
- link: functionsApiUrl + "#" + path,
- text: name,
- };
- } else if (prefix == "source") {
- return {
- link: sourceApiUrl + "#" + path,
- text: name,
- };
- } else if (prefix == "sink") {
- return {
- link: sinkApiUrl + "#" + path,
- text: name,
- };
- } else if (prefix == "packages") {
- return {
- link: packagesApiUrl + "#" + path,
- text: name,
- };
- }
-
- return {
- link: path,
- text: name,
- };
-};
-
-const injectLinkParseForEndpoint = (info) => {
- let [method, path, suffix] = info.split("|");
-
- if (!suffix) {
- suffix = "";
- }
-
- let restPath = path.split("/");
- const restApiVersion = restPath[2];
- const restApiType = restPath[3];
- const restBaseUrl = restApiBaseUrlMapping[restApiType] || restApiUrl;
-
- let restUrl;
- if (suffix.indexOf("?version=") >= 0) {
- const suffixAndVersion = suffix.split("?version=")
- restUrl = "version=" + suffixAndVersion[1] + "&apiversion=" +
restApiVersion + "#" + suffixAndVersion[0];
- if (suffixAndVersion[0].startsWith("operation/")) {
- path += suffixAndVersion[0].slice("operation".length)
- }
- } else {
- restUrl = "version=master&apiversion=" + restApiVersion + "#" + suffix;
- if (suffix.startsWith("operation/")) {
- path += suffix.slice("operation".length)
- }
- }
-
- return {
- text: method + " " + path,
- link: restBaseUrl + "?" + restUrl,
- };
-};
-
/** @type {import('@docusaurus/types').Config} */
module.exports = async function createConfigAsync() {
+ const injectPreprocessor = (await
import("./src/server/markdownPreprocessors/inject")).default;
+ const pulsarVariablesPreprocessor = (await
import("./src/server/markdownPreprocessors/pulsarVariables")).default;
return {
future: {
faster: {
@@ -147,21 +63,10 @@ module.exports = async function createConfigAsync() {
hooks: {
onBrokenMarkdownLinks: "warn",
},
- preprocessor: ({ filePath, fileContent }) => {
- return fileContent.replaceAll(/{@inject:([^}]+)}/g, (_, p1) => {
- const p1Trimmed = p1.trim();
- const endpointPrefix = 'endpoint|';
- let link, text;
- if (p1Trimmed.startsWith(endpointPrefix)) {
- // @ts-ignore
- ({ link, text } =
injectLinkParseForEndpoint(p1Trimmed.substring(endpointPrefix.length)));
- } else {
- const [prefix, name, path] = p1Trimmed.split(':');
- ({ link, text } = injectLinkParse(prefix, name, path));
- }
- return `[${text}](${link})`; // Format as a markdown link
- });
- },
+ preprocessor: (args) => injectPreprocessor({
+ ...args,
+ fileContent: pulsarVariablesPreprocessor(args),
+ }),
},
themes: ['@docusaurus/theme-mermaid'],
themeConfig:
diff --git a/package.json b/package.json
index 06dc7dfc0ed..1fb9b729929 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,6 @@
"react-svg": "^16.1.34",
"rehype-katex": "^7.0.1",
"remark-math": "^6.0.0",
- "replace-in-file": "6.3.2",
"search-insights": "^2.17.3",
"semver": "^7.7.4",
"sine-waves": "^0.3.0",
diff --git a/scripts/replace.js b/scripts/replace.js
deleted file mode 100644
index 191c041ab92..00000000000
--- a/scripts/replace.js
+++ /dev/null
@@ -1,291 +0,0 @@
-const replace = require("replace-in-file");
-
-const semver = require("semver");
-const CWD = process.cwd();
-const urlConfig = require("../site-baseurls.js");
-const nextDocsDir = `${CWD}/docs`;
-const docsDir = `${CWD}/versioned_docs`;
-const restApiVersions = require("../static/swagger/restApiVersions.json");
-const compareVersions = require("compare-versions");
-
-function getRealVersion(version) {
- let versionMap = {};
- let _vsGroups = {};
- for (let [key, val] of Object.entries(restApiVersions)) {
- if (key == "master" || compareVersions.compare(key, "2.8.0", "<")) {
- versionMap[key] = key;
- } else {
- let [one, two] = key.split(".");
- let _tKey = one + "." + two + ".x";
- _vsGroups[_tKey] = [...(_vsGroups[_tKey] || []), key];
- }
- }
- for (let [key, val] of Object.entries(_vsGroups)) {
- let _tKey = val.sort((a, b) => {
- return -compareVersions.compare(b, a, "<");
- })[0];
- versionMap[key] = _tKey;
- }
- return versionMap[version] || version;
-}
-
-function downloadPageUrl() {
- return `${urlConfig.baseUrl}download`;
-}
-
-function binaryReleaseUrl(version) {
- return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/apache-pulsar-${version}-bin.tar.gz`;
-}
-
-function connectorReleaseUrl(version) {
- let v = semver.coerce(version);
- if (v.compareMain("2.3.0") >= 0) {
- return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/connectors`;
- } else {
- return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/apache-pulsar-io-connectors-${version}-bin.tar.gz`;
- }
-}
-
-function offloaderReleaseUrl(version) {
- return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/apache-pulsar-offloaders-${version}-bin.tar.gz`;
-}
-
-function prestoPulsarReleaseUrl(version) {
- return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/pulsar-presto-connector-${version}.tar.gz`;
-}
-
-function rpmDistUrl(version, type) {
- let v = semver.coerce(version);
- if (v.compareMain("2.11.0") < 0) {
- let resolvedVersion = version;
- if (v.minor === 8) {
- resolvedVersion = "2.8.4";
- } else if (v.minor === 9) {
- resolvedVersion = "2.9.4";
- } else if (v.minor === 10) {
- resolvedVersion = "2.10.2"
- }
- return
`https://archive.apache.org/dist/pulsar/pulsar-${resolvedVersion}/RPMS/apache-pulsar-client${type}-${resolvedVersion}-1.x86_64.rpm`;
- } else {
- const versions = require(`${CWD}/data/release-cpp`);
- const ver = versions[0].tagName.substring(1);
- return
`https://archive.apache.org/dist/pulsar/pulsar-client-cpp-${ver}/rpm-x86_64/x86_64/apache-pulsar-client${type}-${ver}-1.x86_64.rpm`
- }
-}
-
-function debDistUrl(version, type) {
- let v = semver.coerce(version);
- if (v.compareMain("2.11.0") < 0) {
- let resolvedVersion = version;
- if (v.minor === 8) {
- resolvedVersion = "2.8.4";
- } else if (v.minor === 9) {
- resolvedVersion = "2.9.4";
- } else if (v.minor === 10) {
- resolvedVersion = "2.10.2"
- }
- return
`https://archive.apache.org/dist/pulsar/pulsar-${resolvedVersion}/DEB/apache-pulsar-client${type}.deb`;
- } else {
- const versions = require(`${CWD}/data/release-cpp`);
- const ver = versions[0].tagName.substring(1);
- return
`https://archive.apache.org/dist/pulsar/pulsar-client-cpp-${ver}/deb-x86_64/apache-pulsar-client${type}.deb`
- }
-}
-
-function clientPythonVersion(version) {
- if (version === "2.6.4") {
- return "2.6.3";
- }
- let v = semver.coerce(version);
- if (v.compareMain("2.8.0") < 0) {
- return version;
- }
- if (v.compareMain("2.11.0") < 0) {
- if (v.minor === 8) {
- return "2.8.4";
- } else if (v.minor === 9) {
- return "2.9.4";
- } else if (v.minor === 10) {
- return "2.10.2"
- }
- }
- let versions = require(`${CWD}/data/release-python`);
- return `${versions[0].tagName.substring(1)}`;
-}
-
-function clientPythonVersionUrl(version) {
- if (version === "2.6.4") {
- return `${urlConfig.siteUrl}/api/python/2.6.3`;
- }
- let v = semver.coerce(version);
- if (v.compareMain("2.8.0") < 0) {
- return `${urlConfig.siteUrl}/api/python/${version}`;
- }
- if (v.compareMain("2.11.0") < 0) {
- return `${urlConfig.siteUrl}/api/python/${v.major}.${v.minor}.x`;
- }
- let versions = require(`${CWD}/data/release-python`);
- return `${urlConfig.siteUrl}/api/python/${versions[0].vtag}`;
-}
-
-function clientCPPVersionUrl(version) {
- let v = semver.coerce(version);
- if (v.compareMain("2.8.0") < 0) {
- return `${urlConfig.siteUrl}/api/cpp/${version}`;
- }
- if (v.compareMain("2.11.0") < 0) {
- return `${urlConfig.siteUrl}/api/cpp/${v.major}.${v.minor}.x`;
- }
- let versions = require(`${CWD}/data/release-cpp`);
- return `${urlConfig.siteUrl}/api/cpp/${versions[0].vtag}`;
-}
-
-function javadocVersionUrl(version, type) {
- return `(${urlConfig.siteUrl}/api/${type}/${version}`
-}
-
-function referenceVersion(version) {
- let v = semver.coerce(version);
- if (v.compareMain("2.7.0") < 0) {
- return "2.6.x";
- }
- return `${v.major}.${v.minor}.x`;
-}
-
-function doReplace(options) {
- replace(options)
- .then((changes) => {
- if (options.dry) {
- console.log("Modified files:");
- console.log(changes.join("\n"));
- }
- })
- .catch((error) => {
- console.error("Error occurred:", error);
- });
-}
-
-const versions = JSON.parse(require("fs").readFileSync(`${CWD}/versions.json`,
"utf8"));
-const latestMajorRelease = versions[0];
-const latestVersion = getRealVersion(latestMajorRelease);
-
-const from = [
- /@pulsar:version_number@/g,
- /@pulsar:version@/g,
- /@pulsar:version_origin@/g,
- /@pulsar:version_reference@/g,
- /pulsar:binary_release_url/g,
- /pulsar:connector_release_url/g,
- /pulsar:offloader_release_url/g,
- /pulsar:presto_pulsar_connector_release_url/g,
- /pulsar:download_page_url/g,
- /@pulsar:rpm:client@/g,
- /@pulsar:rpm:client-debuginfo@/g,
- /@pulsar:rpm:client-devel@/g,
- /@pulsar:deb:client@/g,
- /@pulsar:deb:client-devel@/g,
-
- /@pulsar:dist_rpm:client@/g,
- /@pulsar:dist_rpm:client-debuginfo@/g,
- /@pulsar:dist_rpm:client-devel@/g,
- /@pulsar:dist_deb:client@/g,
- /@pulsar:dist_deb:client-devel@/g,
-
- /@pulsar:version:python@/g,
-
- /@pulsar:apidoc:python@/g,
- /@pulsar:apidoc:cpp@/g,
- /\(\/api\/pulsar-functions/g,
- /\(\/api\/client/g,
- /\(\/api\/admin/g,
-
- /@pulsar:version_number@/g,
-
- /\[([^\]]*)\]\((\/tools\/pulsar[^\)]*)\)/g,
-];
-
-const options = {
- files: [`${nextDocsDir}/*.md`, `${nextDocsDir}/**/*.md`],
- from: from,
- to: [
- `${latestVersion}`,
- `${latestVersion}`,
- `${latestVersion}`,
- `next`,
- binaryReleaseUrl(`${latestVersion}`),
- connectorReleaseUrl(`${latestVersion}`),
- offloaderReleaseUrl(`${latestVersion}`),
- prestoPulsarReleaseUrl(`${latestVersion}`),
- downloadPageUrl(),
- rpmDistUrl(`${latestVersion}`, ""),
- rpmDistUrl(`${latestVersion}`, "-debuginfo"),
- rpmDistUrl(`${latestVersion}`, "-devel"),
- debDistUrl(`${latestVersion}`, ""),
- debDistUrl(`${latestVersion}`, "-dev"),
-
- rpmDistUrl(`${latestVersion}`, ""),
- rpmDistUrl(`${latestVersion}`, "-debuginfo"),
- rpmDistUrl(`${latestVersion}`, "-devel"),
- debDistUrl(`${latestVersion}`, ""),
- debDistUrl(`${latestVersion}`, "-dev"),
-
- clientPythonVersion(`${latestMajorRelease}`),
-
- clientPythonVersionUrl(`${latestMajorRelease}`),
- clientCPPVersionUrl(`${latestMajorRelease}`),
- javadocVersionUrl(`${latestMajorRelease}`, "pulsar-functions"),
- javadocVersionUrl(`${latestMajorRelease}`, "client"),
- javadocVersionUrl(`${latestMajorRelease}`, "admin"),
-
- `${latestVersion}`,
-
- '<a href="$2" target="_blank">$1</a>',
- ],
- dry: false,
-};
-
-doReplace(options);
-
-// replaces versions
-for (let _v of versions) {
- const v = getRealVersion(_v)
- const vWithoutIncubating = v.replace("-incubating", "");
- const opts = {
- files: [
- `${docsDir}/version-${_v}/*.md`,
- `${docsDir}/version-${_v}/**/*.md`,
- ],
- from: from,
- to: [
- `${vWithoutIncubating}`,
- `${v}`,
- `${_v}`,
- referenceVersion(v),
- binaryReleaseUrl(`${v}`),
- connectorReleaseUrl(`${v}`),
- offloaderReleaseUrl(`${v}`),
- prestoPulsarReleaseUrl(`${v}`),
- downloadPageUrl(),
- rpmDistUrl(`${v}`, ""),
- rpmDistUrl(`${v}`, "-debuginfo"),
- rpmDistUrl(`${v}`, "-devel"),
- debDistUrl(`${v}`, ""),
- debDistUrl(`${v}`, "-dev"),
- rpmDistUrl(`${v}`, ""),
- rpmDistUrl(`${v}`, "-debuginfo"),
- rpmDistUrl(`${v}`, "-devel"),
- debDistUrl(`${v}`, ""),
- debDistUrl(`${v}`, "-dev"),
- clientPythonVersion(`${v}`),
- clientPythonVersionUrl(`${v}`),
- clientCPPVersionUrl(`${v}`),
- javadocVersionUrl(`${_v}`, "pulsar-functions"),
- javadocVersionUrl(`${_v}`, "client"),
- javadocVersionUrl(`${_v}`, "admin"),
- `${v}`,
- '<a href="$2" target="_blank">$1</a>',
- ],
- dry: false,
- };
- doReplace(opts);
-}
diff --git a/src/config/pulsarVariables.ts b/src/config/pulsarVariables.ts
new file mode 100644
index 00000000000..da6b7d35743
--- /dev/null
+++ b/src/config/pulsarVariables.ts
@@ -0,0 +1,192 @@
+import semver from "semver";
+import * as compareVersions from "compare-versions";
+
+import versionsList from "../../versions.json";
+import restApiVersions from "../../static/swagger/restApiVersions.json";
+
+// CommonJS modules without type declarations — typed inline below.
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const urlConfig: {
+ siteUrl: string;
+ baseUrl: string;
+} = require("../../site-baseurls");
+
+type ClientRelease = {tagName: string; vtag: string};
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const releaseCpp: ClientRelease[] = require("../../data/release-cpp");
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const releasePython: ClientRelease[] = require("../../data/release-python");
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const releasePulsarAdapters: string[] =
require("../../data/release-pulsar-adapters");
+
+export const pulsarAdaptersVersion: string = releasePulsarAdapters[0];
+
+const versions = versionsList as string[];
+
+export const latestMajorRelease: string = versions[0];
+
+export function getRealVersion(version: string): string {
+ const versionMap: Record<string, string> = {};
+ const vsGroups: Record<string, string[]> = {};
+ for (const key of Object.keys(restApiVersions as Record<string, unknown>)) {
+ if (key === "master" || compareVersions.compare(key, "2.8.0", "<")) {
+ versionMap[key] = key;
+ } else {
+ const [major, minor] = key.split(".");
+ const groupKey = `${major}.${minor}.x`;
+ vsGroups[groupKey] = [...(vsGroups[groupKey] || []), key];
+ }
+ }
+ for (const [groupKey, members] of Object.entries(vsGroups)) {
+ const latest = members.sort((a, b) => -compareVersions.compare(b, a,
"<"))[0];
+ versionMap[groupKey] = latest;
+ }
+ return versionMap[version] || version;
+}
+
+export const latestVersion: string = getRealVersion(latestMajorRelease);
+
+export function downloadPageUrl(): string {
+ return `${urlConfig.baseUrl}download`;
+}
+
+export function binaryReleaseUrl(version: string): string {
+ return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/apache-pulsar-${version}-bin.tar.gz`;
+}
+
+export function connectorReleaseUrl(version: string): string {
+ const v = semver.coerce(version)!;
+ if (v.compareMain("2.3.0") >= 0) {
+ return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/connectors`;
+ }
+ return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/apache-pulsar-io-connectors-${version}-bin.tar.gz`;
+}
+
+export function offloaderReleaseUrl(version: string): string {
+ return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/apache-pulsar-offloaders-${version}-bin.tar.gz`;
+}
+
+export function prestoPulsarReleaseUrl(version: string): string {
+ return
`https://archive.apache.org/dist/pulsar/pulsar-${version}/pulsar-presto-connector-${version}.tar.gz`;
+}
+
+// For Pulsar 2.8–2.10 the client binaries are republished under a fixed
+// patch version; newer majors use the pulsar-client-cpp release line instead.
+function legacyDistVersion(version: string): string | null {
+ const v = semver.coerce(version)!;
+ if (v.compareMain("2.11.0") >= 0) return null;
+ if (v.minor === 8) return "2.8.4";
+ if (v.minor === 9) return "2.9.4";
+ if (v.minor === 10) return "2.10.2";
+ return version;
+}
+
+export function rpmDistUrl(version: string, type: string): string {
+ const legacy = legacyDistVersion(version);
+ if (legacy !== null) {
+ return
`https://archive.apache.org/dist/pulsar/pulsar-${legacy}/RPMS/apache-pulsar-client${type}-${legacy}-1.x86_64.rpm`;
+ }
+ const ver = releaseCpp[0].tagName.substring(1);
+ return
`https://archive.apache.org/dist/pulsar/pulsar-client-cpp-${ver}/rpm-x86_64/x86_64/apache-pulsar-client${type}-${ver}-1.x86_64.rpm`;
+}
+
+export function debDistUrl(version: string, type: string): string {
+ const legacy = legacyDistVersion(version);
+ if (legacy !== null) {
+ return
`https://archive.apache.org/dist/pulsar/pulsar-${legacy}/DEB/apache-pulsar-client${type}.deb`;
+ }
+ const ver = releaseCpp[0].tagName.substring(1);
+ return
`https://archive.apache.org/dist/pulsar/pulsar-client-cpp-${ver}/deb-x86_64/apache-pulsar-client${type}.deb`;
+}
+
+export function clientPythonVersion(version: string): string {
+ if (version === "2.6.4") return "2.6.3";
+ const v = semver.coerce(version)!;
+ if (v.compareMain("2.8.0") < 0) return version;
+ if (v.compareMain("2.11.0") < 0) {
+ if (v.minor === 8) return "2.8.4";
+ if (v.minor === 9) return "2.9.4";
+ if (v.minor === 10) return "2.10.2";
+ }
+ return releasePython[0].tagName.substring(1);
+}
+
+export function clientPythonVersionUrl(version: string): string {
+ if (version === "2.6.4") return `${urlConfig.siteUrl}/api/python/2.6.3`;
+ const v = semver.coerce(version)!;
+ if (v.compareMain("2.8.0") < 0) return
`${urlConfig.siteUrl}/api/python/${version}`;
+ if (v.compareMain("2.11.0") < 0) return
`${urlConfig.siteUrl}/api/python/${v.major}.${v.minor}.x`;
+ return `${urlConfig.siteUrl}/api/python/${releasePython[0].vtag}`;
+}
+
+export function clientCPPVersionUrl(version: string): string {
+ const v = semver.coerce(version)!;
+ if (v.compareMain("2.8.0") < 0) return
`${urlConfig.siteUrl}/api/cpp/${version}`;
+ if (v.compareMain("2.11.0") < 0) return
`${urlConfig.siteUrl}/api/cpp/${v.major}.${v.minor}.x`;
+ return `${urlConfig.siteUrl}/api/cpp/${releaseCpp[0].vtag}`;
+}
+
+// The leading `(` is intentional: paired regexes match the `(` head of a
+// markdown link and this replacement preserves it.
+export function javadocVersionUrl(version: string, type: string): string {
+ return `(${urlConfig.siteUrl}/api/${type}/${version}`;
+}
+
+export function referenceVersion(version: string): string {
+ const v = semver.coerce(version)!;
+ if (v.compareMain("2.7.0") < 0) return "2.6.x";
+ return `${v.major}.${v.minor}.x`;
+}
+
+export type Replacement = [RegExp, string];
+
+/**
+ * Ordered [regex, replacement] pairs for a given version key.
+ * `"current"` → next/current docs; any other string is an origin version
+ * from versions.json (e.g. "3.0.x", "2.10.x", "2.6.4").
+ */
+export function resolveTokens(versionKey: string): Replacement[] {
+ const isCurrent = versionKey === "current";
+ const originVersion = isCurrent ? latestMajorRelease : versionKey;
+ const resolvedVersion = getRealVersion(originVersion);
+
+ // Quirks preserved for behavioral compatibility:
+ // - current docs: version_origin = resolvedVersion (not origin)
+ // - versioned docs: version_number strips any -incubating suffix
+ const versionNumber = isCurrent ? resolvedVersion :
resolvedVersion.replace("-incubating", "");
+ const versionOrigin = isCurrent ? resolvedVersion : originVersion;
+ const versionReference = isCurrent ? "next" :
referenceVersion(resolvedVersion);
+ const pythonArg = isCurrent ? originVersion : resolvedVersion;
+
+ const replacements: Replacement[] = [
+ [/@pulsar:version_number@/g, versionNumber],
+ [/@pulsar:version@/g, resolvedVersion],
+ [/@pulsar:version_origin@/g, versionOrigin],
+ [/@pulsar:version_reference@/g, versionReference],
+ [/pulsar:binary_release_url/g, binaryReleaseUrl(resolvedVersion)],
+ [/pulsar:connector_release_url/g, connectorReleaseUrl(resolvedVersion)],
+ [/pulsar:offloader_release_url/g, offloaderReleaseUrl(resolvedVersion)],
+ [/pulsar:presto_pulsar_connector_release_url/g,
prestoPulsarReleaseUrl(resolvedVersion)],
+ [/pulsar:download_page_url/g, downloadPageUrl()],
+ [/@pulsar:rpm:client@/g, rpmDistUrl(resolvedVersion, "")],
+ [/@pulsar:rpm:client-debuginfo@/g, rpmDistUrl(resolvedVersion,
"-debuginfo")],
+ [/@pulsar:rpm:client-devel@/g, rpmDistUrl(resolvedVersion, "-devel")],
+ [/@pulsar:deb:client@/g, debDistUrl(resolvedVersion, "")],
+ [/@pulsar:deb:client-devel@/g, debDistUrl(resolvedVersion, "-dev")],
+ [/@pulsar:dist_rpm:client@/g, rpmDistUrl(resolvedVersion, "")],
+ [/@pulsar:dist_rpm:client-debuginfo@/g, rpmDistUrl(resolvedVersion,
"-debuginfo")],
+ [/@pulsar:dist_rpm:client-devel@/g, rpmDistUrl(resolvedVersion, "-devel")],
+ [/@pulsar:dist_deb:client@/g, debDistUrl(resolvedVersion, "")],
+ [/@pulsar:dist_deb:client-devel@/g, debDistUrl(resolvedVersion, "-dev")],
+ [/@pulsar:version:python@/g, clientPythonVersion(pythonArg)],
+ [/@pulsar:apidoc:python@/g, clientPythonVersionUrl(pythonArg)],
+ [/@pulsar:apidoc:cpp@/g, clientCPPVersionUrl(pythonArg)],
+ [/\(\/api\/pulsar-functions/g, javadocVersionUrl(originVersion,
"pulsar-functions")],
+ [/\(\/api\/client/g, javadocVersionUrl(originVersion, "client")],
+ [/\(\/api\/admin/g, javadocVersionUrl(originVersion, "admin")],
+ [/@pulsar:version_number@/g, resolvedVersion],
+ [/@pulsar:version:adapters@/g, pulsarAdaptersVersion]
+ ];
+
+ return replacements;
+}
diff --git a/src/server/markdownPreprocessors/inject.ts
b/src/server/markdownPreprocessors/inject.ts
new file mode 100644
index 00000000000..19ea2515484
--- /dev/null
+++ b/src/server/markdownPreprocessors/inject.ts
@@ -0,0 +1,84 @@
+// Expands {@inject:...} tokens in markdown source into markdown links.
+
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const urlConfig: {
+ javadocUrl: string;
+ restApiUrl: string;
+ functionsApiUrl: string;
+ sourceApiUrl: string;
+ sinkApiUrl: string;
+ packagesApiUrl: string;
+ githubUrl: string;
+ restApiBaseUrlMapping: Record<string, string>;
+} = require("../../../site-baseurls");
+
+const {
+ javadocUrl,
+ restApiUrl,
+ functionsApiUrl,
+ sourceApiUrl,
+ sinkApiUrl,
+ packagesApiUrl,
+ githubUrl,
+ restApiBaseUrlMapping,
+} = urlConfig;
+
+function injectLinkParse(prefix: string, name: string, path: string): {link:
string; text: string} {
+ if (prefix === "javadoc") return {link: javadocUrl + path, text: name};
+ if (prefix === "github") return {link: githubUrl + "/tree/master/" + path,
text: name};
+ if (prefix === "rest") return {link: restApiUrl + "#" + path, text: name};
+ if (prefix === "functions") return {link: functionsApiUrl + "#" + path,
text: name};
+ if (prefix === "source") return {link: sourceApiUrl + "#" + path, text:
name};
+ if (prefix === "sink") return {link: sinkApiUrl + "#" + path, text: name};
+ if (prefix === "packages") return {link: packagesApiUrl + "#" + path, text:
name};
+ return {link: path, text: name};
+}
+
+function injectLinkParseForEndpoint(info: string): {text: string; link:
string} {
+ const parts = info.split("|");
+ const method = parts[0];
+ let path = parts[1];
+ const suffix = parts[2] || "";
+
+ const restPath = path.split("/");
+ const restApiVersion = restPath[2];
+ const restApiType = restPath[3];
+ const restBaseUrl = restApiBaseUrlMapping[restApiType] || restApiUrl;
+
+ let restUrl: string;
+ if (suffix.indexOf("?version=") >= 0) {
+ const [suffixPart, versionPart] = suffix.split("?version=");
+ restUrl =
`version=${versionPart}&apiversion=${restApiVersion}#${suffixPart}`;
+ if (suffixPart.startsWith("operation/")) {
+ path += suffixPart.slice("operation".length);
+ }
+ } else {
+ restUrl = `version=master&apiversion=${restApiVersion}#${suffix}`;
+ if (suffix.startsWith("operation/")) {
+ path += suffix.slice("operation".length);
+ }
+ }
+
+ return {
+ text: `${method} ${path}`,
+ link: `${restBaseUrl}?${restUrl}`,
+ };
+}
+
+type Args = {filePath: string; fileContent: string};
+
+export default function injectPreprocessor({fileContent}: Args): string {
+ return fileContent.replaceAll(/{@inject:([^}]+)}/g, (_match, p1: string) => {
+ const trimmed = p1.trim();
+ const endpointPrefix = "endpoint|";
+ let link: string;
+ let text: string;
+ if (trimmed.startsWith(endpointPrefix)) {
+ ({link, text} =
injectLinkParseForEndpoint(trimmed.substring(endpointPrefix.length)));
+ } else {
+ const [prefix, name, path] = trimmed.split(":");
+ ({link, text} = injectLinkParse(prefix, name, path));
+ }
+ return `[${text}](${link})`;
+ });
+}
diff --git a/src/server/markdownPreprocessors/pulsarVariables.ts
b/src/server/markdownPreprocessors/pulsarVariables.ts
new file mode 100644
index 00000000000..ca520eba833
--- /dev/null
+++ b/src/server/markdownPreprocessors/pulsarVariables.ts
@@ -0,0 +1,24 @@
+import {resolveTokens} from "../../config/pulsarVariables";
+
+// Only docs/**/*.md and versioned_docs/version-*/**/*.md are rewritten.
+// Blog posts, src/pages/*.mdx, and the auxiliary content-docs plugins
+// (contribute/release-notes/security/client-feature-matrix) are skipped so
+// the link-targeting regexes (e.g. `[text](/tools/pulsar...)`) can't rewrite
+// legitimately-authored markdown links.
+const SCOPE_RE =
/(?:^|[/\\])(?:docs|versioned_docs[/\\]version-[^/\\]+)[/\\].*\.md$/;
+const VERSIONED_RE = /(?:^|[/\\])versioned_docs[/\\]version-([^/\\]+)[/\\]/;
+
+type Args = {filePath: string; fileContent: string};
+
+export default function pulsarVariablesPreprocessor({filePath, fileContent}:
Args): string {
+ if (!SCOPE_RE.test(filePath)) {
+ return fileContent;
+ }
+ const versionMatch = filePath.match(VERSIONED_RE);
+ const versionKey = versionMatch ? versionMatch[1] : "current";
+ let result = fileContent;
+ for (const [regex, replacement] of resolveTokens(versionKey)) {
+ result = result.replace(regex, replacement);
+ }
+ return result;
+}
diff --git a/tools/pytools/lib/execute/site_builder.py
b/tools/pytools/lib/execute/site_builder.py
index 6c8fa4819cb..95595ac2c89 100644
--- a/tools/pytools/lib/execute/site_builder.py
+++ b/tools/pytools/lib/execute/site_builder.py
@@ -35,10 +35,8 @@ def execute(asf_site: Path):
# # 2. Install and build
yarn = find_command('yarn', msg="yarn is required")
- node = find_command('node', msg="node is required")
bash = find_command('bash', msg="bash is required")
run(yarn, 'install', cwd=site_path())
- run(node, 'scripts/replace.js', cwd=site_path())
run(bash, 'scripts/split-version-build.sh', *modified_files,
cwd=site_path())
latest_content = site_path() / 'build'
diff --git a/versioned_docs/version-3.0.x/reference-configuration.md
b/versioned_docs/version-3.0.x/reference-configuration.md
index e60d9b6348a..653a28e5b92 100644
--- a/versioned_docs/version-3.0.x/reference-configuration.md
+++ b/versioned_docs/version-3.0.x/reference-configuration.md
@@ -4,4 +4,4 @@ title: Pulsar Configuration
sidebar_label: "Pulsar Configuration"
---
-For a complete list of Pulsar configuration, visit the [Pulsar
Reference](pathname:///reference/#/@pulsar:version_reference@/) website.
+For a complete list of Pulsar configuration, visit the <a
href="pathname:///reference/#/@pulsar:version_reference@/" target="_blank"
rel="noopener noreferrer">Pulsar Reference</a> website.
diff --git a/versioned_docs/version-4.0.x/reference-configuration.md
b/versioned_docs/version-4.0.x/reference-configuration.md
index e60d9b6348a..653a28e5b92 100644
--- a/versioned_docs/version-4.0.x/reference-configuration.md
+++ b/versioned_docs/version-4.0.x/reference-configuration.md
@@ -4,4 +4,4 @@ title: Pulsar Configuration
sidebar_label: "Pulsar Configuration"
---
-For a complete list of Pulsar configuration, visit the [Pulsar
Reference](pathname:///reference/#/@pulsar:version_reference@/) website.
+For a complete list of Pulsar configuration, visit the <a
href="pathname:///reference/#/@pulsar:version_reference@/" target="_blank"
rel="noopener noreferrer">Pulsar Reference</a> website.
diff --git a/versioned_docs/version-4.2.x/reference-configuration.md
b/versioned_docs/version-4.2.x/reference-configuration.md
index e60d9b6348a..653a28e5b92 100644
--- a/versioned_docs/version-4.2.x/reference-configuration.md
+++ b/versioned_docs/version-4.2.x/reference-configuration.md
@@ -4,4 +4,4 @@ title: Pulsar Configuration
sidebar_label: "Pulsar Configuration"
---
-For a complete list of Pulsar configuration, visit the [Pulsar
Reference](pathname:///reference/#/@pulsar:version_reference@/) website.
+For a complete list of Pulsar configuration, visit the <a
href="pathname:///reference/#/@pulsar:version_reference@/" target="_blank"
rel="noopener noreferrer">Pulsar Reference</a> website.
diff --git a/yarn.lock b/yarn.lock
index 0fa745a0ce9..a1f16c12cc3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10220,13 +10220,6 @@ __metadata:
languageName: node
linkType: hard
-"fs.realpath@npm:^1.0.0":
- version: 1.0.0
- resolution: "fs.realpath@npm:1.0.0"
- checksum:
99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0
- languageName: node
- linkType: hard
-
"fsevents@npm:~2.3.2":
version: 2.3.3
resolution: "fsevents@npm:2.3.3"
@@ -10465,20 +10458,6 @@ __metadata:
languageName: node
linkType: hard
-"glob@npm:^7.2.0":
- version: 7.2.3
- resolution: "glob@npm:7.2.3"
- dependencies:
- fs.realpath: ^1.0.0
- inflight: ^1.0.4
- inherits: 2
- minimatch: ^3.1.1
- once: ^1.3.0
- path-is-absolute: ^1.0.0
- checksum:
29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133
- languageName: node
- linkType: hard
-
"global-dirs@npm:^3.0.0":
version: 3.0.1
resolution: "global-dirs@npm:3.0.1"
@@ -11302,30 +11281,20 @@ __metadata:
languageName: node
linkType: hard
-"inflight@npm:^1.0.4":
- version: 1.0.6
- resolution: "inflight@npm:1.0.6"
- dependencies:
- once: ^1.3.0
- wrappy: 1
- checksum:
f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd
+"inherits@npm:2.0.3":
+ version: 2.0.3
+ resolution: "inherits@npm:2.0.3"
+ checksum:
78cb8d7d850d20a5e9a7f3620db31483aa00ad5f722ce03a55b110e5a723539b3716a3b463e2b96ce3fe286f33afc7c131fa2f91407528ba80cea98a7545d4c0
languageName: node
linkType: hard
-"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3,
inherits@npm:~2.0.3, inherits@npm:~2.0.4":
+"inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:~2.0.3,
inherits@npm:~2.0.4":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum:
4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1
languageName: node
linkType: hard
-"inherits@npm:2.0.3":
- version: 2.0.3
- resolution: "inherits@npm:2.0.3"
- checksum:
78cb8d7d850d20a5e9a7f3620db31483aa00ad5f722ce03a55b110e5a723539b3716a3b463e2b96ce3fe286f33afc7c131fa2f91407528ba80cea98a7545d4c0
- languageName: node
- linkType: hard
-
"ini@npm:2.0.0":
version: 2.0.0
resolution: "ini@npm:2.0.0"
@@ -13834,7 +13803,7 @@ __metadata:
languageName: node
linkType: hard
-"minimatch@npm:3.1.5, minimatch@npm:^3.1.1":
+"minimatch@npm:3.1.5":
version: 3.1.5
resolution: "minimatch@npm:3.1.5"
dependencies:
@@ -14529,15 +14498,6 @@ __metadata:
languageName: node
linkType: hard
-"once@npm:^1.3.0":
- version: 1.4.0
- resolution: "once@npm:1.4.0"
- dependencies:
- wrappy: 1
- checksum:
cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68
- languageName: node
- linkType: hard
-
"onetime@npm:^5.1.2":
version: 5.1.2
resolution: "onetime@npm:5.1.2"
@@ -14846,13 +14806,6 @@ __metadata:
languageName: node
linkType: hard
-"path-is-absolute@npm:^1.0.0":
- version: 1.0.1
- resolution: "path-is-absolute@npm:1.0.1"
- checksum:
060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8
- languageName: node
- linkType: hard
-
"path-is-inside@npm:1.0.2":
version: 1.0.2
resolution: "path-is-inside@npm:1.0.2"
@@ -17130,19 +17083,6 @@ __metadata:
languageName: node
linkType: hard
-"replace-in-file@npm:6.3.2":
- version: 6.3.2
- resolution: "replace-in-file@npm:6.3.2"
- dependencies:
- chalk: ^4.1.2
- glob: ^7.2.0
- yargs: ^17.2.1
- bin:
- replace-in-file: bin/cli.js
- checksum:
ae3a0486711edfc1d7d769782764902934aeb327e54a56fbf8a92df22862a56312d86dbe0067274fb3666da668457576f6a775f814742acceea70f5aedb01f49
- languageName: node
- linkType: hard
-
"require-directory@npm:^2.1.1":
version: 2.1.1
resolution: "require-directory@npm:2.1.1"
@@ -19614,7 +19554,6 @@ __metadata:
react-svg: ^16.1.34
rehype-katex: ^7.0.1
remark-math: ^6.0.0
- replace-in-file: 6.3.2
search-insights: ^2.17.3
semver: ^7.7.4
sine-waves: ^0.3.0
@@ -19776,13 +19715,6 @@ __metadata:
languageName: node
linkType: hard
-"wrappy@npm:1":
- version: 1.0.2
- resolution: "wrappy@npm:1.0.2"
- checksum:
159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5
- languageName: node
- linkType: hard
-
"write-file-atomic@npm:^3.0.3":
version: 3.0.3
resolution: "write-file-atomic@npm:3.0.3"
@@ -19912,7 +19844,7 @@ __metadata:
languageName: node
linkType: hard
-"yargs@npm:^17.0.0, yargs@npm:^17.2.1":
+"yargs@npm:^17.0.0":
version: 17.7.2
resolution: "yargs@npm:17.7.2"
dependencies: