Files
longbridge__developers/package.json
hold-baby b952761fa6 Complete .com→.cn hostname rewrite for CN region build (#1100)
## Merge Verdict
**[APPROVE]** — Region URL rewriting is now complete across every
artifact channel; global build verified untouched.
> 6 files · +73 / -26 · `docs/.vitepress` `scripts/` `package.json`

---
## Summary
- Extract a shared `buildRegionUrlReplacements()` in `region-utils.ts`
that emits **four** rules per non-default hostname (protocol-prefixed +
bare-text for both `siteHostname` and `apiBaseUrl`), and route the four
pre-existing rewrite sites (`region-filter.ts`, `transformHtml`,
`buildEnd install`, plus the new ones) through it.
- Add a new Vite `region-source-url-rewrite` plugin (`enforce: 'pre'`)
that rewrites hardcoded hostnames inside `.vue/.ts/.json/.yaml` source
modules — this is the only channel that reaches Vite-compiled JS bundles
(install command strings in Vue components, `mcp-tools.json` connect
links, `openapi.yaml` error-message text).
- Bring `.md` copies + `llms.txt` into region rewriting:
`normalize_md.ts` (the real writer of `dist/**/*.md`) and
`generate-llms.ts` (the static `llms-intro.md` injection point) now
share the same helper.
- Fix root-cause env-leak: `build:cn` now also passes `VITE_REGION=cn`
to the `bun run build:llms` segment — previously `cross-env` only scoped
to the `vitepress build` process, so `normalize_md`/`generate-llms`
never knew it was a CN build and silently produced `.com` artifacts.

---
## Risk Analysis
| Risk | Level | Mitigation |
|------|-------|-----------|
| Global build (`build:release`) accidentally rewritten |  | Shared
helper returns `[]` when `VITE_REGION` is unset → every call site is a
no-op. Verified: `dist/longbridge-terminal/install.ps1` is
byte-identical to source; `mcp.html` keeps all 9 `.com`; `llms-full.txt`
keeps all 112 `open.longbridge.com`. |
| Bare-rule replacement double-matches |  | `open.longbridge.com` is
**not** a substring of `openapi.longbridge.com` (5th char `.` vs `a`),
so the four rules are mutually independent regardless of order. |
| Vite `transform` runs on every module — performance hit | 🟢 |
`buildRegionUrlReplacements()` is lightweight (one env read + small
array build). On global builds it short-circuits via
`replacements.length === 0`. |
| `enforce: 'pre'` ordering vs `yaml-transform` |  | `'pre'` plugins
run before normal plugins, so this transform sees raw YAML text and
rewrites it before `yaml-transform` JSON-stringifies it. |
| Hardcoded global hostnames in helper | 🟡 | Helper compares against the
literal `'https://open.longbridge.com'` /
`'https://openapi.longbridge.com'`. If the global domain ever changes,
this file plus `region.config.ts` must be updated together. Same
constraint already existed before this PR. |

---
## Design Decisions
- **Centralize rules in `region-utils.ts`** instead of inlining at four
call sites — four sites already drifted (HTML had two rules but markdown
had only the URL form before the previous PR). One source of truth
prevents future drift.
- **Pre-stage Vite transform** rather than a post-build dist scan —
keeps source maps intact and lets the rewrite participate in dependency
invalidation. It also naturally covers `openapi.yaml` (huge but fine —
string `split/join` is O(n) and only runs once per module per build).
- **Bare-hostname rules alongside URL rules** — covers
`[open.longbridge.com/connect](https://...)` markdown patterns where
only the link target gets matched by URL rules; the display text needs
the bare-host rule.
- **Source `install` / `install.ps1` keep `.com`** — global build's
`buildEnd` already had a rewrite pass; making source `.com`-default lets
the existing rewrite mechanism do the work and avoids two
source-of-truth files.

---
## Code Notes
1. **[Info]** `region-utils.ts:25` comment "first so bare rules don't
double-match"
In practice both orderings are correct because after either rule runs
the other one's "from" string no longer exists in the result. The note
is defensive rather than load-bearing.
   — Author note: deferred to next iteration.
2. **[Info]** `config.mts` Vite transform hook calls
`buildRegionUrlReplacements()` per module
The helper is cheap but is invoked once per source module on every
build. Could be hoisted to the closure top if profiling ever flags it;
not worth the structural change today.
   — Author note: deferred to next iteration.
3. **[Info]** `package.json` build:cn duplicates `cross-env
VITE_REGION=cn` across two segments
Maintainable but easy to forget if a third stage is added later. Could
be solved with `cross-env-shell` wrapping the whole chain, but that's a
separate cleanup.
   — Author note: deferred to next iteration.
4. **[Needs review]** Vite `transform` regex includes `.yaml`/`.yml`
This is intentional — `openapi.yaml` ships hardcoded
`https://open.longbridge.com/sdk` and error-message URLs that must be
rewritten for CN. Reviewer should confirm there's no other YAML in the
dependency graph whose `.com` strings must be preserved as global
references. None observed in the current tree.

---
## Verification
-  `bun run build:cn` succeeds; `rg -l
'(open|openapi)\.longbridge\.com' docs/.vitepress/dist` → **zero
residual `.com`** across HTML/MD/JS/scripts.
-  `bun run build:release` succeeds; `install.ps1` and `install` are
byte-identical to source; `mcp.html` keeps 9× `.com`; `llms-full.txt`
keeps 112× `open.longbridge.com`; CN endpoint mentions inside docs
(`getting-started.md` etc.) are preserved as intended.
-  `openapi-quote.longbridge.cn` / `openapi-trade.longbridge.cn` counts
in `getting-started.html` match source 1:1 (no over-rewrite).
- 📋 Reviewer to confirm: CN site (`open.longbridge.cn`) renders `mcp.md`
/ `skill/install` pages with the new URLs after deploy.

Co-authored-by: 袁昌瑞 <changrui.yuan@longbridge-inc.com>
2026-06-18 11:37:13 +08:00

77 lines
3.1 KiB
JSON

{
"name": "openapi-website",
"version": "0.0.1",
"author": "longbridge",
"module": "index.ts",
"description": "this is a website of longbridge developers",
"license": "MIT",
"private": true,
"scripts": {
"dev": "vitepress dev docs",
"dev:canary": "cross-env PROXY=canary VITE_API_BASE_URL=https://openapi.longbridge.xyz npx vitepress dev docs",
"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 && 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",
"lint:docs": "prettier -c --parser typescript \"packages/**/*.{js,ts,vue}\"",
"format:docs": "prettier --write \"packages/**/*.{js,ts,vue}\""
},
"type": "module",
"dependencies": {
"@headlessui/vue": "^1.7.23",
"@inspira-ui/plugins": "^0.0.2",
"@jsonforms/core": "^3.5.1",
"@jsonforms/vue": "^3.5.1",
"@jsonforms/vue-vanilla": "^3.5.1",
"@tailwindcss/postcss": "^4.2.2",
"@vue-flow/background": "^1.3.2",
"@vue-flow/core": "^1.48.2",
"@vueuse/core": "^14.2.1",
"cheerio": "^1.1.0",
"floating-vue": "^5.2.2",
"fs-extra": "^11.3.0",
"glob": "^11.0.3",
"gray-matter": "^4.0.3",
"js-yaml": "^4.1.1",
"markdown-it": "^14.1.1",
"motion-v": "^2.2.0",
"qrcode": "^1.5.4",
"reka-ui": "^2.9.6",
"shiki": "^3.6.0",
"tailwindcss": "^4.2.2",
"tw-animate-css": "^1.4.0",
"ufo": "^1.6.1",
"vite": "^8.0.0",
"vue-i18n": "11"
},
"devDependencies": {
"@types/bun": "latest",
"@types/fs-extra": "^11.0.4",
"@types/js-yaml": "^4.0.9",
"@types/qrcode": "^1.5.6",
"@unocss/extractor-mdc": "^66.1.2",
"@unocss/transformer-variant-group": "^66.1.2",
"cross-env": "^7.0.3",
"markdown-it-container": "^4.0.0",
"markdown-it-mathjax3": "^4.3.2",
"mermaid": "^11.8.1",
"postcss-rtlcss": "^5.7.0",
"prettier": "^3.5.3",
"sass-embedded": "^1.89.2",
"unocss": "^66.1.2",
"vitepress": "2.0.0-alpha.16",
"vitepress-plugin-group-icons": "^1.5.2",
"vitepress-plugin-mermaid": "^2.0.17",
"vue": "^3.5.13"
},
"peerDependencies": {
"typescript": "^5"
},
"engines": {
"node": ">=24.0.0"
}
}