import fs from 'fs'; import path from 'path'; import {promisify} from 'util'; import fetch from 'cross-fetch'; import {name} from '../../../package.json'; import defaultIcon from '../images/default-icon.png'; import Image from './image'; const readFile = promisify(fs.readFile); const writeFile = promisify(fs.writeFile); const mkdir = promisify(fs.mkdir); const safeJoin = (root, file) => { root = path.join(root, '/'); const joined = path.join(root, file); if (joined.startsWith(root)) { return joined; } return null; }; const cacheDirectory = path.join(__dirname, '..', '.packager-cache'); const getCachePath = async (asset) => { if (!asset.sha256) return null; const path = safeJoin(cacheDirectory, asset.sha256); if (path) { await mkdir(cacheDirectory, { recursive: true }); return path; } return null; }; const nodeBufferToArrayBuffer = (buffer) => buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); const arrayBufferToNodeBuffer = (buffer) => Buffer.from(buffer); class NodeAdapter { async getCachedAsset (asset) { const urls = asset.src; const src = Array.isArray(urls) ? urls[0] : urls; if (src.startsWith('scaffolding/')) { const file = safeJoin(__dirname, src); if (file) { return readFile(file, 'utf-8'); } } if (src.startsWith('https:')) { const cachePath = await getCachePath(asset); if (cachePath) { try { return nodeBufferToArrayBuffer(await readFile(cachePath)); } catch (e) { // ignore } } console.log(`[${name}]: downloading large asset ${src}; this may take a while`); const res = await fetch(src); if (!res.ok) { throw new Error(`Unexpected status code: ${res.status}`); } const arrayBuffer = await res.arrayBuffer(); if (cachePath) { await writeFile(cachePath, arrayBufferToNodeBuffer(arrayBuffer)); } return arrayBuffer; } } async cacheAsset (asset, result) { // all of our caching logic lives in getCachedAsset } async getAppIcon (file) { if (!file) { // Use the default icon when no icon is given const buffer = await readFile(path.join(__dirname, defaultIcon)); return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); } if (file instanceof Image) { if (file.mimeType === 'image/png') { return file.readAsBuffer(); } throw new Error('icon must be of type image/png but found ' + file.mimeType + ' instead.'); } throw new Error('file must be an instance of Packager.Image or null but found ' + file + ' instead.'); } readAsURL (file) { if (!(file instanceof Image)) { throw new Error('file must be an instance of Packager.Image but found ' + file + ' instead.'); } return file.readAsURL(); } } export default NodeAdapter;