1import { getConfig } from '@expo/config'; 2import { generateFaviconAsync, generateImageAsync } from '@expo/image-utils'; 3import fs from 'fs'; 4import path from 'path'; 5 6import { getUserDefinedFile } from './publicFolder'; 7 8const debug = require('debug')('expo:favicon') as typeof console.log; 9 10/** @returns the file system path for a user-defined favicon.ico file in the public folder. */ 11export function getUserDefinedFaviconFile(projectRoot: string): string | null { 12 return getUserDefinedFile(projectRoot, ['./favicon.ico']); 13} 14 15export async function getVirtualFaviconAssetsAsync( 16 projectRoot: string, 17 { basePath, outputDir }: { outputDir: string; basePath: string } 18): Promise<((html: string) => string) | null> { 19 const existing = getUserDefinedFaviconFile(projectRoot); 20 if (existing) { 21 debug('Using user-defined favicon.ico file.'); 22 return null; 23 } 24 25 const data = await getFaviconFromExpoConfigAsync(projectRoot); 26 27 if (!data) { 28 return null; 29 } 30 31 await Promise.all( 32 [data].map((asset) => { 33 const assetPath = path.join(outputDir, asset.path); 34 debug('Writing asset to disk: ' + assetPath); 35 return fs.promises.writeFile(assetPath, asset.source); 36 }) 37 ); 38 39 function injectFaviconTag(html: string): string { 40 if (!html.includes('</head>')) { 41 return html; 42 } 43 return html.replace( 44 '</head>', 45 `<link rel="shortcut icon" href="${basePath}/favicon.ico" /></head>` 46 ); 47 } 48 49 return injectFaviconTag; 50} 51 52export async function getFaviconFromExpoConfigAsync(projectRoot: string) { 53 const { exp } = getConfig(projectRoot); 54 55 const src = exp.web?.favicon ?? null; 56 if (!src) { 57 return null; 58 } 59 60 const dims = [16, 32, 48]; 61 const cacheType = 'favicon'; 62 63 const size = dims[dims.length - 1]; 64 const { source } = await generateImageAsync( 65 { projectRoot, cacheType }, 66 { 67 resizeMode: 'contain', 68 src, 69 backgroundColor: 'transparent', 70 width: size, 71 height: size, 72 name: `favicon-${size}.png`, 73 } 74 ); 75 76 const faviconBuffer = await generateFaviconAsync(source, dims); 77 78 return { source: faviconBuffer, path: 'favicon.ico' }; 79} 80