From e034633c4d8c6564f7bd4c6697615bd747ad33e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E6=98=8C=E7=91=9E?= Date: Thu, 18 Jun 2026 11:29:22 +0800 Subject: [PATCH] fix(region): rewrite all .com hostnames to .cn in cn build artifacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the region URL rewriting mechanism so the dist of `build:cn` contains zero `open.longbridge.com` / `openapi.longbridge.com` strings — not just rendered HTML, but also raw `.md` copies, `llms.txt`, install scripts, and Vite-compiled JS bundles. - region-utils: extract shared `buildRegionUrlReplacements()` that emits both protocol-prefixed (URL) and bare-hostname (link text) pairs for site + API hostnames. - region-filter / config.mts (transformHtml & buildEnd): use the shared helper; covers markdown tokens, final HTML, install scripts. - config.mts: new `region-source-url-rewrite` Vite plugin (`enforce: 'pre'`) that rewrites hardcoded hostnames in .vue/.ts/.json/.yaml source modules before they are bundled — fixes leaks in app.js (install command strings, mcp-tools.json connect links, openapi.yaml error messages). - normalize_md: apply the same rewrite to .md copies written into dist (the real source of `skill/install.md` etc.). - generate-llms: rewrite the static llms-intro.md before injecting into llms.txt. - package.json build:cn: pass `VITE_REGION=cn` to the `bun run build:llms` segment too — previously cross-env only scoped to vitepress build, so normalize_md/generate-llms never knew it was a cn build. Global build (build:release) is unaffected: with no VITE_REGION the shared helper returns [] and every call site is a no-op. --- docs/.vitepress/config.mts | 42 +++++++++++++-------- docs/.vitepress/md-plugins/region-filter.ts | 12 ++---- docs/.vitepress/region-utils.ts | 20 ++++++++++ package.json | 2 +- scripts/generate-llms.ts | 12 +++++- scripts/normalize_md.ts | 11 ++++++ 6 files changed, 73 insertions(+), 26 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 0f52e23d..674910d6 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -9,7 +9,7 @@ import { execSync } from 'node:child_process' import { localesConfig } from './config/locales' import { withMermaid } from 'vitepress-plugin-mermaid' import { rewriteMarkdownPath } from './utils' -import { getRegionConfig, computeSrcExclude } from './region-utils' +import { getRegionConfig, computeSrcExclude, buildRegionUrlReplacements } from './region-utils' import * as cheerio from 'cheerio' import yaml from 'js-yaml' @@ -49,12 +49,9 @@ export default defineConfig( markdown: markdownConfig, transformHtml(code) { let html = insertScript(code) - // Region URL rewriting: replace global hostnames (site + API) with region hostnames in final HTML - if (regionCfg?.siteHostname && regionCfg.siteHostname !== 'https://open.longbridge.com') { - html = html.split('https://open.longbridge.com').join(regionCfg.siteHostname) - } - if (regionCfg?.apiBaseUrl && regionCfg.apiBaseUrl !== 'https://openapi.longbridge.com') { - html = html.split('https://openapi.longbridge.com').join(regionCfg.apiBaseUrl) + // Region URL rewriting: replace global hostnames (URL form + bare-text form) with region hostnames in final HTML + for (const [from, to] of buildRegionUrlReplacements()) { + html = html.split(from).join(to) } return html }, @@ -130,14 +127,8 @@ export default defineConfig( rmSync(tmpDir, { recursive: true, force: true }) - // Region URL rewriting for static assets (site + API hostnames) - const staticReplacements: [string, string][] = [] - if (regionCfg?.siteHostname && regionCfg.siteHostname !== 'https://open.longbridge.com') { - staticReplacements.push(['https://open.longbridge.com', regionCfg.siteHostname]) - } - if (regionCfg?.apiBaseUrl && regionCfg.apiBaseUrl !== 'https://openapi.longbridge.com') { - staticReplacements.push(['https://openapi.longbridge.com', regionCfg.apiBaseUrl]) - } + // Region URL rewriting for static assets (site + API hostnames, URL + bare-text form) + const staticReplacements = buildRegionUrlReplacements() if (staticReplacements.length > 0) { const installDir = resolve(siteConfig.outDir, 'longbridge-terminal') for (const file of ['install', 'install.ps1']) { @@ -265,6 +256,27 @@ export default defineConfig( return { code: `export default ${JSON.stringify(data)}`, map: null } }, }, + { + // Rewrite hardcoded global hostnames in source modules (Vue / TS / JSON) for region builds. + // markdown-it plugin + transformHtml don't reach Vite-compiled string literals. + name: 'region-source-url-rewrite', + enforce: 'pre' as const, + transform(src: string, id: string) { + if (id.includes('node_modules')) return + if (!/\.(vue|ts|tsx|js|jsx|json|ya?ml)(\?|$)/.test(id)) return + const replacements = buildRegionUrlReplacements() + if (replacements.length === 0) return + let code = src + let changed = false + for (const [from, to] of replacements) { + if (code.includes(from)) { + code = code.split(from).join(to) + changed = true + } + } + return changed ? { code, map: null } : undefined + }, + }, { name: 'fetch-mcp-tools', async buildStart() { diff --git a/docs/.vitepress/md-plugins/region-filter.ts b/docs/.vitepress/md-plugins/region-filter.ts index 014be8d4..f4766ce8 100644 --- a/docs/.vitepress/md-plugins/region-filter.ts +++ b/docs/.vitepress/md-plugins/region-filter.ts @@ -1,7 +1,7 @@ import type MarkdownItAsync from 'markdown-it' import type Token from 'markdown-it/lib/token.mjs' import picomatch from 'picomatch' -import { getRegionConfig } from '../region-utils' +import { getRegionConfig, buildRegionUrlReplacements } from '../region-utils' /** * Markdown-it plugin that removes sections (heading + content) based on region config. @@ -11,14 +11,8 @@ export function RegionFilterPlugin(md: MarkdownItAsync) { const config = getRegionConfig() if (!config) return - // URL rewriting: replace global hostnames (site + API) with region hostnames in all inline tokens - const urlReplacements: [string, string][] = [] - if (config.siteHostname && config.siteHostname !== 'https://open.longbridge.com') { - urlReplacements.push(['https://open.longbridge.com', config.siteHostname]) - } - if (config.apiBaseUrl && config.apiBaseUrl !== 'https://openapi.longbridge.com') { - urlReplacements.push(['https://openapi.longbridge.com', config.apiBaseUrl]) - } + // URL rewriting: replace global hostnames (site + API, both URL and bare-text form) with region hostnames + const urlReplacements = buildRegionUrlReplacements() if (urlReplacements.length > 0) { md.core.ruler.push('region_url_rewrite', (state) => { for (const token of state.tokens) { diff --git a/docs/.vitepress/region-utils.ts b/docs/.vitepress/region-utils.ts index 590281e0..0d56d4a5 100644 --- a/docs/.vitepress/region-utils.ts +++ b/docs/.vitepress/region-utils.ts @@ -15,6 +15,26 @@ export function getRegionConfig(): RegionConfig | undefined { return regionConfig[region] } +/** + * URL replacements for region-aware rewriting. + * Returns both `https://`-prefixed (URLs) and bare hostname (link display text / inline mentions) pairs. + * Order is significant: protocol-prefixed rules first so bare rules don't double-match. + */ +export function buildRegionUrlReplacements(): [string, string][] { + const config = getRegionConfig() + if (!config) return [] + const pairs: [string, string][] = [] + if (config.siteHostname && config.siteHostname !== 'https://open.longbridge.com') { + pairs.push(['https://open.longbridge.com', config.siteHostname]) + pairs.push(['open.longbridge.com', config.siteHostname.replace(/^https?:\/\//, '')]) + } + if (config.apiBaseUrl && config.apiBaseUrl !== 'https://openapi.longbridge.com') { + pairs.push(['https://openapi.longbridge.com', config.apiBaseUrl]) + pairs.push(['openapi.longbridge.com', config.apiBaseUrl.replace(/^https?:\/\//, '')]) + } + return pairs +} + /** * Check if a page path is included in the current region's whitelist. * When no region is set (global build), all pages are included. diff --git a/package.json b/package.json index f4453551..5af03f1f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dev:cn": "cross-env VITE_REGION=cn VITE_API_BASE_URL=https://openapi.longbridge.cn VITE_SITE_HOSTNAME=https://open.longbridge.cn npx vitepress dev docs", "build:canary": "cross-env \"NODE_OPTIONS=--max-old-space-size=12288 --expose-gc\" PROXY=canary VITE_API_BASE_URL=https://openapi.longbridge.xyz npx vitepress build docs && bun run build:llms && bun run build:copy-routes", "build:release": "cross-env \"NODE_OPTIONS=--max-old-space-size=12288 --expose-gc\" VITE_API_BASE_URL=https://openapi.longbridge.com npx vitepress build docs && bun run build:llms && bun run build:copy-routes", - "build:cn": "cross-env \"NODE_OPTIONS=--max-old-space-size=12288 --expose-gc\" VITE_REGION=cn VITE_API_BASE_URL=https://openapi.longbridge.cn VITE_PORTAL_GATEWAY_BASE_URL=https://m.lbkrs.com VITE_SITE_HOSTNAME=https://open.longbridge.cn npx vitepress build docs && bun run build:llms && bun run build:copy-routes", + "build:cn": "cross-env \"NODE_OPTIONS=--max-old-space-size=12288 --expose-gc\" VITE_REGION=cn VITE_API_BASE_URL=https://openapi.longbridge.cn VITE_PORTAL_GATEWAY_BASE_URL=https://m.lbkrs.com VITE_SITE_HOSTNAME=https://open.longbridge.cn npx vitepress build docs && cross-env VITE_REGION=cn bun run build:llms && bun run build:copy-routes", "build:llms": "bun run scripts/normalize_md.ts && bun run scripts/generate-llms.ts", "build:copy-routes": "bun run scripts/copy-routes.ts", "preview": "vitepress preview docs", diff --git a/scripts/generate-llms.ts b/scripts/generate-llms.ts index 060aff02..8d8559c9 100644 --- a/scripts/generate-llms.ts +++ b/scripts/generate-llms.ts @@ -1,7 +1,16 @@ import fs from 'fs' import path from 'path' import matter from 'gray-matter' -import { getRegionConfig } from '../docs/.vitepress/region-utils' +import { getRegionConfig, buildRegionUrlReplacements } from '../docs/.vitepress/region-utils' + +const regionUrlReplacements = buildRegionUrlReplacements() + +function applyRegionUrlRewrite(content: string): string { + for (const [from, to] of regionUrlReplacements) { + content = content.split(from).join(to) + } + return content +} // Simple capitalize function to replace lodash function capitalize(str: string): string { @@ -229,6 +238,7 @@ function generateLLMSTxt(): void { // Extract content from index.md let introContent = fs.readFileSync(path.join(__dirname, './llms-intro.md'), 'utf8') + introContent = applyRegionUrlRewrite(introContent) content = `${introContent}\n\n## SDK \n\n${markdownList}` diff --git a/scripts/normalize_md.ts b/scripts/normalize_md.ts index 99ba88c5..6b19f423 100644 --- a/scripts/normalize_md.ts +++ b/scripts/normalize_md.ts @@ -2,6 +2,7 @@ import fs from 'fs-extra' import path from 'path' import { globSync } from 'glob' import { rewriteMarkdownPath } from '../docs/.vitepress/utils' +import { buildRegionUrlReplacements } from '../docs/.vitepress/region-utils' import { QUOTE_COMMANDS, QUOTE_PERMISSION_TITLE, @@ -16,6 +17,15 @@ import { type Locale = QuoteLocale +const regionUrlReplacements = buildRegionUrlReplacements() + +function applyRegionUrlRewrite(content: string): string { + for (const [from, to] of regionUrlReplacements) { + content = content.split(from).join(to) + } + return content +} + /** * Process Markdown file * @param filePath file path @@ -29,6 +39,7 @@ function processMarkdownFile(filePath: string, outputPath: string, locale: Local // Need to implement regex to match tags and replace with Markdown tables content = parseSDKLinks(content, locale) content = parseQuotePermission(content, locale) + content = applyRegionUrlRewrite(content) const relativePath = path.relative(docsDir, filePath)