From 685d793ab2265cf7170328d680c84a04cb4c5ed7 Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Thu, 26 Sep 2024 03:26:41 +0900 Subject: [PATCH] Fix SEGFAULT error by `new Intl.Segmenter().segment()` in the standalone binary Baking the compatible ICU data with the Node binary. --- package.json | 6 ++---- scripts/icu.mjs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 scripts/icu.mjs diff --git a/package.json b/package.json index f6c18470..30c9e323 100644 --- a/package.json +++ b/package.json @@ -40,9 +40,7 @@ }, "pkg": { "scripts": "lib/**/*.js", - "assets": [ - "node_modules/vm2/**/*" - ] + "assets": ["tmp/icu/icudt*.dat"] }, "browserslist": [ "> 1% and last 3 versions", @@ -61,7 +59,7 @@ "lint:css": "stylelint \"src/**/*.{css,scss}\"", "prepack": "npm-run-all --parallel check:* lint:* test:coverage --parallel build types", "preversion": "run-p check:* lint:* test:coverage", - "standalone": "node -e 'fs.rmSync(`bin`,{recursive:true,force:true})' && pkg --out-path ./bin .", + "standalone": "node -e 'fs.rmSync(`bin`,{recursive:true,force:true})' && pkg --options \"icu-data-dir=$(node ./scripts/icu.mjs)\" --out-path ./bin .", "standalone:pack": "node ./scripts/pack.js", "test": "jest", "test:coverage": "jest --coverage", diff --git a/scripts/icu.mjs b/scripts/icu.mjs new file mode 100644 index 00000000..faf30cd4 --- /dev/null +++ b/scripts/icu.mjs @@ -0,0 +1,53 @@ +/* For baking the ICU data into the standalone binary, we need to download the compatible ICU data from the ICU repository. */ +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { promisify } from 'node:util' +import yauzl from 'yauzl' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const icuDir = path.join(__dirname, '../tmp/icu') +await fs.promises.mkdir(icuDir, { recursive: true }) + +const zipFromBuffer = promisify(yauzl.fromBuffer) + +// Get the ICU version and endianness +const [icuMajor, icuMinor] = process.versions.icu.split('.') +const icuEndianness = process.config.variables.icu_endianness.toLowerCase() + +// Download the ICU data +const response = await fetch( + `https://github.com/unicode-org/icu/releases/download/release-${icuMajor}-${icuMinor}/icu4c-${icuMajor}_${icuMinor}-data-bin-${icuEndianness}.zip` +) + +if (!response.ok) { + throw new Error(`Failed to download ICU data: ${response.statusText}`) +} + +// Extract the ICU data +const zip = await zipFromBuffer(Buffer.from(await response.arrayBuffer()), { + lazyEntries: true, +}) + +const icuDat = await new Promise((res, rej) => { + zip.on('error', (err) => rej(err)) + zip.on('entry', async (entry) => { + if (/icudt\d+.\.dat/.test(entry.fileName)) { + zip.openReadStream(entry, (err, readStream) => { + if (err) return rej(err) + + const output = path.join(icuDir, entry.fileName) + + readStream.pipe(fs.createWriteStream(output)) + res(output) + }) + } else { + zip.readEntry() + } + }) + zip.on('end', () => rej(meta)) + zip.readEntry() +}) + +// Print the relative path to the ICU data from the project root +console.log(path.relative(path.join(__dirname, '../'), icuDat))