Skip to content

Commit

Permalink
refactor: simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
BlackGlory committed Mar 26, 2024
1 parent 1d515c5 commit ea04eea
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 45 deletions.
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,20 @@ yarn add parse-favicon
```js
import { parseFavicon } from 'parse-favicon'

const pageUrl = 'https://github.com'
const pageURL = 'https://github.com'

parseFavicon(pageUrl, textFetcher, bufferFetcher)
parseFavicon(pageURL, textFetcher, bufferFetcher)
.subscribe(icon => console.log(icon))

function textFetcher(url: string): Promise<string> {
return fetch(resolveURL(url, pageUrl))
return fetch(url)
.then(res => res.text())
}

function bufferFetcher(url: string): Promise<ArrayBuffer> {
return fetch(resolveURL(url, pageUrl))
return fetch(url)
.then(res => res.arrayBuffer())
}

function resolveURL(url: string, base: string): string {
return new URL(url, base).href
}
```

## API
Expand All @@ -51,7 +47,7 @@ interface ISize {
}

function parseFavicon(
url: string
pageURL: string
, textFetcher: TextFetcher
, bufferFetcher?: BufferFetcher
): Observable<IIcon>
Expand Down
48 changes: 32 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,34 @@ import { parseImage, IImage } from '@utils/parse-image.js'
import { Observable } from 'rxjs'
import { flatten, each } from 'iterable-operator'
import { IIcon, TextFetcher, BufferFetcher } from './types.js'
import { Awaitable } from '@blackglory/prelude'
export { IIcon, TextFetcher, BufferFetcher } from './types.js'

export function parseFavicon(
url: string
, textFetcher: TextFetcher
, bufferFetcher?: BufferFetcher
pageURL: string
, _fetchText: TextFetcher
, _fetchBuffer?: BufferFetcher
): Observable<IIcon> {
const fetchText: TextFetcher = (url: string): Awaitable<string> => {
const absoluteURL = new URL(url, pageURL).href
return _fetchText(absoluteURL)
}
const fetchBuffer: BufferFetcher | undefined =
_fetchBuffer
? (url: string): Awaitable<ArrayBuffer> => {
const absoluteURL = new URL(url, pageURL).href
return _fetchBuffer(absoluteURL)
}
: undefined

return new Observable(observer => {
parse(icon => observer.next(icon))
.then(() => observer.complete())
.catch(err => observer.error(err))
})

async function parse(publish: (icon: IIcon) => void) {
const html = await textFetcher(url)
const html = await fetchText(pageURL)

const icons = [
...parseAppleTouchIcons(html)
Expand All @@ -36,24 +49,24 @@ export function parseFavicon(
, ...parseWindows8Tiles(html)
]

if (bufferFetcher) {
if (fetchBuffer) {
const imagePromisePool = new Map<string, Promise<IImage | null>>()

icons.forEach(async icon => publish(
await tryUpdateIcon(bufferFetcher, imagePromisePool, icon)
await tryUpdateIcon(fetchBuffer, imagePromisePool, icon)
))

const results = await Promise.all([
parseIEConfig(html, textFetcher)
, parseManifest(html, textFetcher)
parseIEConfig(html, fetchText)
, parseManifest(html, fetchText)
])
each(flatten<IIcon>(results), async icon => {
publish(await tryUpdateIcon(bufferFetcher, imagePromisePool, icon))
publish(await tryUpdateIcon(fetchBuffer, imagePromisePool, icon))
})

getDefaultIconUrls().forEach(async url => {
if (!imagePromisePool.has(url)) {
imagePromisePool.set(url, fetchImage(bufferFetcher, url))
imagePromisePool.set(url, fetchImage(fetchBuffer, url))
}
const image = await imagePromisePool.get(url)
if (image) {
Expand All @@ -71,20 +84,23 @@ export function parseFavicon(
icons.forEach(publish)

const results = await Promise.all([
parseIEConfig(html, textFetcher)
, parseManifest(html, textFetcher)
parseIEConfig(html, fetchText)
, parseManifest(html, fetchText)
])
each(flatten<IIcon>(results), publish)
}
}

async function tryUpdateIcon(
bufferFetcher: BufferFetcher
fetchBuffer: BufferFetcher
, imagePromisePool: Map<string, Promise<IImage | null>>
, icon: IIcon
): Promise<IIcon> {
if (!imagePromisePool.has(icon.url)) {
imagePromisePool.set(icon.url, fetchImage(bufferFetcher, icon.url))
imagePromisePool.set(
icon.url
, fetchImage(fetchBuffer, new URL(icon.url, pageURL).href)
)
}
const image = await imagePromisePool.get(icon.url)
if (image) {
Expand All @@ -95,10 +111,10 @@ export function parseFavicon(
}

async function fetchImage(
bufferFetcher: BufferFetcher
fetchBuffer: BufferFetcher
, url: string
): Promise<IImage | null> {
const arrayBuffer = await getResultAsync(() => bufferFetcher(url))
const arrayBuffer = await getResultAsync(() => fetchBuffer(url))
if (!arrayBuffer) return null
const buffer = Buffer.from(arrayBuffer)

Expand Down
13 changes: 3 additions & 10 deletions src/parse-ie-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,24 @@ import { extractAttributes } from '@utils/extract-attributes.js'
import { IIcon, TextFetcher } from '@src/types.js'
import { isElement } from 'extra-dom'
import { flatten, toArray } from 'iterable-operator'
import { getResultAsync } from 'return-style'

export async function parseIEConfig(
html: string
, textFetcher: TextFetcher
, fetchText: TextFetcher
): Promise<IIcon[]> {
const document = parseHTML(html)
const configUrls = extractConfigURLs(document)
const icons = await map(configUrls, extractIconsFromURL)
return toArray(flatten(icons))

async function extractIconsFromURL(url: string): Promise<IIcon[]> {
const text = await fetch(url)
const text = await getResultAsync(() => fetchText(url))
if (text) {
return parseIEConfigIcons(text, url)
} else {
return []
}

async function fetch(url: string): Promise<string | null> {
try {
return await textFetcher(url)
} catch {
return null
}
}
}
}

Expand Down
13 changes: 3 additions & 10 deletions src/parse-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { parseSpaceSeparatedSizes } from '@utils/parse-space-separated-sizes.js'
import { IIcon, TextFetcher } from '@src/types.js'
import { extractAttributes } from '@utils/extract-attributes.js'
import { isObject, isArray, isString } from '@blackglory/prelude'
import { getResultAsync } from 'return-style'

interface IManifest {
icons: Array<{
Expand All @@ -20,27 +21,19 @@ interface IManifest {

export async function parseManifest(
html: string
, textFetcher: TextFetcher
, fetchText: TextFetcher
): Promise<IIcon[]> {
const document = parseHTML(html)
const manifestUrls = extractManifestURLs(document)
const results = await map(manifestUrls, async url => {
const text = await fetch(url)
const text = await getResultAsync(() => fetchText(url))
if (text) {
return extractManifestIcons(text, url)
} else {
return []
}
})
return ([] as IIcon[]).concat(...results)

async function fetch(url: string): Promise<string | null> {
try {
return await textFetcher(url)
} catch {
return null
}
}
}

function isManifest(val: unknown): val is IManifest {
Expand Down

0 comments on commit ea04eea

Please sign in to comment.