fix: optimize i18n page generation to prevent OOM during Docker build

This commit is contained in:
alam00000
2026-01-27 16:40:57 +05:30
parent 73a8156c80
commit 4fb3b0ec1c
3 changed files with 207 additions and 150 deletions

View File

@@ -26,7 +26,7 @@ ENV COMPRESSION_MODE=$COMPRESSION_MODE
ARG BASE_URL
ENV BASE_URL=$BASE_URL
ENV NODE_OPTIONS="--max-old-space-size=4096"
ENV NODE_OPTIONS="--max-old-space-size=3072"
RUN npm run build:with-docs

View File

@@ -6,7 +6,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build && NODE_OPTIONS='--max-old-space-size=4096' node scripts/generate-i18n-pages.mjs && node scripts/generate-sitemap.mjs",
"build": "tsc && vite build && NODE_OPTIONS='--max-old-space-size=3072' node scripts/generate-i18n-pages.mjs && node scripts/generate-sitemap.mjs",
"build:with-docs": "npm run build && npm run docs:build && node scripts/include-docs-in-dist.js",
"build:gzip": "COMPRESSION_MODE=g npm run build",
"build:brotli": "COMPRESSION_MODE=b npm run build",

View File

@@ -24,6 +24,24 @@ const KEY_MAPPING = {
404: 'notFound',
};
function loadAllTranslations() {
const translations = {};
for (const lang of languages) {
if (lang === 'en') continue;
const commonPath = path.join(LOCALES_DIR, `${lang}/common.json`);
const toolsPath = path.join(LOCALES_DIR, `${lang}/tools.json`);
translations[lang] = {
common: fs.existsSync(commonPath)
? JSON.parse(fs.readFileSync(commonPath, 'utf-8'))
: {},
tools: fs.existsSync(toolsPath)
? JSON.parse(fs.readFileSync(toolsPath, 'utf-8'))
: {},
};
}
return translations;
}
// TODO@ALAM: Let users build only a single language
function buildUrl(langPrefix, pagePath) {
const parts = [SITE_URL];
@@ -33,48 +51,20 @@ function buildUrl(langPrefix, pagePath) {
return parts.filter(Boolean).join('/').replace(/\/+$/, '') || SITE_URL;
}
async function generateI18nPages() {
console.log('🌍 Generating i18n pages...');
console.log(` SITE_URL: ${SITE_URL}`);
console.log(` BASE_PATH: ${BASE_PATH || '/'}`);
if (!fs.existsSync(DIST_DIR)) {
console.error('❌ dist directory not found. Please run build first.');
process.exit(1);
}
const htmlFiles = fs
.readdirSync(DIST_DIR)
.filter((file) => file.endsWith('.html'));
for (const file of htmlFiles) {
const filePath = path.join(DIST_DIR, file);
const originalContent = fs.readFileSync(filePath, 'utf-8');
function processFileForLanguage(
originalContent,
file,
lang,
translations,
langDir
) {
const filenameNoExt = file.replace('.html', '');
let translationKey = toCamelCase(filenameNoExt);
if (KEY_MAPPING[filenameNoExt]) {
translationKey = KEY_MAPPING[filenameNoExt];
}
for (const lang of languages) {
if (lang === 'en') continue;
const langDir = path.join(DIST_DIR, lang);
if (!fs.existsSync(langDir)) {
fs.mkdirSync(langDir, { recursive: true });
}
const commonPath = path.join(LOCALES_DIR, `${lang}/common.json`);
const toolsPath = path.join(LOCALES_DIR, `${lang}/tools.json`);
const common = fs.existsSync(commonPath)
? JSON.parse(fs.readFileSync(commonPath, 'utf-8'))
: {};
const tools = fs.existsSync(toolsPath)
? JSON.parse(fs.readFileSync(toolsPath, 'utf-8'))
: {};
const { tools } = translations[lang];
const dom = new JSDOM(originalContent);
const document = dom.window.document;
@@ -179,9 +169,15 @@ async function generateI18nPages() {
link.setAttribute('href', newHref);
});
fs.writeFileSync(path.join(langDir, file), dom.serialize());
const result = dom.serialize();
dom.window.close();
fs.writeFileSync(path.join(langDir, file), result);
}
function updateEnglishFile(filePath, originalContent) {
const filenameNoExt = path.basename(filePath, '.html');
const dom = new JSDOM(originalContent);
const document = dom.window.document;
@@ -205,7 +201,68 @@ async function generateI18nPages() {
defaultLink.href = buildUrl('', pagePath);
document.head.appendChild(defaultLink);
fs.writeFileSync(filePath, dom.serialize());
const result = dom.serialize();
dom.window.close();
fs.writeFileSync(filePath, result);
}
async function generateI18nPages() {
console.log('🌍 Generating i18n pages...');
console.log(` SITE_URL: ${SITE_URL}`);
console.log(` BASE_PATH: ${BASE_PATH || '/'}`);
console.log(` Languages: ${languages.length} (${languages.join(', ')})`);
if (!fs.existsSync(DIST_DIR)) {
console.error('❌ dist directory not found. Please run build first.');
process.exit(1);
}
console.log(' Loading translations...');
const translations = loadAllTranslations();
const htmlFiles = fs
.readdirSync(DIST_DIR)
.filter((file) => file.endsWith('.html'));
console.log(` Processing ${htmlFiles.length} HTML files...`);
for (const lang of languages) {
if (lang === 'en') continue;
const langDir = path.join(DIST_DIR, lang);
if (!fs.existsSync(langDir)) {
fs.mkdirSync(langDir, { recursive: true });
}
}
let processed = 0;
const total = htmlFiles.length * (languages.length - 1);
for (const file of htmlFiles) {
const filePath = path.join(DIST_DIR, file);
const originalContent = fs.readFileSync(filePath, 'utf-8');
for (const lang of languages) {
if (lang === 'en') continue;
const langDir = path.join(DIST_DIR, lang);
processFileForLanguage(
originalContent,
file,
lang,
translations,
langDir
);
processed++;
if (processed % 10 === 0 || processed === total) {
console.log(` Progress: ${processed}/${total} pages`);
}
}
updateEnglishFile(filePath, originalContent);
}
console.log('✅ i18n pages generated successfully!');