Skip to content

Commit

Permalink
feat(logo-favicon): add favicon.png support
Browse files Browse the repository at this point in the history
  • Loading branch information
Kikobeats committed Feb 25, 2024
1 parent 443aed3 commit 8f68b3e
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 28 deletions.
51 changes: 30 additions & 21 deletions packages/metascraper-logo-favicon/src/index.js
Expand Up @@ -107,23 +107,22 @@ const pickBiggerSize = async (sizes, { gotOpts } = {}) => {
pickBiggerSize.sortBySize = collection =>
orderBy(collection, ['size.priority'], ['desc'])

const favicon = async (url, { gotOpts } = {}) => {
const faviconUrl = logo('/favicon.ico', { url })
if (!faviconUrl) return undefined

const response = await reachableUrl(faviconUrl, gotOpts)
const contentType = response.headers['content-type']

const isValidContenType =
contentType &&
['image/vnd.microsoft.icon', 'image/x-icon'].some(ct =>
contentType.includes(ct)
)

return isValidContenType && reachableUrl.isReachable(response)
? response.url
: undefined
}
const createFavicon =
({ ext, contentTypes }) =>
async (url, { gotOpts } = {}) => {
const faviconUrl = logo(`/favicon.${ext}`, { url })
if (!faviconUrl) return undefined

const response = await reachableUrl(faviconUrl, gotOpts)
const contentType = response.headers['content-type']

const isValidContenType =
contentType && contentTypes.some(ct => contentType.includes(ct))

return isValidContenType && reachableUrl.isReachable(response)
? response.url
: undefined
}

const google = async (url, { gotOpts } = {}) => {
const response = await reachableUrl(google.url(url), gotOpts)
Expand All @@ -135,9 +134,19 @@ google.url = (url, size = 128) =>

const createGetLogo = ({ withGoogle, withFavicon, gotOpts, keyvOpts }) => {
const getLogo = async url => {
const providers = [withFavicon && favicon, withGoogle && google].filter(
Boolean
)
const providers = [
withFavicon &&
createFavicon({
ext: 'png',
contentTypes: ['image/png']
}),
withFavicon &&
createFavicon({
ext: 'ico',
contentTypes: ['image/vnd.microsoft.icon', 'image/x-icon']
}),
withGoogle && google
].filter(Boolean)

for (const provider of providers) {
const logoUrl = await provider(url, { gotOpts })
Expand Down Expand Up @@ -190,8 +199,8 @@ module.exports = ({
}
}

module.exports.favicon = favicon
module.exports.google = google
module.exports.createFavicon = createFavicon
module.exports.createRootFavicon = createRootFavicon
module.exports.createGetLogo = createGetLogo
module.exports.pickBiggerSize = pickBiggerSize
32 changes: 25 additions & 7 deletions packages/metascraper-logo-favicon/test/favicon.js
Expand Up @@ -2,44 +2,62 @@

const test = require('ava')

const { favicon } = require('..')
const { createFavicon } = require('..')

const { runServer } = require('./helpers')

const faviconPNG = createFavicon({ ext: 'png', contentTypes: ['image/png'] })
const faviconICO = createFavicon({
ext: 'ico',
contentTypes: ['image/vnd.microsoft.icon', 'image/x-icon']
})

test('return undefined if favicon is not reachable', async t => {
const url = 'https://idontexist.lol'
t.is(await favicon(url), undefined)
t.is(await faviconICO(url), undefined)
})

test("don't resolve favicon.ico with no content-type", async t => {
const url = await runServer(t, async ({ res }) => {
res.end('<svg></svg>')
})
t.is(await favicon(url), undefined)
t.is(await faviconICO(url), undefined)
})

test("don't resolve favicon.png with no content-type", async t => {
const url = await runServer(t, async ({ res }) => {
res.end('<svg></svg>')
})
t.is(await faviconPNG(url), undefined)
})

test("don't resolve favicon.ico with no valid content-type", async t => {
const url = await runServer(t, async ({ res }) => {
res.setHeader('content-type', 'image/svg+xml; charset=utf-8')
res.end('<svg></svg>')
})
t.is(await favicon(url), undefined)
t.is(await faviconICO(url), undefined)
})

test("favicon.png with 'image/png' content-type", async t => {
const url = 'https://adroll.com/'
t.is(await faviconPNG(url), 'https://adroll.com/favicon.png')
})

test("favicon.ico with 'image/vnd.microsoft.icon' content-type", async t => {
const url = 'https://microlink.io/'
t.is(await favicon(url), 'https://microlink.io/favicon.ico')
t.is(await faviconICO(url), 'https://microlink.io/favicon.ico')
})

test("favicon.ico with 'image/x-icon' content-type", async t => {
const url = 'https://2miners.com/'
t.is(await favicon(url), 'https://2miners.com/favicon.ico')
t.is(await faviconICO(url), 'https://2miners.com/favicon.ico')
})

test('handle redirects', async t => {
const url = await runServer(t, async ({ res }) => {
res.writeHead(301, { Location: 'https://microlink.io/favicon.ico' })
res.end()
})
t.is(await favicon(url), 'https://microlink.io/favicon.ico')
t.is(await faviconICO(url), 'https://microlink.io/favicon.ico')
})

0 comments on commit 8f68b3e

Please sign in to comment.