From 4e4e6695b4f7df81ec29e605c603efdf2ee14dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 23 Sep 2026 15:06:43 -0400 Subject: [PATCH 1/3] perf: only write changed files --- scripts/utils.mjs | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/scripts/utils.mjs b/scripts/utils.mjs index 721c5c6..f69ad12 100644 --- a/scripts/utils.mjs +++ b/scripts/utils.mjs @@ -76,6 +76,19 @@ const samePropertyRuns = (codePointProperties) => { return result; }; +// Only write the file if its content has changed. +// Avoid the unnecessary filesystem I/O +const writeFileIfChanged = (filePath, content) => { + try { + if (fs.readFileSync(filePath, 'utf8') === content) { + return; + } + } catch { + // Ignore errors such ENOENT (file not found) + } + fs.writeFileSync(filePath, content); +}; + const writeFiles = (options) => { const version = options.version; const subType = options.subType; @@ -147,11 +160,11 @@ const writeFiles = (options) => { if (type === 'Sequence_Property' || isNameAliases) { const sequences = codePoints; const output = `import { gunzipSync } from 'node:zlib';\n\nexport default ${ gzipInline(sequences) };\n`; - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'index.mjs'), output ); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'index.d.mts'), type === 'Sequence_Property' ? `declare const data: string[];\nexport default data;\n` @@ -167,19 +180,19 @@ const writeFiles = (options) => { let symbolsType = 'string[]'; if (!isCaseFoldingOrMapping) { const encodedRanges = codePoints instanceof regenerate ? encodeRegenerate(codePoints) : encodeRanges(codePoints); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'ranges.mjs'), `import decodeRanges from '../../decode-ranges.mjs';\n\nexport default decodeRanges('${encodedRanges}');\n` ); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'ranges.d.mts'), 'import type { UnicodeRange } from "../../decode-ranges.mjs";\n\ndeclare const ranges: UnicodeRange[];\nexport default ranges;\n' ); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'regex.mjs'), 'export default /' + regenerate(codePoints).toString() + '/;\n' ); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'regex.d.mts'), 'declare const regex: RegExp;\nexport default regex;\n' ); @@ -213,19 +226,19 @@ const writeFiles = (options) => { } symbolsType = 'Map'; } - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'code-points.mjs'), codePointsFileContent ); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'code-points.d.mts'), `declare const codePoints: ${ codePointsType };\nexport default codePoints;\n` ); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'symbols.mjs'), symbolsFileContent ); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'symbols.d.mts'), `declare const symbols: ${ symbolsType };\nexport default symbols;\n` ); @@ -249,11 +262,11 @@ const writeFiles = (options) => { JSON.stringify(bidiMirroringGlyphFlatPairs) }.map((v, i, a) => pair(i & 1, a[i ^ 1], v)));\n` ].join('\n'); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'index.mjs'), output ); - fs.writeFileSync( + writeFileIfChanged( path.resolve(dir, 'index.d.mts'), `declare const data: Map;\nexport default data;\n` ); @@ -275,8 +288,8 @@ const writeFiles = (options) => { const output = `import { gunzipSync } from 'node:zlib';\nimport decodePropertyMap from '../decode-property-map.mjs';\n\nexport default decodePropertyMap(${gzipInline( flatRuns )});\n`; - fs.writeFileSync(path.resolve(dir, 'index.mjs'), output); - fs.writeFileSync(path.resolve(dir, 'index.d.mts'), `declare const map: Map;\nexport default map;\n`); + writeFileIfChanged(path.resolve(dir, 'index.mjs'), output); + writeFileIfChanged(path.resolve(dir, 'index.d.mts'), `declare const map: Map;\nexport default map;\n`); } } return dirMap; From 09ded3df93e8d93d6050ad2f171b3ac65e98cbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 23 Sep 2026 15:07:38 -0400 Subject: [PATCH 2/3] perf: reduce cluster amount More than 4 works increases kernel overhead on APFS system. --- scripts/generate-data.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/generate-data.mjs b/scripts/generate-data.mjs index 3980b67..f91219c 100644 --- a/scripts/generate-data.mjs +++ b/scripts/generate-data.mjs @@ -1,11 +1,11 @@ import cluster from 'node:cluster'; -import os from 'node:os'; +import { availableParallelism } from 'node:os'; import resources from '../data/resources.mjs'; import generateData from '../index.mjs'; // ----------------------------------------------------------------------------- -const numCPUs = os.cpus().length; +const numCPUs = Math.min(4, availableParallelism()); const pad = (number) => { return String(number).padStart(2, '0'); From 144ab1bcf3b4caba6c0b024239d2c14ae060efac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 23 Sep 2026 16:04:33 -0400 Subject: [PATCH 3/3] Update scripts/utils.mjs Co-authored-by: Mathias Bynens --- scripts/utils.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/utils.mjs b/scripts/utils.mjs index f69ad12..a194f4a 100644 --- a/scripts/utils.mjs +++ b/scripts/utils.mjs @@ -77,7 +77,7 @@ const samePropertyRuns = (codePointProperties) => { }; // Only write the file if its content has changed. -// Avoid the unnecessary filesystem I/O +// Reading files is cheaper than writing files, and most files don’t change between builds. const writeFileIfChanged = (filePath, content) => { try { if (fs.readFileSync(filePath, 'utf8') === content) {