This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-website.git


The following commit(s) were added to refs/heads/master by this push:
     new f182044ff23c [build](i18n) Isolate native compiler memory without 
changing site output (#4537)
f182044ff23c is described below

commit f182044ff23c1fb0c4a8d5d3016857d8a2ddd114
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Wed Oct 28 00:24:50 2026 +0000

    [build](i18n) Isolate native compiler memory without changing site output 
(#4537)
    
    The full five-language production build reached 14.36 GiB RSS and left
    only about 344 MiB of available RAM, despite a 6 GiB V8 heap limit. The
    peak occurs while Docusaurus runs its client and server Rspack compilers
    together; closed compilers in the pinned native implementation remain
    reachable.
    
    Build the client in a disposable process, wait for its successful exit,
    then compile the server and render pages in the locale process. Transfer
    the exact client manifest and client MDX results with checksums and
    source-stamp checks. This preserves Docusaurus 3.6.3's client-derived
    MDX asset semantics and avoids a server loader waiting for an in-process
    client promise. Reuse the existing content-addressed structure cache
    only within a fresh, fixed-source build directory to limit repeated
    parsing.
    
    Keep atomic output assembly, all languages and active versions, strict
    verification, and the existing client minifier. Record separate compiler
    PIDs, phase memory and client exit ordering. Synchronize cron/manual
    settings and archive compiler metrics. Sampling retains the actual
    client compilation proof alongside server checks.
    
    Validation:
    - 407 Node tests: 405 passed, 2 conditional skips; 27 workflow tests
    passed.
    - Same-source real five-language Rspack comparison: 325 emitted files
    (3.67 MB) are byte-identical between the previous parallel pipeline and
    process isolation, including docs, blog excerpts/full posts and a local
    image.
    - Publishing fixture/browser checks, 36 native search/recovery cases and
    the five-language sampling fixture passed. The sampling fixture still
    rejects changed broken docs/blog modules and excludes unselected
    modules.
    - No local full website compilation. The next complete production run
    must establish the actual memory headroom and final time budget; this PR
    does not claim that measurement has already passed.
    
    Self-review against AGENTS.md:
    
    | Checkpoint | Conclusion |
    | --- | --- |
    | Goal | Addresses overlapping native compiler lifetimes; client process
    exit is checked before the server starts. Full-scale resource acceptance
    remains pending. |
    | Focus | Pinned build adapter, transient compiler state, measurements
    and focused regression fixtures. |
    | Information architecture | Prose, translation ownership, active
    versions, plugins and sidebars are unchanged. |
    | Paths/navigation | Same-source emitted artifacts are byte-identical;
    no document URL changes. |
    | Frontend | Existing client minification, locale prefixes, worker
    origin and theme behavior are preserved; publishing/search browser
    fixtures passed. |
    | Validation | Real native artifact equality, failure/manifest/cache
    tests and actual compilation-proof checks cover the process boundary. |
    | Other quality | No dependency upgrades or permission/secret expansion;
    failed client/source/checksum validation stops before server compilation
    or publication. |
    
    Co-authored-by: morningman <[email protected]>
---
 .github/scripts/publish_metrics.py                 |   2 +-
 .github/workflows/build-check.yml                  |   6 +
 .github/workflows/cron-deploy-website.yml          |   4 +-
 .github/workflows/manual-deploy-website.yml        |   4 +-
 scripts/build-locale.cjs                           |  30 ++++-
 scripts/build-locales.cjs                          |  15 ++-
 scripts/i18n/__tests__/compiler-processes.test.cjs |  64 +++++++++
 scripts/i18n/ci-plan.cjs                           |   9 +-
 scripts/i18n/compiler-processes.cjs                | 148 +++++++++++++++++++++
 scripts/i18n/verify-compiler-fixture.cjs           |  62 +++++++++
 10 files changed, 331 insertions(+), 13 deletions(-)

diff --git a/.github/scripts/publish_metrics.py 
b/.github/scripts/publish_metrics.py
index ca125062ee1d..af7529129a61 100644
--- a/.github/scripts/publish_metrics.py
+++ b/.github/scripts/publish_metrics.py
@@ -67,7 +67,7 @@ def start(job):
         "GITHUB_WORKFLOW_REF", "GITHUB_EVENT_NAME", "RUNNER_OS", "RUNNER_ARCH",
         "ImageOS", "ImageVersion", "DEPLOY_SOURCE_SHA", "DORIS_ENABLE_JA",
         "DORIS_ENABLE_KO", "DORIS_ENABLE_RU", "DOCS_VERSIONS", "NODE_OPTIONS",
-        "TERSER_PARALLEL", "RAYON_NUM_THREADS", "DOCUSAURUS_SSR_CONCURRENCY")}
+        "TERSER_PARALLEL", "RAYON_NUM_THREADS", "DOCUSAURUS_SSR_CONCURRENCY", 
"DORIS_COMPILER_MODE")}
     write_json(REPORTS / "context.json", {
         "schemaVersion": 1, "job": job, "startedAt": time.time(),
         "checkoutSha": git("rev-parse", "HEAD"), "context": context,
diff --git a/.github/workflows/build-check.yml 
b/.github/workflows/build-check.yml
index f8fecc3c6f85..a9873df6e599 100644
--- a/.github/workflows/build-check.yml
+++ b/.github/workflows/build-check.yml
@@ -132,6 +132,10 @@ jobs:
                   node --test scripts/publish/__tests__/*.test.cjs
                   node scripts/publish/verify-fixture.cjs
 
+            - name: Verify isolated compiler outputs
+              if: steps.detect.outputs.compiler_fixture == 'true'
+              run: node scripts/i18n/verify-compiler-fixture.cjs
+
             - name: Verify native content manifest fixture
               if: steps.detect.outputs.content_fixture == 'true'
               run: |
@@ -267,6 +271,8 @@ jobs:
                       .i18n-build/ci-sample/verification.json
                       .i18n-build/ci-sampling-fixture-proof.json
                       .i18n-build/search-transport-verification.json
+                      .i18n-build/compiler-fixture-verification.json
+                      .i18n-build/compiler-metrics/*.json
                       .i18n-build/publish-result.json
                       .i18n-build/build-result.json
                       .i18n-build/content-fixture-verification.json
diff --git a/.github/workflows/cron-deploy-website.yml 
b/.github/workflows/cron-deploy-website.yml
index 44f0cd32a993..4c348c1fcb01 100644
--- a/.github/workflows/cron-deploy-website.yml
+++ b/.github/workflows/cron-deploy-website.yml
@@ -55,7 +55,8 @@ jobs:
         DORIS_ENABLE_RU: 'true'
         NODE_OPTIONS: '--max-old-space-size=6144'
         TERSER_PARALLEL: '2'
-        RAYON_NUM_THREADS: '2'
+        RAYON_NUM_THREADS: '4'
+        DORIS_COMPILER_MODE: 'isolated'
         DOCUSAURUS_SSR_CONCURRENCY: '8'
         DOCUSAURUS_PERF_LOGGER: 'true'
         DORIS_BUILD_METRICS: 'true'
@@ -205,6 +206,7 @@ jobs:
                   .i18n-build/manual-drift.json
                   .i18n-build/coverage.json
                   .i18n-build/content-manifests/*.json
+                  .i18n-build/compiler-metrics/*.json
               if-no-files-found: error
               retention-days: 14
 
diff --git a/.github/workflows/manual-deploy-website.yml 
b/.github/workflows/manual-deploy-website.yml
index b433d7b49e5f..859e4e88b946 100644
--- a/.github/workflows/manual-deploy-website.yml
+++ b/.github/workflows/manual-deploy-website.yml
@@ -50,7 +50,8 @@ jobs:
         DORIS_ENABLE_RU: 'true'
         NODE_OPTIONS: '--max-old-space-size=6144'
         TERSER_PARALLEL: '2'
-        RAYON_NUM_THREADS: '2'
+        RAYON_NUM_THREADS: '4'
+        DORIS_COMPILER_MODE: 'isolated'
         DOCUSAURUS_SSR_CONCURRENCY: '8'
         DOCUSAURUS_PERF_LOGGER: 'true'
         DORIS_BUILD_METRICS: 'true'
@@ -178,6 +179,7 @@ jobs:
                   .i18n-build/manual-drift.json
                   .i18n-build/coverage.json
                   .i18n-build/content-manifests/*.json
+                  .i18n-build/compiler-metrics/*.json
               if-no-files-found: error
               retention-days: 14
 
diff --git a/scripts/build-locale.cjs b/scripts/build-locale.cjs
index d83de9a42d11..30e9a05b88fe 100644
--- a/scripts/build-locale.cjs
+++ b/scripts/build-locale.cjs
@@ -24,10 +24,32 @@ async function main() {
                 directory:locale === i18n.defaultLocale ? '.' : locale,
                 baseUrl:localizePath({path:siteConfig.baseUrl, pathType:'url', 
i18n:{...i18n,currentLocale:locale}}),
             }))}, null, 2) + '\n');
-    } else if (mode === 'build' || mode === 'sample') {
-        const restore = mode === 'sample' ? await 
require('./i18n/ci-sample.cjs').install({root:siteDir,outDir,locale:value,planFile:samplePlan})
 : () => {};
-        const {buildLocale} = 
fromSite('@docusaurus/core/lib/commands/build/buildLocale.js');
-        try {await buildLocale({siteDir, locale:value, cliOptions:{outDir}});} 
finally {restore();}
+    } else if (mode === 'build' || mode === 'sample' || mode === 'client') {
+        const adapter = require('./i18n/compiler-processes.cjs');
+        const {sourceStamp} = require('./build-locales.cjs');
+        const stamp = sourceStamp(siteDir);
+        const compilerMode = process.env.DORIS_COMPILER_MODE ?? 'isolated';
+        if (!['isolated','parallel'].includes(compilerMode)) throw new 
Error(`Unknown DORIS_COMPILER_MODE: ${compilerMode}`);
+        const clientResult = mode !== 'client' && compilerMode === 'isolated'
+            ? await 
adapter.runClient({siteDir,outDir,locale:value,samplePlan:mode === 'sample' ? 
samplePlan : null}) : null;
+        if (clientResult && JSON.stringify(clientResult.source) !== 
JSON.stringify(sourceStamp(siteDir))) throw new Error('Source changed during 
isolated client compilation');
+        const restore = (mode === 'sample' || mode === 'client' && samplePlan) 
? await 
require('./i18n/ci-sample.cjs').install({root:siteDir,outDir,locale:value,planFile:samplePlan})
 : () => {};
+        let restoreCompiler = () => {};
+        let result;
+        try {
+            if (mode === 'client') result = await 
adapter.buildClient({fromSite,siteDir,outDir,locale:value,samplePlan});
+            else {
+                restoreCompiler = 
adapter.install({fromSite,siteDir,locale:value,clientResult});
+                const {buildLocale} = 
fromSite('@docusaurus/core/lib/commands/build/buildLocale.js');
+                await buildLocale({siteDir, locale:value, 
cliOptions:{outDir}});
+            }
+        } finally {try {restoreCompiler();} finally {restore();}}
+        if (JSON.stringify(stamp) !== JSON.stringify(sourceStamp(siteDir))) 
throw new Error('Source changed between compiler phases');
+        if (mode === 'client') 
adapter.writeClientResult(siteDir,value,{...result,source:stamp});
+        else {
+            if(mode === 'sample' && 
clientResult)adapter.mergeSamplingScope(siteDir,value,clientResult);
+            
fs.rmSync(adapter.stateDirectory(siteDir,value),{recursive:true,force:true});
+        }
     } else throw new Error(`Unknown locale build mode: ${mode}`);
 }
 main().catch(error => {console.error(error); process.exitCode = 1;});
diff --git a/scripts/build-locales.cjs b/scripts/build-locales.cjs
index 74f62979392d..e4cd004c344d 100644
--- a/scripts/build-locales.cjs
+++ b/scripts/build-locales.cjs
@@ -43,11 +43,12 @@ async function buildLocales({siteDir = process.cwd(), 
outDir = 'build', locales:
     if(samplePlan)report.validation={mode:'sample',publicationAllowed:false};
     const reportDir = path.join(siteDir, '.i18n-build');
     fs.mkdirSync(reportDir, {recursive:true});
+    
fs.rmSync(path.join(reportDir,'compiler-metrics'),{recursive:true,force:true});
     const reportPath = path.join(reportDir, 'build-result.json');
     const execute = run ?? ((args) => new Promise((resolve, reject) => {
         const child = 
spawn(process.execPath,[path.join(__dirname,'build-locale.cjs'), ...args],{
             cwd:siteDir,stdio:'inherit',env:{...process.env,
-                ...(samplePlan && 
{DORIS_I18N_STRUCTURE_CACHE:path.join(stage,'structure-cache')})},
+                ...((samplePlan || process.env.DORIS_COMPILER_MODE !== 
'parallel') && 
{DORIS_I18N_STRUCTURE_CACHE:path.join(stage,'structure-cache')})},
         });
         console.log(JSON.stringify({event:'locale_process_start', 
mode:args[0], locale:args[0] === 'plan' ? null : args[3], pid:child.pid}));
         const stop = signal => child.kill(signal);
@@ -79,7 +80,15 @@ async function buildLocales({siteDir = process.cwd(), outDir 
= 'build', locales:
             cleanup();
             console.log(JSON.stringify({event:'locale_process_end', 
mode:args[0], locale:args[0] === 'plan' ? null : args[3], pid:child.pid, code, 
signal}));
             if (code !== 0) reject(new Error(`Locale ${args[3]} failed 
(${signal ?? code}); output was not published`));
-            else resolve({pid:child.pid,...(memory && {memory})});
+            else {
+                const 
compilerFile=path.join(reportDir,'compiler-metrics',args[3]+'.json');
+                if(memory && fs.existsSync(compilerFile))for(const record of 
JSON.parse(fs.readFileSync(compilerFile)).records??[]){
+                    
memory.peakRssKiB=Math.max(memory.peakRssKiB,Math.ceil(record.peakRssBytes/1024));
+                    
if(record.minimumSystemAvailableKiB)memory.minimumSystemAvailableKiB=Math.min(memory.minimumSystemAvailableKiB??Infinity,record.minimumSystemAvailableKiB);
+                }
+                if(memory)memory.scope='Maximum sampled individual process RSS 
across locale and isolated client; system availability includes all processes';
+                resolve({pid:child.pid,...(memory && {memory})});
+            }
         });
     }));
     try {
@@ -153,4 +162,4 @@ if (require.main === module) {
         locales:localeList === undefined ? undefined : 
localeList.split(',').map(locale => locale.trim()).filter(Boolean)})
         .catch(error => {console.error(error);process.exitCode = 1;});
 }
-module.exports = {buildLocales, selectLocales};
+module.exports = {buildLocales, selectLocales, sourceStamp};
diff --git a/scripts/i18n/__tests__/compiler-processes.test.cjs 
b/scripts/i18n/__tests__/compiler-processes.test.cjs
new file mode 100644
index 000000000000..1fffbd02ef0d
--- /dev/null
+++ b/scripts/i18n/__tests__/compiler-processes.test.cjs
@@ -0,0 +1,64 @@
+const test=require('node:test');
+const assert=require('node:assert/strict');
+const 
{createServerCompile,snapshotMdx,restoreMdx,combineSamplingScopes}=require('../compiler-processes.cjs');
+const 
request=()=>({configs:[{name:'client'},{name:'server'}],currentBundler:{name:'rspack'},other:'unchanged'});
+test('parent restores the isolated client manifest before compiling only the 
server',async()=>{
+    const calls=[],input=request(),stats={};
+    const 
compile=createServerCompile({restoreManifest:value=>{assert.equal(value,input);calls.push('restore');},compile:async
 value=>{
+        
assert.deepEqual(value.configs,[input.configs[1]]);assert.equal(value.other,'unchanged');calls.push('server');return
 stats;
+    },measureServer:async callback=>{calls.push('measure');return 
callback();}});
+    assert.equal(await 
compile(input),stats);assert.deepEqual(calls,['restore','measure','server']);
+    assert.deepEqual(input.configs.map(c=>c.name),['client','server']);
+});
+test('invalid or changed manifest stops before allocating the server 
compiler',async()=>{
+    let calls=0;const error=new Error('manifest changed');
+    const compile=createServerCompile({restoreManifest:()=>{throw 
error;},compile:()=>calls++});
+    await 
assert.rejects(compile(request()),value=>value===error);assert.equal(calls,0);
+});
+test('server failures propagate without a second client compilation',async()=>{
+    const calls=[],error=new Error('server failed');
+    const compile=createServerCompile({restoreManifest:()=>{},compile:async 
value=>{calls.push(value.configs.map(c=>c.name));throw error;}});
+    await 
assert.rejects(compile(request()),value=>value===error);assert.deepEqual(calls,[['server']]);
+});
+test('hash-router client output is retained without allocating a 
server',async()=>{
+    const calls=[];const 
compile=createServerCompile({restoreManifest:()=>calls.push('restore'),compile:()=>calls.push('unexpected')});
+    await 
compile({...request(),configs:[{name:'client'}]});assert.deepEqual(calls,['restore']);
+});
+test('unknown compiler shapes fail before manifest restoration',async()=>{
+    let calls=0;const 
compile=createServerCompile({restoreManifest:()=>calls++,compile:()=>calls++});
+    await 
assert.rejects(compile({...request(),configs:[{name:'server'},{name:'client'}]}),/Revalidate/);assert.equal(calls,0);
+});
+test('compiler mechanism changes select native equality, publishing and search 
fixtures',()=>{
+    const {policy}=require('../ci-plan.cjs');
+    for(const file of 
['scripts/build-locale.cjs','scripts/build-locales.cjs','scripts/i18n/compiler-processes.cjs']){
+        const 
plan=policy([file]);assert(plan.runCompilerFixture);assert(plan.runSearchFixture);assert(plan.runTranslationTests);assert(plan.runPublishFixture);assert(plan.runSamplingFixture);
+    }
+    
assert(!policy(['src/theme/SearchBar/SearchBar.module.css']).runCompilerFixture);
+});
+test('MDX transfer preserves full/truncated resource keys and client asset 
loader code',async()=>{
+    const full='export const assets = 
require("image?emit=true");',excerpt='truncated client JSX';
+    const config={module:{rules:[{use:[{options:{crossCompilerCache:new Map([
+        
['/blog/post.mdx',{promise:Promise.resolve(full)}],['/blog/post.mdx?truncated',{promise:Promise.resolve(excerpt)}],
+    ])}}]}]}};
+    const bytes=await snapshotMdx(config);
+    const options={crossCompilerCache:new 
Map()},server={module:{rules:[{oneOf:[{use:{options}}]}]}};
+    restoreMdx(server,bytes);
+    assert.equal(await 
options.crossCompilerCache.get('/blog/post.mdx').promise,full);
+    assert.equal(await 
options.crossCompilerCache.get('/blog/post.mdx?truncated').promise,excerpt);
+    assert.throws(()=>options.crossCompilerCache.get('/missing.mdx'),/Missing 
isolated/);
+});
+test('conflicting client MDX variants fail instead of silently selecting a 
plugin',async()=>{
+    const entry=code=>({options:{crossCompilerCache:new 
Map([['/same.mdx',{promise:Promise.resolve(code)}]])}});
+    await 
assert.rejects(snapshotMdx({module:{rules:[{use:[entry('one'),entry('other')]}]}}),/Conflicting/);
+});
+test('malformed MDX snapshots fail before server compilation',()=>{
+    
assert.throws(()=>restoreMdx({module:{rules:[]}},Buffer.from('{"invalid":true}')),/Invalid
 isolated/);
+});
+test('sample proof preserves actual client modules and server-side checks for 
the same selection',()=>{
+    const 
base={sourceSha:'a'.repeat(40),locale:'ja',selected:[{pluginId:'default',version:'current',docId:'one',route:'/ja/docs/dev/one/'}]};
+    const 
server={...base,anchorChecksSkipped:2,compiledMdxModules:{server:['docs/one.mdx']}},client={...base,compiledMdxModules:{client:['docs/one.mdx','blog/post.mdx']}};
+    const 
merged=combineSamplingScopes(server,client);assert.equal(merged.anchorChecksSkipped,2);assert.deepEqual(merged.compiledMdxModules,{...server.compiledMdxModules,...client.compiledMdxModules});
+    
assert.throws(()=>combineSamplingScopes(server,{...client,sourceSha:'b'.repeat(40)}),/scopes
 differ/);
+    
assert.throws(()=>combineSamplingScopes(server,{...client,selected:[]}),/scopes 
differ/);
+    assert.throws(()=>combineSamplingScopes(server,{...base}),/proof is 
missing/);
+});
diff --git a/scripts/i18n/ci-plan.cjs b/scripts/i18n/ci-plan.cjs
index 44a1883820d8..b02eae80433c 100644
--- a/scripts/i18n/ci-plan.cjs
+++ b/scripts/i18n/ci-plan.cjs
@@ -68,10 +68,12 @@ function policy(files, {forceFull = false, forceSample = 
false} = {}) {
                 
/verify-(content-fixture|global-language)|__tests__\/fixtures\/content-manifest/.test(file),
             ),
         runSamplingFixture: files.some(file =>
-            
/^scripts\/(build-locales?\.cjs$|i18n\/(ci-sample|ci-blog-sample|build-cache|verify-ci-sampl(?:e|ing-fixture))\.cjs$)/.test(file)),
+            
/^scripts\/(build-locales?\.cjs$|i18n\/(compiler-processes|ci-sample|ci-blog-sample|build-cache|verify-ci-sampl(?:e|ing-fixture))\.cjs$)/.test(file)),
+        runCompilerFixture: mode === 'full' || files.some(file =>
+            
/^scripts\/(build-locales?\.cjs|i18n\/(compiler-processes|verify-compiler-fixture)\.cjs)$/.test(file)),
         runSearchFixture: mode === 'full' || files.some(file =>
-            
/^(src\/theme\/(SearchBar|SearchPage)\/|src\/utils\/(fetchSearchIndex|searchMessages|searchScopes|useLanguageItems)\.[jt]sx?$|src\/hooks\/use-search-query\.ts$|docusaurus\.config\.js$|scripts\/i18n\/(search-plugin|search-worker-loader|verify-search-transport)\.cjs$)/.test(file)),
-        runPublishFixture: checks.check_publish_fixture || mode === 'full',
+            
/^(src\/theme\/(SearchBar|SearchPage)\/|src\/utils\/(fetchSearchIndex|searchMessages|searchScopes|useLanguageItems)\.[jt]sx?$|src\/hooks\/use-search-query\.ts$|docusaurus\.config\.js$|scripts\/build-locales?\.cjs$|scripts\/i18n\/(compiler-processes|search-plugin|search-worker-loader|verify-search-transport)\.cjs$)/.test(file)),
+        runPublishFixture: checks.check_publish_fixture || mode === 'full' || 
files.includes('scripts/i18n/compiler-processes.cjs'),
         runTranslationTests: mode === 'full' || files.some(file =>
             runtime.test(file) || mechanism.test(file) || 
maintenanceConfig(file) || /^scripts\/(i18n\/|build-locales?\.cjs$)/.test(file)
                 || file === '.github/workflows/build-check.yml'),
@@ -186,6 +188,7 @@ if (require.main === module) {
                     publish_fixture: result.runPublishFixture,
                     sampling_fixture: result.runSamplingFixture,
                     search_fixture: result.runSearchFixture,
+                    compiler_fixture: result.runCompilerFixture,
                     translation_tests: result.runTranslationTests,
                     docs_report: result.runDocsReport,
                 })
diff --git a/scripts/i18n/compiler-processes.cjs 
b/scripts/i18n/compiler-processes.cjs
new file mode 100644
index 000000000000..dd5e6127e873
--- /dev/null
+++ b/scripts/i18n/compiler-processes.cjs
@@ -0,0 +1,148 @@
+// Pinned Docusaurus compiler adapter. Rspack 1.2's closed native compiler is
+// still reachable; only process exit gives this build a reliable memory bound.
+const fs = require('node:fs');
+const path = require('node:path');
+const {createHash} = require('node:crypto');
+const {createRequire} = require('node:module');
+const {spawn} = require('node:child_process');
+const digest = bytes => createHash('sha256').update(bytes).digest('hex');
+function mdxCaches(config) {
+    const caches=[];
+    function inspect(item){
+        if(!item||typeof item!=='object')return;
+        if(item.options?.crossCompilerCache instanceof 
Map)caches.push(item.options);
+        for(const key of ['rules','oneOf','use']){
+            const 
value=item[key];if(Array.isArray(value))value.forEach(inspect);else 
inspect(value);
+        }
+    }
+    (config.module?.rules??[]).forEach(inspect);return caches;
+}
+async function snapshotMdx(config) {
+    const entries=new Map();
+    for(const options of mdxCaches(config))for(const [resource,entry]of 
options.crossCompilerCache){
+        const code=await entry.promise;
+        if(typeof code!=='string')throw new Error(`Unexpected compiled MDX 
value: ${resource}`);
+        if(entries.has(resource)&&entries.get(resource)!==code)throw new 
Error(`Conflicting compiled MDX resources: ${resource}`);
+        entries.set(resource,code);
+    }
+    return 
Buffer.from(JSON.stringify([...entries].sort(([a],[b])=>a.localeCompare(b))));
+}
+function restoreMdx(config,bytes) {
+    const entries=JSON.parse(bytes);
+    
if(!Array.isArray(entries)||entries.some(row=>!Array.isArray(row)||row.length!==2||row.some(value=>typeof
 value!=='string')))throw new Error('Invalid isolated MDX snapshot');
+    for(const loader of mdxCaches(config)){
+        const cache=new 
Map(entries.map(([resource,code])=>[resource,{promise:Promise.resolve(code)}]));
+        cache.get=function(resource){if(!this.has(resource))throw new 
Error(`Missing isolated client MDX resource: ${resource}`);return 
Map.prototype.get.call(this,resource);};
+        loader.crossCompilerCache=cache;
+    }
+}
+const stateDirectory = (siteDir,locale) => 
path.join(siteDir,'.i18n-build/compiler-state',locale);
+function reportFile(siteDir,locale) {return 
path.join(siteDir,'.i18n-build/compiler-metrics',locale+'.json');}
+function updateReport(siteDir,locale,update) {
+    const 
file=reportFile(siteDir,locale);fs.mkdirSync(path.dirname(file),{recursive:true});
+    const 
value=fs.existsSync(file)?JSON.parse(fs.readFileSync(file)):{schemaVersion:1,locale,mode:process.env.DORIS_COMPILER_MODE??'isolated',records:[]};
+    
update(value);fs.writeFileSync(file,JSON.stringify(value,null,2)+'\n');return 
value;
+}
+function coreModules(fromSite) {
+    const 
requireCore=createRequire(fromSite.resolve('@docusaurus/core/lib/commands/build/buildLocale.js'));
+    for(const name of 
['@docusaurus/core','@docusaurus/bundler','@docusaurus/mdx-loader']) {
+        const version=requireCore(name+'/package.json').version;
+        if(version!=='3.6.3')throw new Error(`Revalidate isolated compilation 
for ${name} ${version}`);
+    }
+    return requireCore;
+}
+async function measure({siteDir,locale,name},compile) {
+    const startedAtMs=Date.now(),before=process.memoryUsage();let 
success=false,samples=0,peakRssBytes=before.rss,minimumSystemAvailableKiB=null;
+    const 
sample=()=>{samples++;peakRssBytes=Math.max(peakRssBytes,process.memoryUsage().rss);
+        try{const 
available=Number(fs.readFileSync('/proc/meminfo','utf8').match(/^MemAvailable:\s+(\d+)/m)?.[1]);if(available>0)minimumSystemAvailableKiB=Math.min(minimumSystemAvailableKiB??Infinity,available);}catch{}
+    };
+    const timer=setInterval(sample,1000);timer.unref();sample();
+    try{const result=await compile();success=true;return result;}
+    finally{sample();clearInterval(timer);const 
record={name,pid:process.pid,success,startedAtMs,finishedAtMs:Date.now(),sampleIntervalMs:1000,samples,peakRssBytes,minimumSystemAvailableKiB,before,afterClose:process.memoryUsage()};
+        
updateReport(siteDir,locale,value=>value.records.push(record));console.log(JSON.stringify({event:'compiler_phase_end',locale,...record}));}
+}
+async function buildClient({fromSite,siteDir,outDir,locale,samplePlan}) {
+    const requireCore=coreModules(fromSite);
+    process.env.DOCUSAURUS_CURRENT_LOCALE=locale;
+    const {loadSite}=requireCore('@docusaurus/core/lib/server/site.js');
+    const 
{createConfigureWebpackUtils,executePluginsConfigureWebpack}=requireCore('@docusaurus/core/lib/webpack/configure.js');
+    const 
{createBuildClientConfig}=requireCore('@docusaurus/core/lib/webpack/client.js');
+    const {props}=await 
loadSite({siteDir,outDir,locale,localizePath:undefined});
+    const utils=await 
createConfigureWebpackUtils({siteConfig:props.siteConfig});
+    const {config:initial,clientManifestPath}=await 
createBuildClientConfig({props,minify:true,faster:props.siteConfig.future.experimental_faster,configureWebpackUtils:utils,bundleAnalyzer:false});
+    const 
config=executePluginsConfigureWebpack({plugins:props.plugins,config:initial,isServer:false,configureWebpackUtils:utils});
+    
updateReport(siteDir,locale,value=>{value.bundler=utils.currentBundler.name;value.bundlerVersion='3.6.3';});
+    await 
measure({siteDir,locale,name:'client'},()=>requireCore('@docusaurus/bundler').compile({configs:[config],currentBundler:utils.currentBundler}));
+    const 
bytes=fs.readFileSync(clientManifestPath),directory=stateDirectory(siteDir,locale);fs.mkdirSync(directory,{recursive:true});
+    fs.writeFileSync(path.join(directory,'client-manifest.json'),bytes);
+    // The pinned MDX loader waits for client promises when compiling the
+    // server. Preserve their exact client JSX instead of recompiling in server
+    // mode, which would change inline image-loader behavior.
+    const mdx=await snapshotMdx(config),entryCount=JSON.parse(mdx).length;
+    fs.writeFileSync(path.join(directory,'client-mdx.json'),mdx);
+    
console.log(JSON.stringify({event:'client_mdx_snapshot',locale,entries:entryCount,bytes:mdx.length}));
+    let scopeSha256;
+    if(samplePlan){const 
scope=fs.readFileSync(path.join(siteDir,'.i18n-build/ci-sample/scopes',locale+'.json'));fs.writeFileSync(path.join(directory,'client-scope.json'),scope);scopeSha256=digest(scope);}
+    return 
{clientManifestPath,manifestSha256:digest(bytes),mdxSha256:digest(mdx),mdxEntries:entryCount,bundler:utils.currentBundler.name,scopeSha256};
+}
+function 
createServerCompile({compile,restoreManifest,measureServer=callback=>callback()})
 {
+    return async options=>{
+        const names=options.configs.map(config=>config.name);
+        
if(names.length>2||names[0]!=='client'||names.length===2&&names[1]!=='server')throw
 new Error(`Revalidate the isolated compiler sequence: ${names.join(',')}`);
+        restoreManifest(options);
+        // The known hash-router path has no server compiler or SSG bundle.
+        if(names.length===1)return;
+        return 
measureServer(()=>compile({...options,configs:[options.configs[1]]}));
+    };
+}
+function install({fromSite,siteDir,locale,clientResult}) {
+    const 
requireCore=coreModules(fromSite),compiler=requireCore('@docusaurus/bundler/lib/compiler.js'),namespace=requireCore('@docusaurus/bundler');
+    const original=compiler.compile;
+    const 
wrapped=clientResult?createServerCompile({compile:original,restoreManifest:options=>{
+        if(options.currentBundler.name!==clientResult.bundler)throw new 
Error('Client/server bundler changed between isolated processes');
+        const generated=options.configs[0].resolve?.alias?.['@generated'];
+        if(typeof 
generated!=='string'||path.resolve(generated,'client-manifest.json')!==path.resolve(clientResult.clientManifestPath))throw
 new Error('Client manifest path changed between isolated processes');
+        const 
bytes=fs.readFileSync(path.join(stateDirectory(siteDir,locale),'client-manifest.json'));
+        if(digest(bytes)!==clientResult.manifestSha256)throw new 
Error('Isolated client manifest changed before SSG');
+        
fs.mkdirSync(path.dirname(clientResult.clientManifestPath),{recursive:true});fs.writeFileSync(clientResult.clientManifestPath,bytes);
+        const 
mdx=fs.readFileSync(path.join(stateDirectory(siteDir,locale),'client-mdx.json'));
+        if(digest(mdx)!==clientResult.mdxSha256)throw new Error('Isolated 
client MDX changed before server compilation');
+        restoreMdx(options.configs[1]??options.configs[0],mdx);
+    
},measureServer:callback=>measure({siteDir,locale,name:'server'},callback)})
+        
:options=>measure({siteDir,locale,name:options.configs.map(c=>c.name).join('+')},()=>original(options));
+    compiler.compile=wrapped;
+    if(namespace.compile!==wrapped){compiler.compile=original;throw new 
Error('Docusaurus compile export no longer references the pinned adapter');}
+    return()=>{compiler.compile=original;};
+}
+async function runClient({siteDir,outDir,locale,samplePlan}) {
+    const 
directory=stateDirectory(siteDir,locale);fs.rmSync(directory,{recursive:true,force:true});
+    
updateReport(siteDir,locale,value=>{value.mode='isolated';value.records=[];});
+    const 
args=[path.resolve(__dirname,'../build-locale.cjs'),'client',siteDir,outDir,locale,...(samplePlan?[samplePlan]:[])];
+    let signal=null;
+    await new Promise((resolve,reject)=>{
+        const 
child=spawn(process.execPath,args,{cwd:siteDir,stdio:'inherit',env:process.env});
+        
console.log(JSON.stringify({event:'client_compiler_start',locale,pid:child.pid}));
+        const 
terminate=()=>{signal='SIGTERM';child.kill(signal);},interrupt=()=>{signal='SIGINT';child.kill(signal);};
+        const 
cleanup=()=>{process.removeListener('SIGTERM',terminate);process.removeListener('SIGINT',interrupt);};
+        process.once('SIGTERM',terminate);process.once('SIGINT',interrupt);
+        child.once('error',error=>{cleanup();reject(error);});
+        
child.once('exit',(code,childSignal)=>{cleanup();updateReport(siteDir,locale,value=>{value.clientPid=child.pid;value.clientExitedAtMs=Date.now();value.clientExitCode=code;});
+            
console.log(JSON.stringify({event:'client_compiler_exit',locale,pid:child.pid,code,signal:childSignal}));
+            if(code!==0||signal)reject(new Error(`Isolated client compiler 
failed (${signal??childSignal??code})`));else resolve();});
+    });
+    return JSON.parse(fs.readFileSync(path.join(directory,'result.json')));
+}
+function writeClientResult(siteDir,locale,result) 
{fs.writeFileSync(path.join(stateDirectory(siteDir,locale),'result.json'),JSON.stringify(result)+'\n');}
+function combineSamplingScopes(server,client) {
+    const 
selection=scope=>JSON.stringify((scope.selected??[]).map(({pluginId,version,docId,route})=>[pluginId,version,docId,route]));
+    
if(server.sourceSha!==client.sourceSha||server.locale!==client.locale||selection(server)!==selection(client))throw
 new Error('Isolated sample compiler scopes differ');
+    if(!Array.isArray(client.compiledMdxModules?.client))throw new 
Error('Isolated sample client compilation proof is missing');
+    return 
{...server,compiledMdxModules:{...server.compiledMdxModules,client:client.compiledMdxModules.client}};
+}
+function mergeSamplingScope(siteDir,locale,clientResult) {
+    const 
bytes=fs.readFileSync(path.join(stateDirectory(siteDir,locale),'client-scope.json'));
+    if(digest(bytes)!==clientResult.scopeSha256)throw new Error('Isolated 
sample client proof changed');
+    const 
file=path.join(siteDir,'.i18n-build/ci-sample/scopes',locale+'.json');
+    
fs.writeFileSync(file,JSON.stringify(combineSamplingScopes(JSON.parse(fs.readFileSync(file)),JSON.parse(bytes)),null,2)+'\n');
+}
+module.exports={buildClient,createServerCompile,install,runClient,writeClientResult,stateDirectory,mdxCaches,snapshotMdx,restoreMdx,combineSamplingScopes,mergeSamplingScope};
diff --git a/scripts/i18n/verify-compiler-fixture.cjs 
b/scripts/i18n/verify-compiler-fixture.cjs
new file mode 100644
index 000000000000..37c734d7b6bc
--- /dev/null
+++ b/scripts/i18n/verify-compiler-fixture.cjs
@@ -0,0 +1,62 @@
+const fs = require('node:fs');
+const path = require('node:path');
+const assert = require('node:assert/strict');
+const {createHash} = require('node:crypto');
+const {execFileSync,spawnSync} = require('node:child_process');
+const {createFixture} = require('./__tests__/fixtures/multi-locale.cjs');
+const root = path.resolve(__dirname,'../..');
+const fixture = path.join(root,'.i18n-build/compiler-fixture');
+function inventory(directory) {
+    const files={};
+    function walk(current){for(const entry of 
fs.readdirSync(current,{withFileTypes:true})){
+        const file=path.join(current,entry.name);
+        if(entry.isDirectory())walk(file);
+        else {const 
bytes=fs.readFileSync(file);files[path.relative(directory,file)]={bytes:bytes.length,sha256:createHash('sha256').update(bytes).digest('hex')};}
+    }}
+    walk(directory);return files;
+}
+async function main() {
+    fs.rmSync(fixture,{recursive:true,force:true});
+    const {write}=await createFixture(fixture);
+    write('docs/pixel.svg','<svg xmlns="http://www.w3.org/2000/svg"; width="8" 
height="8"><rect width="8" height="8"/></svg>');
+    fs.appendFileSync(path.join(fixture,'docs/01-shared.md'),'\n![Local 
image](./pixel.svg)\n');
+    write('blog/2026-01-01-first.md','---\ntitle: First post\nslug: 
first\n---\n\nExcerpt.\n\n<!-- truncate -->\n\n## More\n\nFull blog body.\n');
+    write('blog/2026-01-02-second.md','---\ntitle: Second post\nslug: 
second\n---\n\nSecond body.\n');
+    
write('i18n/ja/docusaurus-plugin-content-blog/2026-01-01-first.md','---\ntitle: 
日本語の記事\nslug: first\n---\n\n日本語。\n\n<!-- truncate -->\n\n## More\n\n記事の本文。\n');
+    fs.appendFileSync(path.join(fixture,'docusaurus.config.js'),`\nconst 
original=module.exports;
+module.exports=()=>{const 
config=original();config.future={experimental_faster:true};
+config.presets[0][1].blog={path:'blog',showReadingTime:false,onInlineAuthors:'ignore',onUntruncatedBlogPosts:'ignore'};
+config.plugins.push(require(${JSON.stringify(path.join(root,'config/custom-docusaurus-plugin.js'))}));return
 config;};\n`);
+    write('.gitignore','node_modules\n.i18n-build/\n.docusaurus/\nbuild/\n');
+    execFileSync('git',['add','.'],{cwd:fixture});
+    
execFileSync('git',['-c','user.name=Fixture','-c','[email protected]','commit','-qm','Pinned
 native compiler equality fixture'],{cwd:fixture});
+    const modes={};
+    for(const mode of ['parallel','isolated']){
+        const 
child=spawnSync(process.execPath,[path.join(root,'scripts/build-locales.cjs'),'--site-dir',fixture],{
+            
cwd:root,stdio:'inherit',env:{...process.env,NODE_OPTIONS:'--max-old-space-size=4096',
+                
DORIS_COMPILER_MODE:mode,DORIS_OPTIMIZE_PUBLISH:'false',DORIS_ASSET_ORIGIN:'',
+                
TERSER_PARALLEL:'2',RAYON_NUM_THREADS:'4',DOCUSAURUS_SSR_CONCURRENCY:'4',DORIS_BUILD_METRICS:'true'}});
+        assert.equal(child.status,0,mode+' native fixture build');
+        const 
build=JSON.parse(fs.readFileSync(path.join(fixture,'.i18n-build/build-result.json')));
+        assert.equal(build.locales.length,5);
+        const 
metrics=build.locales.map(({locale})=>JSON.parse(fs.readFileSync(path.join(fixture,'.i18n-build/compiler-metrics',locale+'.json'))));
+        if(mode==='isolated')for(const record of metrics){
+            
assert.equal(record.bundler,'rspack');assert.deepEqual(record.records.map(r=>r.name),['client','server']);
+            
assert(record.records.every(r=>r.success));assert(record.records[0].finishedAtMs<=record.records[1].startedAtMs);
+            
assert.notEqual(record.records[0].pid,record.records[1].pid);assert.equal(record.clientExitCode,0);
+            
assert(record.clientExitedAtMs<=record.records[1].startedAtMs,'Server started 
before the client process exited');
+        }
+        modes[mode]={files:inventory(path.join(fixture,'build')),metrics};
+    }
+    const paths=new 
Set([...Object.keys(modes.parallel.files),...Object.keys(modes.isolated.files)]);
+    const 
differences=[...paths].filter(file=>JSON.stringify(modes.parallel.files[file])!==JSON.stringify(modes.isolated.files[file]));
+    const result={passed:!differences.length,scope:'Same-source five-language 
real Rspack HTML/JS/CSS output; no full website build',
+        
implementationSha:execFileSync('git',['rev-parse','HEAD'],{cwd:root,encoding:'utf8'}).trim(),
+        
fixtureSource:execFileSync('git',['rev-parse','HEAD'],{cwd:fixture,encoding:'utf8'}).trim(),
+        
locales:5,filesCompared:paths.size,bytesCompared:Object.values(modes.isolated.files).reduce((sum,row)=>sum+row.bytes,0),
+        differences,modes};
+    
fs.writeFileSync(path.join(root,'.i18n-build/compiler-fixture-verification.json'),JSON.stringify(result,null,2)+'\n');
+    
console.log(JSON.stringify({passed:result.passed,filesCompared:paths.size,differences}));assert(result.passed,'Process-isolated
 native compilation changed emitted artifacts');
+}
+if(require.main===module)main().catch(error=>{console.error(error);process.exitCode=1;});
+module.exports={inventory};


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to