Skip to content

Commit

Permalink
refactor(fff): improve toJSONFeedItem function
Browse files Browse the repository at this point in the history
  • Loading branch information
kwaa committed Jan 11, 2024
1 parent b3d474e commit 1ecdec4
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .changeset/olive-roses-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fff-flavored-frontmatter": patch
---

Refactor(fff): improve `toJSONFeedItem` function
4 changes: 4 additions & 0 deletions docs/version/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ For non-version-related changes, please refer to the [git commit history](https:

- Modify: [`lang`](/version/1.2.html#lang) no longer supports arrays

### Version 1.2.1 (2024-01-11)

- Improve `toJSONFeedItem` function

## [Version 1.1](/version/1.1.html) (2023-08-31)

- Add: [`draft`](/version/1.1.html#draft) [`visibility`](/version/1.1.html#visibility)
Expand Down
131 changes: 96 additions & 35 deletions packages/fff-flavored-frontmatter/src/utils/feed.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,111 @@
import type { FFFFlavoredFrontmatter } from '../types.ts'

import { strict } from './presets.ts'
import { postTypeDiscovery } from './ptd.ts'
import { type PostType, postTypeDiscovery } from './ptd.ts'
import { transform } from './transform.ts'

/**
* Type validation for `toJSONFeedItem` function.
* @beta
* @see {@link https://jsonfeed.org/version/1.1/#items-a-name-items-a}
*/
/* eslint-disable perfectionist/sort-object-types */
export type JSONFeedItem = {
[key: `_${string}`]: Record<string, unknown> | undefined
[key: string]: unknown
// _indieweb?: Partial<JSONFeedItemIndieWeb>
id: string
url?: string
external_url?: string
title?: FFFFlavoredFrontmatter['title']
content_html?: string
content_text?: string
summary?: FFFFlavoredFrontmatter['summary']
image?: string
banner_image?: string
date_published?: string
date_modified?: string
authors?: FFFFlavoredFrontmatter['authors']
tags?: FFFFlavoredFrontmatter['tags']
language?: FFFFlavoredFrontmatter['lang']
attachments?: JSONFeedItemAttachment[]
}

/**
* JSON Feed IndieWeb Extension.
* @beta
* @see {@link https://indieweb.org/JSON_Feed#IndieWeb_Extension}
*/
export type JSONFeedItemIndieWeb = {
[key: string]: unknown
type: PostType & (string & {})
syndication?: FFFFlavoredFrontmatter['syndication']
'bookmark-of'?: FFFFlavoredFrontmatter['bookmark_of']
'in-reply-to'?: FFFFlavoredFrontmatter['in_reply_to']
'like-of'?: FFFFlavoredFrontmatter['like_of']
'repost-of'?: FFFFlavoredFrontmatter['repost_of']
}

/**
* Type validation for `toJSONFeedItem` function.
* @beta
* @see {@link https://www.jsonfeed.org/version/1.1/#attachments-a-name-attachments-a}
*/
export type JSONFeedItemAttachment = {
url: string
mime_type: string
title?: string
size_in_bytes?: number
duration_in_seconds?: number
}
/* eslint-enable perfectionist/sort-object-types */

/**
* From FFF to JSON Feed Item (Version 1.1)
* @beta
* @param fm - FFF Flavored Frontmatter
* @returns JSON Feed Item Object (without content / id / url)
* @returns JSON Feed Item Object (without content_\{text,html\} / id / url)
* @see {@link https://jsonfeed.org/version/1.1}
*/
export const toJSONFeedItem = (fm: FFFFlavoredFrontmatter, item?: Record<string, unknown>): Record<string, unknown> => {
fm = transform(fm as Record<string, unknown>, [strict({
categories: false,
media: {
array: false,
type: 'string',
},
})])
return {
...item,
/**
* JSON Feed IndieWeb Extension
* @see {@link https://indieweb.org/JSON_Feed#IndieWeb_Extension}
*/
_indieweb: {
'bookmark-of': fm.bookmark_of,
'in-reply-to': fm.in_reply_to,
'like-of': fm.like_of,
'repost-of': fm.repost_of,
'syndication': fm.syndication,
'type': postTypeDiscovery(fm),
},
authors: fm.authors,
date_modified: fm.updated,
date_published: fm.published ?? fm.created,
image: fm.image,
language: fm.lang,
summary: fm.summary,
tags: fm.tags,
title: fm.title,
// TODO: https://www.jsonfeed.org/version/1.1/#attachments-a-name-attachments-a
// TODO: https://www.jsonfeed.org/podcasting/
export const toJSONFeedItem
= <TInput extends Partial<JSONFeedItem> = Partial<JSONFeedItem>, TOutput extends TInput = TInput & { _indieweb: JSONFeedItemIndieWeb }>(fm: FFFFlavoredFrontmatter & Record<string, unknown>, item?: TInput): TOutput => {
fm = transform(fm as Record<string, unknown>, [strict({
categories: false,
media: {
array: false,
type: 'string',
},
})])
return {
...item,
/**
* Treats keys starts with underscore as Custom JSON Feed Extensions
* @see {@link https://www.jsonfeed.org/version/1.1/#extensions-a-name-extensions-a}
*/
...Object.fromEntries(
Object.entries(fm)
.filter(([key, value]) => key.startsWith('_') && typeof value === 'object'),
),
_indieweb: {
'bookmark-of': fm.bookmark_of,
'in-reply-to': fm.in_reply_to,
'like-of': fm.like_of,
'repost-of': fm.repost_of,
'syndication': fm.syndication,
'type': postTypeDiscovery(fm),
...fm._indieweb as Partial<JSONFeedItemIndieWeb>,
...item?._indieweb as Partial<JSONFeedItemIndieWeb>,
},
authors: fm.authors,
date_modified: fm.updated,
date_published: fm.published ?? fm.created,
image: fm.image,
language: fm.lang,
summary: fm.summary,
tags: fm.tags,
title: fm.title,
} as JSONFeedItem as TOutput
}
}

/**
* From FFF to JF2 Feed Child (Editor's Draft 09 February 2019)
Expand Down
54 changes: 44 additions & 10 deletions packages/fff-flavored-frontmatter/test/utils/feed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,32 +62,32 @@ describe('fff-flavored-frontmatter feed util', () => {
}
const bookmarkOfOutput = toJSONFeedItem(bookmarkOf)

expect((bookmarkOfOutput._indieweb as Record<string, string>).type).toEqual('bookmark')
expect((bookmarkOfOutput._indieweb as Record<string, string>)['bookmark-of']).toEqual(example)
expect(bookmarkOfOutput._indieweb.type).toEqual('bookmark')
expect(bookmarkOfOutput._indieweb?.['bookmark-of']).toEqual(example)

const inReplyTo: FFFFlavoredFrontmatter = {
in_reply_to: example,
}
const inReplyToOutput = toJSONFeedItem(inReplyTo)

expect((inReplyToOutput._indieweb as Record<string, string>).type).toEqual('reply')
expect((inReplyToOutput._indieweb as Record<string, string>)['in-reply-to']).toEqual(example)
expect(inReplyToOutput._indieweb.type).toEqual('reply')
expect(inReplyToOutput._indieweb?.['in-reply-to']).toEqual(example)

const likeOf: FFFFlavoredFrontmatter = {
like_of: example,
}
const likeOfOutput = toJSONFeedItem(likeOf)

expect((likeOfOutput._indieweb as Record<string, string>).type).toEqual('like')
expect((likeOfOutput._indieweb as Record<string, string>)['like-of']).toEqual(example)
expect(likeOfOutput._indieweb.type).toEqual('like')
expect(likeOfOutput._indieweb?.['like-of']).toEqual(example)

const repostOf: FFFFlavoredFrontmatter = {
repost_of: example,
}
const repostOfOutput = toJSONFeedItem(repostOf)

expect((repostOfOutput._indieweb as Record<string, string>).type).toEqual('repost')
expect((repostOfOutput._indieweb as Record<string, string>)['repost-of']).toEqual(example)
expect(repostOfOutput._indieweb.type).toEqual('repost')
expect(repostOfOutput._indieweb?.['repost-of']).toEqual(example)
})

it('custom', () => {
Expand All @@ -105,10 +105,44 @@ describe('fff-flavored-frontmatter feed util', () => {
url: 'http://jsonfeed.micro.blog/2020/08/07/json-feed-version.html',
})

expect((output._hatsu as Record<string, string>).about).toEqual('https://github.com/importantimport/hatsu/issues/1')
expect((output._hatsu as Record<string, string>).banner_image).toEqual('https://example.com/foo.png')
expect(output._hatsu.about).toEqual('https://github.com/importantimport/hatsu/issues/1')
expect(output._hatsu.banner_image).toEqual('https://example.com/foo.png')
expect(output.content_html).toEqual('Custom Item Test')
expect(output.id).toEqual('http://jsonfeed.micro.blog/2020/08/07/json-feed-version.html')
expect(output.url).toEqual('http://jsonfeed.micro.blog/2020/08/07/json-feed-version.html')
})

it('json feed extension', () => {
const input: FFFFlavoredFrontmatter & Record<string, unknown> = {
_hatsu: { about: 'https://github.com/importantimport/hatsu/issues/1' },
_indieweb: { hello: 'world' },
}
const output = toJSONFeedItem(input)

expect(output._hatsu?.about).toEqual('https://github.com/importantimport/hatsu/issues/1')
expect(output._indieweb.hello).toEqual('world')
})

it('override _indieweb', () => {
const input: FFFFlavoredFrontmatter & Record<string, unknown> = {
_indieweb: {
b: 'b',
syndication: 'https://fff.js.org/b',
type: 'rsvp',
},
syndication: 'https://fff.js.org/a',
}
const output = toJSONFeedItem(input, {
_indieweb: {
c: 'c',
syndication: 'https://fff.js.org/c',
type: 'article',
},
})

expect((output._indieweb as Record<string, string>).b).toEqual('b')
expect(output._indieweb.c).toEqual('c')
expect(output._indieweb.syndication).toEqual('https://fff.js.org/c')
expect(output._indieweb.type).toEqual('article')
})
})

0 comments on commit 1ecdec4

Please sign in to comment.