diff --git a/auto-imports.d.ts b/auto-imports.d.ts deleted file mode 100644 index b30f4ef..0000000 --- a/auto-imports.d.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* eslint-disable */ -/* prettier-ignore */ -// @ts-nocheck -// noinspection JSUnusedGlobalSymbols -// Generated by unplugin-auto-import -// biome-ignore lint: disable -export {} -declare global { - const $fetch: typeof import('ofetch')['$fetch'] - const afterAll: typeof import('vitest')['afterAll'] - const afterEach: typeof import('vitest')['afterEach'] - const appendCorsHeaders: typeof import('h3')['appendCorsHeaders'] - const appendCorsPreflightHeaders: typeof import('h3')['appendCorsPreflightHeaders'] - const appendHeader: typeof import('h3')['appendHeader'] - const appendHeaders: typeof import('h3')['appendHeaders'] - const appendResponseHeader: typeof import('h3')['appendResponseHeader'] - const appendResponseHeaders: typeof import('h3')['appendResponseHeaders'] - const assert: typeof import('vitest')['assert'] - const assertMethod: typeof import('h3')['assertMethod'] - const beforeAll: typeof import('vitest')['beforeAll'] - const beforeEach: typeof import('vitest')['beforeEach'] - const callNodeListener: typeof import('h3')['callNodeListener'] - const chai: typeof import('vitest')['chai'] - const clearResponseHeaders: typeof import('h3')['clearResponseHeaders'] - const clearSession: typeof import('h3')['clearSession'] - const createApp: typeof import('h3')['createApp'] - const createAppEventHandler: typeof import('h3')['createAppEventHandler'] - const createError: typeof import('h3')['createError'] - const createEvent: typeof import('h3')['createEvent'] - const createEventStream: typeof import('h3')['createEventStream'] - const createRouter: typeof import('h3')['createRouter'] - const day: typeof import('./server/utils/date')['day'] - const defaultContentType: typeof import('h3')['defaultContentType'] - const defineEventHandler: typeof import('h3')['defineEventHandler'] - const defineFallbackSource: typeof import('./server/utils/source')['defineFallbackSource'] - const defineLazyEventHandler: typeof import('h3')['defineLazyEventHandler'] - const defineNodeListener: typeof import('h3')['defineNodeListener'] - const defineNodeMiddleware: typeof import('h3')['defineNodeMiddleware'] - const defineRSSHubSource: typeof import('./server/utils/source')['defineRSSHubSource'] - const defineRSSSource: typeof import('./server/utils/source')['defineRSSSource'] - const defineRequestMiddleware: typeof import('h3')['defineRequestMiddleware'] - const defineResponseMiddleware: typeof import('h3')['defineResponseMiddleware'] - const defineSource: typeof import('./server/utils/source')['defineSource'] - const defineWebSocket: typeof import('h3')['defineWebSocket'] - const defineWebSocketHandler: typeof import('h3')['defineWebSocketHandler'] - const deleteCookie: typeof import('h3')['deleteCookie'] - const describe: typeof import('vitest')['describe'] - const dynamicEventHandler: typeof import('h3')['dynamicEventHandler'] - const eventHandler: typeof import('h3')['eventHandler'] - const expect: typeof import('vitest')['expect'] - const fetchWithEvent: typeof import('h3')['fetchWithEvent'] - const fromNodeMiddleware: typeof import('h3')['fromNodeMiddleware'] - const fromPlainHandler: typeof import('h3')['fromPlainHandler'] - const fromWebHandler: typeof import('h3')['fromWebHandler'] - const getCookie: typeof import('h3')['getCookie'] - const getHeader: typeof import('h3')['getHeader'] - const getHeaders: typeof import('h3')['getHeaders'] - const getMethod: typeof import('h3')['getMethod'] - const getProxyRequestHeaders: typeof import('h3')['getProxyRequestHeaders'] - const getQuery: typeof import('h3')['getQuery'] - const getRequestFingerprint: typeof import('h3')['getRequestFingerprint'] - const getRequestHeader: typeof import('h3')['getRequestHeader'] - const getRequestHeaders: typeof import('h3')['getRequestHeaders'] - const getRequestHost: typeof import('h3')['getRequestHost'] - const getRequestIP: typeof import('h3')['getRequestIP'] - const getRequestPath: typeof import('h3')['getRequestPath'] - const getRequestProtocol: typeof import('h3')['getRequestProtocol'] - const getRequestURL: typeof import('h3')['getRequestURL'] - const getRequestWebStream: typeof import('h3')['getRequestWebStream'] - const getResponseHeader: typeof import('h3')['getResponseHeader'] - const getResponseHeaders: typeof import('h3')['getResponseHeaders'] - const getResponseStatus: typeof import('h3')['getResponseStatus'] - const getResponseStatusText: typeof import('h3')['getResponseStatusText'] - const getRouterParam: typeof import('h3')['getRouterParam'] - const getRouterParams: typeof import('h3')['getRouterParams'] - const getSession: typeof import('h3')['getSession'] - const getValidatedQuery: typeof import('h3')['getValidatedQuery'] - const getValidatedRouterParams: typeof import('h3')['getValidatedRouterParams'] - const handleCacheHeaders: typeof import('h3')['handleCacheHeaders'] - const handleCors: typeof import('h3')['handleCors'] - const isCorsOriginAllowed: typeof import('h3')['isCorsOriginAllowed'] - const isError: typeof import('h3')['isError'] - const isEvent: typeof import('h3')['isEvent'] - const isEventHandler: typeof import('h3')['isEventHandler'] - const isMethod: typeof import('h3')['isMethod'] - const isPreflightRequest: typeof import('h3')['isPreflightRequest'] - const isStream: typeof import('h3')['isStream'] - const isWebResponse: typeof import('h3')['isWebResponse'] - const it: typeof import('vitest')['it'] - const lazyEventHandler: typeof import('h3')['lazyEventHandler'] - const logger: typeof import('./server/utils/logger')['logger'] - const ofetch: typeof import('ofetch')['ofetch'] - const parseCookies: typeof import('h3')['parseCookies'] - const promisifyNodeListener: typeof import('h3')['promisifyNodeListener'] - const proxyRequest: typeof import('h3')['proxyRequest'] - const readBody: typeof import('h3')['readBody'] - const readFormData: typeof import('h3')['readFormData'] - const readMultipartFormData: typeof import('h3')['readMultipartFormData'] - const readRawBody: typeof import('h3')['readRawBody'] - const readValidatedBody: typeof import('h3')['readValidatedBody'] - const removeResponseHeader: typeof import('h3')['removeResponseHeader'] - const rss2json: typeof import('./server/utils/rss2json')['rss2json'] - const sanitizeStatusCode: typeof import('h3')['sanitizeStatusCode'] - const sanitizeStatusMessage: typeof import('h3')['sanitizeStatusMessage'] - const sealSession: typeof import('h3')['sealSession'] - const send: typeof import('h3')['send'] - const sendError: typeof import('h3')['sendError'] - const sendIterable: typeof import('h3')['sendIterable'] - const sendNoContent: typeof import('h3')['sendNoContent'] - const sendProxy: typeof import('h3')['sendProxy'] - const sendRedirect: typeof import('h3')['sendRedirect'] - const sendStream: typeof import('h3')['sendStream'] - const sendWebResponse: typeof import('h3')['sendWebResponse'] - const serveStatic: typeof import('h3')['serveStatic'] - const setCookie: typeof import('h3')['setCookie'] - const setHeader: typeof import('h3')['setHeader'] - const setHeaders: typeof import('h3')['setHeaders'] - const setResponseHeader: typeof import('h3')['setResponseHeader'] - const setResponseHeaders: typeof import('h3')['setResponseHeaders'] - const setResponseStatus: typeof import('h3')['setResponseStatus'] - const splitCookiesString: typeof import('h3')['splitCookiesString'] - const suite: typeof import('vitest')['suite'] - const test: typeof import('vitest')['test'] - const toEventHandler: typeof import('h3')['toEventHandler'] - const toNodeListener: typeof import('h3')['toNodeListener'] - const toPlainHandler: typeof import('h3')['toPlainHandler'] - const toWebHandler: typeof import('h3')['toWebHandler'] - const toWebRequest: typeof import('h3')['toWebRequest'] - const tranformToUTC: typeof import('./server/utils/date')['tranformToUTC'] - const unsealSession: typeof import('h3')['unsealSession'] - const updateSession: typeof import('h3')['updateSession'] - const useBase: typeof import('h3')['useBase'] - const useSession: typeof import('h3')['useSession'] - const vi: typeof import('vitest')['vi'] - const vitest: typeof import('vitest')['vitest'] - const writeEarlyHints: typeof import('h3')['writeEarlyHints'] -} diff --git a/server/sources/36kr.ts b/server/sources/36kr.ts new file mode 100644 index 0000000..476ba6f --- /dev/null +++ b/server/sources/36kr.ts @@ -0,0 +1,30 @@ +import type { NewsItem } from "@shared/types" +import { load } from "cheerio" +import { $fetch } from "ofetch" + +export default defineSource(async () => { + const url = "https://www.36kr.com/newsflashes" + const response = await $fetch(url) + const $ = load(response) + const news: NewsItem[] = [] + const $items = $(".news-list-item") + $items.each((_, el) => { + const $el = $(el) + const $a = $el.find("a") + const url = $a.attr("href") + const title = $a.text() + const relativeDate = $el.find(".time") + if (url && title && relativeDate) { + news.push({ + url, + title, + id: url, + extra: { + date: parseRelativeDate(relativeDate.text()), + }, + }) + } + }) + + return news.slice(0, 20) +}) diff --git a/server/sources/coolapk/index.ts b/server/sources/coolapk/index.ts index 0be6532..ea0b08f 100644 --- a/server/sources/coolapk/index.ts +++ b/server/sources/coolapk/index.ts @@ -31,7 +31,8 @@ export default defineSource(async () => { title: i.editor_title || load(i.message).text().split("\n")[0], url: i.shareUrl, extra: { - date: new Date(i.dateline * 1000).getTime(), + info: i.targetRow?.subTitle, + // date: new Date(i.dateline * 1000).getTime(), }, })).slice(0, 20) }) diff --git a/server/utils/date.test.ts b/server/utils/date.test.ts index 353b195..8ac8f1a 100644 --- a/server/utils/date.test.ts +++ b/server/utils/date.test.ts @@ -1,4 +1,165 @@ import { describe, expect, it } from "vitest" +import MockDate from "mockdate" + +describe("parseRelativeDate", () => { + const second = 1000 + const minute = 60 * second + const hour = 60 * minute + const day = 24 * hour + const week = 7 * day + const month = 30 * day + const year = 365 * day + const date = new Date() + + const weekday = (d: number) => +new Date(date.getFullYear(), date.getMonth(), date.getDate() + d - (date.getDay() > d ? date.getDay() : date.getDay() + 7)) + + // 固定时间 + MockDate.set(date) + + it("s秒钟前", () => { + expect(+new Date(parseRelativeDate("10秒前"))).toBe(+date - 10 * second) + }) + + it("m分钟前", () => { + expect(+new Date(parseRelativeDate("10分钟前"))).toBe(+date - 10 * minute) + }) + + it("m分鐘前", () => { + expect(+new Date(parseRelativeDate("10分鐘前"))).toBe(+date - 10 * minute) + }) + + it("m分钟后", () => { + expect(+new Date(parseRelativeDate("10分钟后"))).toBe(+date + 10 * minute) + }) + + it("a minute ago", () => { + expect(+new Date(parseRelativeDate("a minute ago"))).toBe(+date - 1 * minute) + }) + + it("s minutes ago", () => { + expect(+new Date(parseRelativeDate("10 minutes ago"))).toBe(+date - 10 * minute) + }) + + it("s mins ago", () => { + expect(+new Date(parseRelativeDate("10 mins ago"))).toBe(+date - 10 * minute) + }) + + it("in s minutes", () => { + expect(+new Date(parseRelativeDate("in 10 minutes"))).toBe(+date + 10 * minute) + }) + + it("in an hour", () => { + expect(+new Date(parseRelativeDate("in an hour"))).toBe(+date + 1 * hour) + }) + + it("h小时前", () => { + expect(+new Date(parseRelativeDate("10小时前"))).toBe(+date - 10 * hour) + }) + + it("h个小时前", () => { + expect(+new Date(parseRelativeDate("10个小时前"))).toBe(+date - 10 * hour) + }) + + it("d天前", () => { + expect(+new Date(parseRelativeDate("10天前"))).toBe(+date - 10 * day) + }) + + it("w周前", () => { + expect(+new Date(parseRelativeDate("10周前"))).toBe(+date - 10 * week) + }) + + it("w星期前", () => { + expect(+new Date(parseRelativeDate("10星期前"))).toBe(+date - 10 * week) + }) + + it("w个星期前", () => { + expect(+new Date(parseRelativeDate("10个星期前"))).toBe(+date - 10 * week) + }) + + it("m月前", () => { + expect(+new Date(parseRelativeDate("1月前"))).toBe(+date - 1 * month) + }) + + it("m个月前", () => { + expect(+new Date(parseRelativeDate("1个月前"))).toBe(+date - 1 * month) + }) + + it("y年前", () => { + expect(+new Date(parseRelativeDate("1年前"))).toBe(+date - 1 * year) + }) + + it("y年M个月前", () => { + expect(+new Date(parseRelativeDate("1年1个月前"))).toBe(+date - 1 * year - 1 * month) + }) + + it("d天H小时前", () => { + expect(+new Date(parseRelativeDate("1天1小时前"))).toBe(+date - 1 * day - 1 * hour) + }) + + it("h小时m分钟s秒钟前", () => { + expect(+new Date(parseRelativeDate("1小时1分钟1秒钟前"))).toBe(+date - 1 * hour - 1 * minute - 1 * second) + }) + + it("dd Hh mm ss ago", () => { + expect(+new Date(parseRelativeDate("1d 1h 1m 1s ago"))).toBe(+date - 1 * day - 1 * hour - 1 * minute - 1 * second) + }) + + it("h小时m分钟s秒钟后", () => { + expect(+new Date(parseRelativeDate("1小时1分钟1秒钟后"))).toBe(+date + 1 * hour + 1 * minute + 1 * second) + }) + + it("今天", () => { + expect(+new Date(parseRelativeDate("今天"))).toBe(+date.setHours(0, 0, 0, 0)) + }) + + it("today H:m", () => { + expect(+new Date(parseRelativeDate("Today 08:00"))).toBe(+date + 8 * hour) + }) + + it("today, h:m a", () => { + expect(+new Date(parseRelativeDate("Today, 8:00 pm"))).toBe(+date + 20 * hour) + }) + + it("tDA H:m:s", () => { + expect(+new Date(parseRelativeDate("TDA 08:00:00"))).toBe(+date + 8 * hour) + }) + + it("今天 H:m", () => { + expect(+new Date(parseRelativeDate("今天 08:00"))).toBe(+date + 8 * hour) + }) + + it("今天H点m分", () => { + expect(+new Date(parseRelativeDate("今天8点0分"))).toBe(+date + 8 * hour) + }) + + it("昨日H点m分s秒", () => { + expect(+new Date(parseRelativeDate("昨日20时0分0秒"))).toBe(+date - 4 * hour) + }) + + it("前天 H:m", () => { + expect(+new Date(parseRelativeDate("前天 20:00"))).toBe(+date - 1 * day - 4 * hour) + }) + + it("明天 H:m", () => { + expect(+new Date(parseRelativeDate("明天 20:00"))).toBe(+date + 1 * day + 20 * hour) + }) + + it("星期几 h:m", () => { + expect(+new Date(parseRelativeDate("星期一 8:00"))).toBe(weekday(1) + 8 * hour) + }) + + it("周几 h:m", () => { + expect(+new Date(parseRelativeDate("周二 8:00"))).toBe(weekday(2) + 8 * hour) + }) + + it("星期天 h:m", () => { + expect(+new Date(parseRelativeDate("星期天 8:00"))).toBe(weekday(7) + 8 * hour) + }) + + it("invalid", () => { + expect(parseRelativeDate("RSSHub")).toBe("RSSHub") + }) +}) describe("transform Beijing time to UTC in different timezone", () => { const a = "2024/10/3 12:26:16" diff --git a/server/utils/date.ts b/server/utils/date.ts index 17b6326..334c85f 100644 --- a/server/utils/date.ts +++ b/server/utils/date.ts @@ -1,9 +1,17 @@ import dayjs from "dayjs" import utcPlugin from "dayjs/plugin/utc.js" import timezonePlugin from "dayjs/plugin/timezone.js" +import customParseFormat from "dayjs/plugin/customParseFormat" +import duration from "dayjs/plugin/duration" +import isSameOrBefore from "dayjs/plugin/isSameOrBefore" +import weekday from "dayjs/plugin/weekday" dayjs.extend(utcPlugin) dayjs.extend(timezonePlugin) +dayjs.extend(customParseFormat) +dayjs.extend(duration) +dayjs.extend(isSameOrBefore) +dayjs.extend(weekday) /** * 传入任意时区的时间(不携带时区),转换为 UTC 时间 @@ -14,3 +22,201 @@ export function tranformToUTC(date: string, format?: string, timezone: string = } export const day = dayjs + +const words = [ + { + startAt: dayjs(), + regExp: /^(?:今[天日]|to?day?)(.*)/, + }, + { + startAt: dayjs().subtract(1, "days"), + regExp: /^(?:昨[天日]|y(?:ester)?day?)(.*)/, + }, + { + startAt: dayjs().subtract(2, "days"), + regExp: /^(?:前天|(?:the)?d(?:ay)?b(?:eforeyesterda)?y)(.*)/, + }, + { + startAt: dayjs().isSameOrBefore(dayjs().weekday(1)) ? dayjs().weekday(1).subtract(1, "week") : dayjs().weekday(1), + regExp: /^(?:周|星期)一(.*)/, + }, + { + startAt: dayjs().isSameOrBefore(dayjs().weekday(2)) ? dayjs().weekday(2).subtract(1, "week") : dayjs().weekday(2), + regExp: /^(?:周|星期)二(.*)/, + }, + { + startAt: dayjs().isSameOrBefore(dayjs().weekday(3)) ? dayjs().weekday(3).subtract(1, "week") : dayjs().weekday(3), + regExp: /^(?:周|星期)三(.*)/, + }, + { + startAt: dayjs().isSameOrBefore(dayjs().weekday(4)) ? dayjs().weekday(4).subtract(1, "week") : dayjs().weekday(4), + regExp: /^(?:周|星期)四(.*)/, + }, + { + startAt: dayjs().isSameOrBefore(dayjs().weekday(5)) ? dayjs().weekday(5).subtract(1, "week") : dayjs().weekday(5), + regExp: /^(?:周|星期)五(.*)/, + }, + { + startAt: dayjs().isSameOrBefore(dayjs().weekday(6)) ? dayjs().weekday(6).subtract(1, "week") : dayjs().weekday(6), + regExp: /^(?:周|星期)六(.*)/, + }, + { + startAt: dayjs().isSameOrBefore(dayjs().weekday(7)) ? dayjs().weekday(7).subtract(1, "week") : dayjs().weekday(7), + regExp: /^(?:周|星期)[天日](.*)/, + }, + { + startAt: dayjs().add(1, "days"), + regExp: /^(?:明[天日]|y(?:ester)?day?)(.*)/, + }, + { + startAt: dayjs().add(2, "days"), + regExp: /^(?:[后後][天日]|(?:the)?d(?:ay)?a(?:fter)?t(?:omrrow)?)(.*)/, + }, +] + +const patterns = [ + { + unit: "years", + regExp: /(\d+)(?:年|y(?:ea)?rs?)/, + }, + { + unit: "months", + regExp: /(\d+)(?:[个個]?月|months?)/, + }, + { + unit: "weeks", + regExp: /(\d+)(?:周|[个個]?星期|weeks?)/, + }, + { + unit: "days", + regExp: /(\d+)(?:天|日|d(?:ay)?s?)/, + }, + { + unit: "hours", + regExp: /(\d+)(?:[个個]?(?:小?时|[時点點])|h(?:(?:ou)?r)?s?)/, + }, + { + unit: "minutes", + regExp: /(\d+)(?:分[鐘钟]?|m(?:in(?:ute)?)?s?)/, + }, + { + unit: "seconds", + regExp: /(\d+)(?:秒[鐘钟]?|s(?:ec(?:ond)?)?s?)/, + }, +] + +const patternSize = Object.keys(patterns).length + +/** + * 预处理日期字符串 + * @param {string} date 原始日期字符串 + */ +function toDate(date: string) { + return date + .toLowerCase() + .replace(/(^an?\s)|(\san?\s)/g, "1") // 替换 `a` 和 `an` 为 `1` + .replace(/几|幾/g, "3") // 如 `几秒钟前` 视作 `3秒钟前` + .replace(/[\s,]/g, "") +} // 移除所有空格 + +/** + * 将 `['\d+时', ..., '\d+秒']` 转换为 `{ hours: \d+, ..., seconds: \d+ }` + * 用于描述时间长度 + * @param {Array.} matches 所有匹配结果 + */ +function toDurations(matches: string[]) { + const durations: Record = {} + + let p = 0 + for (const m of matches) { + for (; p <= patternSize; p++) { + const match = patterns[p].regExp.exec(m) + if (match) { + durations[patterns[p].unit] = match[1] + break + } + } + } + return durations +} + +export const parseDate = (date: string | number, ...options: any) => dayjs(date, ...options).toDate() + +export function parseRelativeDate(date: string) { + // 预处理日期字符串 date + + const theDate = toDate(date) + + // 将 `\d+年\d+月...\d+秒前` 分割成 `['\d+年', ..., '\d+秒前']` + + const matches = theDate.match(/\D*\d+(?![:\-/]|(a|p)m)\D+/g) + + if (matches) { + // 获得最后的时间单元,如 `\d+秒前` + + const lastMatch = matches.pop() + + if (lastMatch) { + // 若最后的时间单元含有 `前`、`以前`、`之前` 等标识字段,减去相应的时间长度 + // 如 `1分10秒前` + + const beforeMatches = /(.*)(?:前|ago)$/.exec(lastMatch) + if (beforeMatches) { + matches.push(beforeMatches[1]) + // duration 这个插件有 bug,他会重新实现 subtract 这个方法,并且不会处理 weeks。用 ms 就可以调用默认的方法 + // 改成这样之后,月又出问题了,我也是服了 + return dayjs().subtract(dayjs.duration(toDurations(matches))).toDate() + } + + // 若最后的时间单元含有 `后`、`以后`、`之后` 等标识字段,加上相应的时间长度 + // 如 `1分10秒后` + + const afterMatches = /(?:^in(.*)|(.*)[后後])$/.exec(lastMatch) + if (afterMatches) { + matches.push(afterMatches[1] ?? afterMatches[2]) + return dayjs() + .add(dayjs.duration(toDurations(matches))) + .toDate() + } + + // 以下处理日期字符串 date 含有特殊词的情形 + // 如 `今天1点10分` + + matches.push(lastMatch) + } + const firstMatch = matches.shift() + + if (firstMatch) { + for (const w of words) { + const wordMatches = w.regExp.exec(firstMatch) + if (wordMatches) { + matches.unshift(wordMatches[1]) + + // 取特殊词对应日零时为起点,加上相应的时间长度 + + return w.startAt + .set("hour", 0) + .set("minute", 0) + .set("second", 0) + .set("millisecond", 0) + .add(dayjs.duration(toDurations(matches))) + .toDate() + } + } + } + } else { + // 若日期字符串 date 不匹配 patterns 中所有模式,则默认为 `特殊词 + 标准时间格式` 的情形,此时直接将特殊词替换为对应日期 + // 如今天为 `2022-03-22`,则 `今天 20:00` => `2022-03-22 20:00` + + for (const w of words) { + const wordMatches = w.regExp.exec(theDate) + if (wordMatches) { + // The default parser of dayjs() can parse '8:00 pm' but not '8:00pm' + // so we need to insert a space in between + return dayjs(`${w.startAt.format("YYYY-MM-DD")} ${/a|pm$/.test(wordMatches[1]) ? wordMatches[1].replace(/a|pm/, " $&") : wordMatches[1]}`).toDate() + } + } + } + + return date +} diff --git a/shared/sources.ts b/shared/sources.ts index d786995..3f60a3d 100644 --- a/shared/sources.ts +++ b/shared/sources.ts @@ -2,6 +2,7 @@ import { typeSafeObjectFromEntries } from "./type.util" import type { OriginSource, Source, SourceID } from "./types" const Time = { + test: 1, half: 30 * 60 * 1000, five: 5 * 60 * 1000, } diff --git a/src/components/section/card.tsx b/src/components/section/card.tsx index cd96926..137dc41 100644 --- a/src/components/section/card.tsx +++ b/src/components/section/card.tsx @@ -154,14 +154,16 @@ function Num({ num }: { num: number }) { function ExtraInfo({ item }: { item: NewsItem }) { const relativeTime = useRelativeTime(item?.extra?.date) - if (relativeTime) { - return <>{relativeTime} + if (item?.extra?.info) { + return <>{item.extra.info} } if (item?.extra?.icon) { - return ( - - ) + return + } + + if (relativeTime) { + return <>{relativeTime} } } diff --git a/vitest.config.ts b/vitest.config.ts index c001db9..9c6b894 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -25,6 +25,7 @@ export default defineConfig({ imports: ["$fetch", "ofetch"], }], dirs: ["server/utils"], + dts: "dist/.nitro/types/nitro-imports.d.ts", }), ], })