|
import { constants as fsConstants } from 'node:fs'; |
|
import fs from 'node:fs/promises'; |
|
import https from 'node:https'; |
|
import path from 'node:path'; |
|
import url from 'node:url'; |
|
import extractZip from 'extract-zip'; |
|
import { fdir } from 'fdir'; |
|
import { temporaryDirectoryTask } from 'tempy'; |
|
import _ from 'lodash'; |
|
|
|
const downloadFile = async (url, dstPath) => { |
|
console.info('[DownloadFonts] Downloading:', url, '=>', dstPath); |
|
|
|
const fh = await fs.open(dstPath, 'wx'); |
|
const writeStream = fh.createWriteStream(); |
|
|
|
return new Promise((resolve, reject) => { |
|
const req = https.request(url, { method: 'get' }, (res) => { |
|
res.pipe(writeStream); |
|
res.on('error', reject); |
|
writeStream.on('finish', resolve); |
|
}); |
|
|
|
req.end(); |
|
}); |
|
}; |
|
|
|
const extractFile = async (archivePath, dstDir) => { |
|
console.info('[DownloadFonts] Extracting', archivePath, '=>', dstDir); |
|
return await extractZip(archivePath, { dir: dstDir }); |
|
}; |
|
|
|
const generateFontIndexFile = (fontFiles) => { |
|
return [ |
|
'// !!! DO NOT MODIFY THIS FILE !!!', |
|
'// !!! it is generated by our font download script !!!', |
|
'// !!! at package install time, and ignored by git !!!', |
|
'', |
|
'const fontFiles = {', |
|
...fontFiles.map((p) => { |
|
const ext = path.extname(p); |
|
const filenameWithoutExt = path.basename(p, ext); |
|
|
|
return ` '${filenameWithoutExt}': require('./${p}'),`; |
|
}), |
|
'};', |
|
'', |
|
'export default fontFiles;', |
|
'', |
|
] |
|
.map((l) => ` ${l}`) |
|
.join('\n'); |
|
}; |
|
|
|
/** |
|
* Downloads a font from Google Fonts. |
|
* |
|
* The file is downloaded as a .zip file, so we need to uncompress it to get its contents. |
|
* |
|
* @param {string} fontFamily |
|
*/ |
|
const downloadGoogleFont = async (fontFamily, dstDir) => { |
|
const url = `https://fonts.google.com/download?family=${encodeURIComponent(fontFamily)}`; |
|
|
|
return await temporaryDirectoryTask(async (tmpDir) => { |
|
console.info('[DownloadFonts] Using temporary dir:', tmpDir); |
|
|
|
try { |
|
// Download font archive |
|
const fontZipPath = path.join(tmpDir, 'font.zip'); |
|
await downloadFile(url, fontZipPath); |
|
|
|
// Extract font archive |
|
const extractDir = path.join(tmpDir, 'extracted'); |
|
await extractFile(fontZipPath, extractDir); |
|
|
|
// Gather paths of extracted files |
|
const crawler = new fdir() |
|
//.withBasePath().withFullPaths() |
|
.glob('**/*.*'); |
|
const fontFiles = await crawler.crawl(extractDir).withPromise(); |
|
|
|
// Remove pre-existing target dir (use with care!) |
|
if (process.argv.includes('--clean')) { |
|
try { |
|
await fs.access(dstDir, fsConstants.F_OK); |
|
try { |
|
console.info('[DownloadFonts] Removing existing target dir:', dstDir); |
|
await fs.rm(dstDir, { recursive: true }); |
|
} catch (err) { |
|
console.error('[DownloadFonts] Failed to remove existing target dir:', err); |
|
process.exit(1); |
|
} |
|
} catch (err) {} |
|
} |
|
|
|
try { |
|
await fs.access(dstDir, fsConstants.F_OK); |
|
console.info('[DownloadFonts] Reusing existing target dir:', dstDir); |
|
} catch (err) { |
|
console.info('[DownloadFonts] Creating target dir:', dstDir); |
|
await fs.mkdir(dstDir, { recursive: true }); |
|
} |
|
|
|
// Copy to the target dir |
|
console.info('[DownloadFonts] Copying', fontFiles.length, 'files ...'); |
|
const maxFilenameLength = fontFiles.map((p) => p.length).reduce((acc, cur) => Math.max(acc, cur), 0); |
|
await Promise.all( |
|
fontFiles.map(async (filePath) => { |
|
const srcFile = path.join(extractDir, filePath); |
|
const dstFile = path.join(dstDir, filePath); |
|
console.debug(' ', filePath.padEnd(maxFilenameLength, ' '), '=>', dstFile); |
|
await fs.copyFile(srcFile, dstFile); |
|
}) |
|
); |
|
|
|
// Generate index file |
|
console.info('[DownloadFonts] Generating index file ...'); |
|
const indexFile = generateFontIndexFile(fontFiles.filter((p) => /\.(ttf|otf)$/i.test(p))); |
|
await fs.writeFile(path.join(dstDir, 'index.ts'), indexFile); |
|
|
|
console.info('[DownloadFonts] Use this to load the fonts:'); |
|
console.info(''); |
|
console.info(` const [fontsLoaded] = useFonts(require('assets/fonts/${fontFamily}').default);`); |
|
console.info(''); |
|
|
|
console.info('[DownloadFonts] Done.'); |
|
} catch (err) { |
|
console.error('[DownloadFonts] Error:', err); |
|
process.exit(1); |
|
} |
|
}); |
|
}; |
|
|
|
const main = async () => { |
|
const scriptDir = path.dirname(url.fileURLToPath(import.meta.url)); |
|
const fontsDir = path.join(scriptDir, '../assets/fonts'); |
|
|
|
await downloadGoogleFont('Barlow', path.join(fontsDir, 'Barlow')); |
|
}; |
|
|
|
await main(); |