Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,23 @@ All exports are available from `@podlink/icons`.
### Platform data

```ts
import { platforms, getPlatform, getIconData } from '@podlink/icons'

platforms // Platform[] — all platforms
getPlatform(id) // Platform | undefined
getIconData(id) // IconData | undefined
import {
platforms,
resolvePlatformId,
hasPlatformIcon,
getPlatform,
getIconData,
} from '@podlink/icons'

platforms // Platform[] — all platforms
resolvePlatformId(input) // PlatformId | undefined
hasPlatformIcon(input) // boolean
getPlatform(input) // Platform | undefined
getIconData(input) // IconData | undefined
```

Platform lookups accept canonical IDs and known aliases. They also normalize common slug input, so `amazon`, `Amazon Music`, and `amazon-music` all resolve to the canonical `amazonmusic` ID.

### Badge resolution

```ts
Expand Down Expand Up @@ -171,7 +181,7 @@ minifySvg(svgString) // minify SVG markup
### Types

```ts
import type { Platform, IconData, IconShape, ShapeDefinition } from '@podlink/icons'
import type { PlatformId, Platform, IconData, IconShape, ShapeDefinition } from '@podlink/icons'
```

## Shapes
Expand Down
12 changes: 10 additions & 2 deletions src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
export { platforms, getPlatform } from './platforms.js'
export { platforms, getPlatform, resolvePlatformId } from './platforms.js'
export { shapes } from './shapes.js'
export { resolveBadgeContent, resolveBadgeViewBox } from './resolve.js'
export { extractSvgContent, extractViewBox, prefixIds, minifySvg } from './svg.js'

export type { Platform, IconData, IconShape, ShapeDefinition } from './types.js'
export type { PlatformId } from '../generated/platform-ids.js'

// Re-export icon data access
import { iconDataMap } from '../generated/icons.js'
import type { IconData } from './types.js'
import { resolvePlatformId } from './platforms.js'

const warnedIds = new Set<string>()

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const __DEV__ = (globalThis as any).process?.env?.NODE_ENV !== 'production'

export function getIconData(platformId: string): IconData | undefined {
const data = iconDataMap[platformId]
const resolvedPlatformId = resolvePlatformId(platformId)
const data = resolvedPlatformId ? iconDataMap[resolvedPlatformId] : undefined
if (!data && __DEV__ && !warnedIds.has(platformId)) {
warnedIds.add(platformId)
console.warn(
Expand All @@ -24,3 +27,8 @@ export function getIconData(platformId: string): IconData | undefined {
}
return data
}

export function hasPlatformIcon(input: string): boolean {
const platformId = resolvePlatformId(input)
return platformId ? Boolean(iconDataMap[platformId]) : false
}
36 changes: 36 additions & 0 deletions src/core/platforms.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest'
import { getIconData, getPlatform, hasPlatformIcon, resolvePlatformId } from './index.js'

describe('platform resolution', () => {
it('returns canonical IDs unchanged', () => {
expect(resolvePlatformId('amazonmusic')).toBe('amazonmusic')
})

it('resolves metadata aliases to canonical IDs', () => {
expect(resolvePlatformId('amazon')).toBe('amazonmusic')
expect(getPlatform('amazon')?.id).toBe('amazonmusic')
})

it('normalizes common slug and vanity slug input', () => {
expect(resolvePlatformId('Amazon Music')).toBe('amazonmusic')
expect(resolvePlatformId('amazon-music')).toBe('amazonmusic')
})

it('returns undefined for unknown IDs', () => {
expect(resolvePlatformId('not-a-platform')).toBeUndefined()
expect(getPlatform('not-a-platform')).toBeUndefined()
})
})

describe('icon lookup resolution', () => {
it('accepts aliases in getIconData', () => {
expect(getIconData('amazon')).toBe(getIconData('amazonmusic'))
})

it('reports whether aliases and canonical IDs have icon data', () => {
expect(hasPlatformIcon('amazon')).toBe(true)
expect(hasPlatformIcon('amazonmusic')).toBe(true)
expect(hasPlatformIcon('greatpods')).toBe(true)
expect(hasPlatformIcon('not-a-platform')).toBe(false)
})
})
21 changes: 20 additions & 1 deletion src/core/platforms.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
import type { Platform } from './types.js'
import type { PlatformId } from '../generated/platform-ids.js'
import platformData from '../data/platforms.json'

const platformMap = new Map<string, Platform>()
const platformIdMap = new Map<string, PlatformId>()

function normalizePlatformInput(input: string): string {
return input
.trim()
.toLowerCase()
.replace(/[^a-z0-9]/g, '')
}

for (const p of platformData.platforms) {
platformMap.set(p.id, p)
platformIdMap.set(normalizePlatformInput(p.id), p.id as PlatformId)

for (const alias of p.aliases ?? []) {
platformIdMap.set(normalizePlatformInput(alias), p.id as PlatformId)
}
}

export const platforms: Platform[] = platformData.platforms

export function resolvePlatformId(input: string): PlatformId | undefined {
return platformIdMap.get(normalizePlatformInput(input))
}

export function getPlatform(id: string): Platform | undefined {
return platformMap.get(id)
const platformId = resolvePlatformId(id)
return platformId ? platformMap.get(platformId) : undefined
}
9 changes: 7 additions & 2 deletions src/core/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ export function extractSvgContent(svg: string): string {
}

/**
* Extract viewBox attribute from an SVG string
* Extract viewBox attribute from an SVG string.
* Returns '0 0 32 32' with a warning if no viewBox is found.
*/
export function extractViewBox(svg: string): string {
const m = svg.match(/viewBox=["']([^"']+)["']/)
return m ? m[1] : '0 0 32 32'
if (!m) {
console.warn('[@podlink/icons] SVG is missing a viewBox attribute, defaulting to "0 0 32 32".')
return '0 0 32 32'
}
return m[1]
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface Platform {
id: string
name: string
active: boolean
aliases?: string[]
guidelinesUrl?: string
}

Expand Down
11 changes: 11 additions & 0 deletions src/data/platforms.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"name": "Amazon Music",
"id": "amazonmusic",
"active": true,
"aliases": ["amazon"],
"guidelinesUrl": "https://podcasters.amazon.com/promotional-tools"
},
{
Expand Down Expand Up @@ -99,6 +100,11 @@
"active": true,
"guidelinesUrl": "https://goodpods.com/badges"
},
{
"name": "Great Pods",
"id": "greatpods",
"active": true
},
{
"name": "Hark",
"id": "hark",
Expand Down Expand Up @@ -153,6 +159,11 @@
"id": "moonfm",
"active": true
},
{
"name": "Netflix",
"id": "netflix",
"active": false
},
{
"name": "Overcast",
"id": "overcast",
Expand Down
8 changes: 8 additions & 0 deletions src/source-icons/greatpods/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/source-icons/netflix/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/source-icons/netflix/netflix-divided.eps
Binary file not shown.
Loading