Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"start": "docusaurus start",
"prebuild": "node scripts/optimize-thumbnails.js && node scripts/optimize-screenshots.js",
"build": "docusaurus build",
"postbuild": "node scripts/sitemap-sections.js",
"postbuild": "node scripts/sitemap-sections.js && node scripts/dedupe-locale-static.js",
"optimize:thumbnails": "node scripts/optimize-thumbnails.js",
"optimize:screenshots": "node scripts/optimize-screenshots.js",
"swizzle": "docusaurus swizzle",
Expand Down
69 changes: 69 additions & 0 deletions scripts/dedupe-locale-static.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Docusaurus copies the whole static/ folder into every locale build
// (build/ru/, build/ua/, ...), so each deployment carried six copies of the
// ~340 MB utility apps. This removes the locale copies; vercel.json rewrites
// /<locale>/<dir>/... back to the root copy. Rewrites only apply when no file
// matches, so generated locale pages under the same prefixes still win.
const fs = require('fs');
const path = require('path');

const ROOT = path.join(__dirname, '..');
const STATIC_DIR = path.join(ROOT, 'static');
const BUILD_DIR = path.join(ROOT, 'build');
const LOCALES = ['ru', 'ua', 'de', 'es', 'et'];
// Tiny, and utilities/ shares its prefix with generated /utilities/ pages;
// not worth a rewrite.
const KEEP = new Set(['utilities', '.nojekyll']);

function rewrittenEntries() {
const config = JSON.parse(fs.readFileSync(path.join(ROOT, 'vercel.json'), 'utf8'));
const rule = (config.rewrites || []).find((r) => r.source.includes(':asset((?:'));
if (!rule) throw new Error('vercel.json has no locale static rewrite with an :asset((?:...)) group');
return new Set(rule.source.match(/:asset\(\(\?:([^)]+)\)/)[1].replace(/\\\./g, '.').split('|'));
}

function walk(dir, rel = '') {
const out = [];
for (const entry of fs.readdirSync(path.join(dir, rel), {withFileTypes: true})) {
const child = path.join(rel, entry.name);
if (entry.isDirectory()) out.push(...walk(dir, child));
else out.push(child);
}
return out;
}

function removeEmptyDirs(dir) {
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return;
for (const entry of fs.readdirSync(dir)) removeEmptyDirs(path.join(dir, entry));
if (fs.readdirSync(dir).length === 0) fs.rmdirSync(dir);
}

const rewritten = rewrittenEntries();
const topLevel = fs.readdirSync(STATIC_DIR).filter((name) => !KEEP.has(name));
const uncovered = topLevel.filter((name) => !rewritten.has(name));
if (uncovered.length) {
throw new Error(
`static/ entries not covered by the locale rewrite in vercel.json: ${uncovered.join(', ')}. ` +
'Add them to the :asset((?:...)) group or to KEEP in scripts/dedupe-locale-static.js.',
);
}

let removed = 0;
let bytes = 0;
for (const name of topLevel) {
const src = path.join(STATIC_DIR, name);
const files = fs.statSync(src).isDirectory() ? walk(src).map((f) => path.join(name, f)) : [name];
for (const locale of LOCALES) {
for (const rel of files) {
const copy = path.join(BUILD_DIR, locale, rel);
if (!fs.existsSync(copy)) continue;
const {size} = fs.statSync(copy);
if (size !== fs.statSync(path.join(STATIC_DIR, rel)).size) continue;
fs.unlinkSync(copy);
Comment thread
YurMil marked this conversation as resolved.
removed += 1;
bytes += size;
}
removeEmptyDirs(path.join(BUILD_DIR, locale, name));
}
}

console.log(`Removed ${removed} duplicated static files from locale builds (${(bytes / 1024 / 1024).toFixed(1)} MB).`);
11 changes: 11 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,17 @@
]
}
],
"git": {
"deploymentEnabled": {
"dependabot/**": false
}
},
"rewrites": [
{
"source": "/:locale(ru|ua|de|es|et)/:asset((?:fonts|icons|img|mini-games|styles\\.css|textures|utility-apps|vendor)(?:/.*)?)",
"destination": "/:asset"
}
],
"redirects": [
{
"source": "/:path*",
Expand Down
Loading