mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-18 18:59:15 +08:00
chore: add rollup-glob
This commit is contained in:
parent
ae57261fb9
commit
3535cdb84a
@ -62,6 +62,7 @@
|
||||
"@iconify-json/ph": "^1.2.1",
|
||||
"@ourongxing/eslint-config": "3.2.3-beta.6",
|
||||
"@ourongxing/tsconfig": "^0.0.4",
|
||||
"@rollup/pluginutils": "^5.1.3",
|
||||
"@tanstack/react-query": "^5.59.9",
|
||||
"@tanstack/router-devtools": "^1.64.0",
|
||||
"@tanstack/router-plugin": "^1.64.0",
|
||||
@ -75,11 +76,13 @@
|
||||
"eslint": "^9.12.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0-rc-77f43893-20241010",
|
||||
"eslint-plugin-react-refresh": "^0.4.12",
|
||||
"fast-glob": "^3.3.2",
|
||||
"favicons-scraper": "^1.3.2",
|
||||
"lint-staged": "^15.2.10",
|
||||
"mlly": "^1.7.2",
|
||||
"mockdate": "^3.0.5",
|
||||
"pnpm-patch-i": "^0.4.1",
|
||||
"rollup": "^4.24.0",
|
||||
"simple-git-hooks": "^2.11.1",
|
||||
"tsx": "^4.19.1",
|
||||
"typescript": "^5.6.3",
|
||||
|
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@ -120,6 +120,9 @@ importers:
|
||||
'@ourongxing/tsconfig':
|
||||
specifier: ^0.0.4
|
||||
version: 0.0.4
|
||||
'@rollup/pluginutils':
|
||||
specifier: ^5.1.3
|
||||
version: 5.1.3(rollup@4.24.0)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.59.9
|
||||
version: 5.59.16(react@18.3.1)
|
||||
@ -159,6 +162,9 @@ importers:
|
||||
eslint-plugin-react-refresh:
|
||||
specifier: ^0.4.12
|
||||
version: 0.4.13(eslint@9.13.0(jiti@2.3.3))
|
||||
fast-glob:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
favicons-scraper:
|
||||
specifier: ^1.3.2
|
||||
version: 1.3.2
|
||||
@ -174,6 +180,9 @@ importers:
|
||||
pnpm-patch-i:
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1
|
||||
rollup:
|
||||
specifier: ^4.24.0
|
||||
version: 4.24.0
|
||||
simple-git-hooks:
|
||||
specifier: ^2.11.1
|
||||
version: 2.11.1
|
||||
|
@ -2,7 +2,7 @@ import process from "node:process"
|
||||
import { TTL } from "@shared/consts"
|
||||
import type { SourceID, SourceResponse } from "@shared/types"
|
||||
import { sources } from "@shared/sources"
|
||||
import { sourcesGetters } from "#/sources"
|
||||
import { getters } from "#/getters"
|
||||
import { useCache } from "#/hooks/useCache"
|
||||
|
||||
export default defineEventHandler(async (event): Promise<SourceResponse> => {
|
||||
@ -10,7 +10,7 @@ export default defineEventHandler(async (event): Promise<SourceResponse> => {
|
||||
let id = getRouterParam(event, "id") as SourceID
|
||||
const query = getQuery(event)
|
||||
const latest = query.latest !== undefined && query.latest !== "false"
|
||||
const isValid = (id: SourceID) => !id || !sources[id] || !sourcesGetters[id]
|
||||
const isValid = (id: SourceID) => !id || !sources[id] || !getters[id]
|
||||
|
||||
if (isValid(id)) {
|
||||
const redirectID = sources?.[id]?.redirect
|
||||
@ -54,7 +54,7 @@ export default defineEventHandler(async (event): Promise<SourceResponse> => {
|
||||
}
|
||||
}
|
||||
|
||||
const data = (await sourcesGetters[id]()).slice(0, 30)
|
||||
const data = (await getters[id]()).slice(0, 30)
|
||||
logger.success(`fetch ${id} latest`)
|
||||
if (cacheTable) {
|
||||
if (event.context.waitUntil) event.context.waitUntil(cacheTable.set(id, data))
|
||||
|
16
server/getters.ts
Normal file
16
server/getters.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { typeSafeObjectEntries } from "@shared/type.util"
|
||||
import type { SourceID } from "@shared/types"
|
||||
import * as x from "glob:./sources/{*.ts,**/index.ts}"
|
||||
import type { SourceGetter } from "./types"
|
||||
|
||||
export const getters = (function () {
|
||||
const getters = {} as Record<SourceID, SourceGetter>
|
||||
typeSafeObjectEntries(x).forEach(([id, x]) => {
|
||||
if (x.default instanceof Function) {
|
||||
Object.assign(getters, { [id]: x.default })
|
||||
} else {
|
||||
Object.assign(getters, x.default)
|
||||
}
|
||||
})
|
||||
return getters
|
||||
})()
|
22
server/glob.d.ts
vendored
Normal file
22
server/glob.d.ts
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
/* eslint-disable */
|
||||
|
||||
declare module 'glob:./sources/{*.ts,**/index.ts}' {
|
||||
export const _36kr: typeof import('./sources/_36kr')
|
||||
export const cankaoxiaoxi: typeof import('./sources/cankaoxiaoxi')
|
||||
export const cls: typeof import('./sources/cls/index')
|
||||
export const coolapk: typeof import('./sources/coolapk/index')
|
||||
export const douyin: typeof import('./sources/douyin')
|
||||
export const fastbull: typeof import('./sources/fastbull')
|
||||
export const gelonghui: typeof import('./sources/gelonghui')
|
||||
export const ithome: typeof import('./sources/ithome')
|
||||
export const sputniknewscn: typeof import('./sources/sputniknewscn')
|
||||
export const thepaper: typeof import('./sources/thepaper')
|
||||
export const tieba: typeof import('./sources/tieba')
|
||||
export const toutiao: typeof import('./sources/toutiao')
|
||||
export const v2ex: typeof import('./sources/v2ex')
|
||||
export const wallstreetcn: typeof import('./sources/wallstreetcn')
|
||||
export const weibo: typeof import('./sources/weibo')
|
||||
export const xueqiu: typeof import('./sources/xueqiu')
|
||||
export const zaobao: typeof import('./sources/zaobao')
|
||||
export const zhihu: typeof import('./sources/zhihu')
|
||||
}
|
@ -20,19 +20,21 @@ interface Res {
|
||||
}[]
|
||||
}
|
||||
|
||||
export default defineSource(async () => {
|
||||
const url = "https://api.coolapk.com/v6/page/dataList?url=%2Ffeed%2FstatList%3FcacheExpires%3D300%26statType%3Dday%26sortField%3Ddetailnum%26title%3D%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&title=%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&subTitle=&page=1"
|
||||
const r: Res = await $fetch(url, {
|
||||
headers: await genHeaders(),
|
||||
})
|
||||
if (!r.data.length) throw new Error("Failed to fetch")
|
||||
return r.data.filter(k => k.id).map(i => ({
|
||||
id: i.id,
|
||||
title: i.editor_title || load(i.message).text().split("\n")[0],
|
||||
url: i.shareUrl,
|
||||
extra: {
|
||||
info: i.targetRow?.subTitle,
|
||||
// date: new Date(i.dateline * 1000).getTime(),
|
||||
},
|
||||
})).slice(0, 30)
|
||||
export default defineSource({
|
||||
coolapk: async () => {
|
||||
const url = "https://api.coolapk.com/v6/page/dataList?url=%2Ffeed%2FstatList%3FcacheExpires%3D300%26statType%3Dday%26sortField%3Ddetailnum%26title%3D%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&title=%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&subTitle=&page=1"
|
||||
const r: Res = await $fetch(url, {
|
||||
headers: await genHeaders(),
|
||||
})
|
||||
if (!r.data.length) throw new Error("Failed to fetch")
|
||||
return r.data.filter(k => k.id).map(i => ({
|
||||
id: i.id,
|
||||
title: i.editor_title || load(i.message).text().split("\n")[0],
|
||||
url: i.shareUrl,
|
||||
extra: {
|
||||
info: i.targetRow?.subTitle,
|
||||
// date: new Date(i.dateline * 1000).getTime(),
|
||||
},
|
||||
}))
|
||||
},
|
||||
})
|
||||
|
@ -17,12 +17,11 @@ export default defineSource(async () => {
|
||||
cookie: cookie.join("; "),
|
||||
},
|
||||
})
|
||||
return res.data.word_list
|
||||
.map((k) => {
|
||||
return {
|
||||
id: k.sentence_id,
|
||||
title: k.word,
|
||||
url: `https://www.douyin.com/hot/${k.sentence_id}`,
|
||||
}
|
||||
})
|
||||
return res.data.word_list.map((k) => {
|
||||
return {
|
||||
id: k.sentence_id,
|
||||
title: k.word,
|
||||
url: `https://www.douyin.com/hot/${k.sentence_id}`,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -1,41 +0,0 @@
|
||||
import type { DisabledSourceID, SourceID } from "@shared/types"
|
||||
import weibo from "./weibo"
|
||||
import zaobao from "./zaobao"
|
||||
import v2ex from "./v2ex"
|
||||
import ithome from "./ithome"
|
||||
import zhihu from "./zhihu"
|
||||
import cankaoxiaoxi from "./cankaoxiaoxi"
|
||||
import coolapk from "./coolapk"
|
||||
import kr36 from "./36kr"
|
||||
import wallstreetcn from "./wallstreetcn"
|
||||
import douyin from "./douyin"
|
||||
import toutiao from "./toutiao"
|
||||
import cls from "./cls"
|
||||
import sputniknewscn from "./sputniknewscn"
|
||||
import xueqiu from "./xueqiu"
|
||||
import gelonghui from "./gelonghui"
|
||||
import tieba from "./tieba"
|
||||
import thepaper from "./thepaper"
|
||||
import fastbull from "./fastbull"
|
||||
import type { SourceGetter } from "#/types"
|
||||
|
||||
export const sourcesGetters = {
|
||||
weibo,
|
||||
zaobao,
|
||||
...v2ex,
|
||||
ithome,
|
||||
zhihu,
|
||||
coolapk,
|
||||
cankaoxiaoxi,
|
||||
thepaper,
|
||||
sputniknewscn,
|
||||
...fastbull,
|
||||
...wallstreetcn,
|
||||
...xueqiu,
|
||||
gelonghui,
|
||||
douyin,
|
||||
...cls,
|
||||
toutiao,
|
||||
tieba,
|
||||
...kr36,
|
||||
} as Record<SourceID, SourceGetter> & Partial<Record<DisabledSourceID, SourceGetter>>
|
@ -19,19 +19,21 @@ interface Res {
|
||||
}[]
|
||||
}
|
||||
|
||||
export default defineSource(async () => {
|
||||
const url = "https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=20&desktop=true"
|
||||
const res: Res = await $fetch(url)
|
||||
return res.data
|
||||
.slice(0, 30)
|
||||
.map((k) => {
|
||||
return {
|
||||
id: k.target.id,
|
||||
title: k.target.title,
|
||||
extra: {
|
||||
icon: k.card_label?.night_icon,
|
||||
},
|
||||
url: `https://www.zhihu.com/question/${k.target.id}`,
|
||||
}
|
||||
})
|
||||
export default defineSource({
|
||||
zhihu: async () => {
|
||||
const url = "https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=20&desktop=true"
|
||||
const res: Res = await $fetch(url)
|
||||
return res.data
|
||||
.slice(0, 30)
|
||||
.map((k) => {
|
||||
return {
|
||||
id: k.target.id,
|
||||
title: k.target.title,
|
||||
extra: {
|
||||
icon: k.card_label?.night_icon,
|
||||
},
|
||||
url: `https://www.zhihu.com/question/${k.target.id}`,
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
78
tools/rollup-glob.ts
Normal file
78
tools/rollup-glob.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import path from "node:path"
|
||||
import { writeFile } from "node:fs/promises"
|
||||
import type { Plugin } from "rollup"
|
||||
import glob from "fast-glob"
|
||||
import type { FilterPattern } from "@rollup/pluginutils"
|
||||
import { createFilter, normalizePath } from "@rollup/pluginutils"
|
||||
import { projectDir } from "../shared/dir"
|
||||
|
||||
const ID_PREFIX = "glob:"
|
||||
const root = path.join(projectDir, "server")
|
||||
type GlobMap = Record<string /* name:pattern */, string[]>
|
||||
|
||||
export function RollopGlob(): Plugin {
|
||||
const map: GlobMap = {}
|
||||
const include: FilterPattern = []
|
||||
const exclude: FilterPattern = []
|
||||
const filter = createFilter(include, exclude)
|
||||
return {
|
||||
name: "rollup-glob",
|
||||
resolveId(id, src) {
|
||||
if (!id.startsWith(ID_PREFIX)) return
|
||||
if (!src || !filter(src)) return
|
||||
|
||||
return `${id}:${encodeURIComponent(src)}`
|
||||
},
|
||||
async load(id) {
|
||||
if (!id.startsWith(ID_PREFIX)) return
|
||||
|
||||
const [_, pattern, encodePath] = id.split(":")
|
||||
const currentPath = decodeURIComponent(encodePath)
|
||||
|
||||
const files = (
|
||||
await glob(pattern, {
|
||||
cwd: currentPath ? path.dirname(currentPath) : root,
|
||||
absolute: true,
|
||||
})
|
||||
)
|
||||
.map(file => normalizePath(file))
|
||||
.filter(file => file !== normalizePath(currentPath))
|
||||
.sort()
|
||||
map[pattern] = files
|
||||
|
||||
const contents = files.map((file) => {
|
||||
const r = file.replace("/index", "")
|
||||
const name = path.basename(r, path.extname(r))
|
||||
return `export * as ${name} from '${file}'\n`
|
||||
}).join("\n")
|
||||
|
||||
await writeTypeDeclaration(map, path.join(root, "glob"))
|
||||
|
||||
return `${contents}\n`
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async function writeTypeDeclaration(map: GlobMap, filename: string) {
|
||||
function relatePath(filepath: string) {
|
||||
return normalizePath(path.relative(path.dirname(filename), filepath))
|
||||
}
|
||||
|
||||
let declare = `/* eslint-disable */\n\n`
|
||||
|
||||
const sortedEntries = Object.entries(map).sort(([a], [b]) =>
|
||||
a.localeCompare(b),
|
||||
)
|
||||
|
||||
for (const [_idx, [id, files]] of sortedEntries.entries()) {
|
||||
declare += `declare module '${ID_PREFIX}${id}' {\n`
|
||||
for (const file of files) {
|
||||
const relative = `./${relatePath(file)}`.replace(/\.tsx?$/, "")
|
||||
const r = file.replace("/index", "")
|
||||
const fileName = path.basename(r, path.extname(r))
|
||||
declare += ` export const ${fileName}: typeof import('${relative}')\n`
|
||||
}
|
||||
declare += `}\n`
|
||||
}
|
||||
await writeFile(`${filename}.d.ts`, declare, "utf-8")
|
||||
}
|
@ -8,5 +8,5 @@
|
||||
"@shared/*": ["shared/*"]
|
||||
}
|
||||
},
|
||||
"include": ["server", "*.config.*", "shared", "test", "scripts", "dist/.nitro/types"]
|
||||
"include": ["server", "*.config.*", "shared", "test", "scripts", "tools", "dist/.nitro/types"]
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import dotenv from "dotenv"
|
||||
import type { VitePWAOptions } from "vite-plugin-pwa"
|
||||
import { VitePWA } from "vite-plugin-pwa"
|
||||
import { projectDir } from "./shared/dir"
|
||||
import { RollopGlob } from "./tools/rollup-glob"
|
||||
|
||||
dotenv.config({
|
||||
path: join(projectDir, ".env.server"),
|
||||
@ -62,6 +63,9 @@ const nitroOption: Parameters<typeof nitro>[0] = {
|
||||
experimental: {
|
||||
database: true,
|
||||
},
|
||||
rollupConfig: {
|
||||
plugins: [RollopGlob()],
|
||||
},
|
||||
sourceMap: false,
|
||||
database: {
|
||||
default: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user