mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-19 03:09:14 +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",
|
"@iconify-json/ph": "^1.2.1",
|
||||||
"@ourongxing/eslint-config": "3.2.3-beta.6",
|
"@ourongxing/eslint-config": "3.2.3-beta.6",
|
||||||
"@ourongxing/tsconfig": "^0.0.4",
|
"@ourongxing/tsconfig": "^0.0.4",
|
||||||
|
"@rollup/pluginutils": "^5.1.3",
|
||||||
"@tanstack/react-query": "^5.59.9",
|
"@tanstack/react-query": "^5.59.9",
|
||||||
"@tanstack/router-devtools": "^1.64.0",
|
"@tanstack/router-devtools": "^1.64.0",
|
||||||
"@tanstack/router-plugin": "^1.64.0",
|
"@tanstack/router-plugin": "^1.64.0",
|
||||||
@ -75,11 +76,13 @@
|
|||||||
"eslint": "^9.12.0",
|
"eslint": "^9.12.0",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0-rc-77f43893-20241010",
|
"eslint-plugin-react-hooks": "^5.1.0-rc-77f43893-20241010",
|
||||||
"eslint-plugin-react-refresh": "^0.4.12",
|
"eslint-plugin-react-refresh": "^0.4.12",
|
||||||
|
"fast-glob": "^3.3.2",
|
||||||
"favicons-scraper": "^1.3.2",
|
"favicons-scraper": "^1.3.2",
|
||||||
"lint-staged": "^15.2.10",
|
"lint-staged": "^15.2.10",
|
||||||
"mlly": "^1.7.2",
|
"mlly": "^1.7.2",
|
||||||
"mockdate": "^3.0.5",
|
"mockdate": "^3.0.5",
|
||||||
"pnpm-patch-i": "^0.4.1",
|
"pnpm-patch-i": "^0.4.1",
|
||||||
|
"rollup": "^4.24.0",
|
||||||
"simple-git-hooks": "^2.11.1",
|
"simple-git-hooks": "^2.11.1",
|
||||||
"tsx": "^4.19.1",
|
"tsx": "^4.19.1",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
|
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@ -120,6 +120,9 @@ importers:
|
|||||||
'@ourongxing/tsconfig':
|
'@ourongxing/tsconfig':
|
||||||
specifier: ^0.0.4
|
specifier: ^0.0.4
|
||||||
version: 0.0.4
|
version: 0.0.4
|
||||||
|
'@rollup/pluginutils':
|
||||||
|
specifier: ^5.1.3
|
||||||
|
version: 5.1.3(rollup@4.24.0)
|
||||||
'@tanstack/react-query':
|
'@tanstack/react-query':
|
||||||
specifier: ^5.59.9
|
specifier: ^5.59.9
|
||||||
version: 5.59.16(react@18.3.1)
|
version: 5.59.16(react@18.3.1)
|
||||||
@ -159,6 +162,9 @@ importers:
|
|||||||
eslint-plugin-react-refresh:
|
eslint-plugin-react-refresh:
|
||||||
specifier: ^0.4.12
|
specifier: ^0.4.12
|
||||||
version: 0.4.13(eslint@9.13.0(jiti@2.3.3))
|
version: 0.4.13(eslint@9.13.0(jiti@2.3.3))
|
||||||
|
fast-glob:
|
||||||
|
specifier: ^3.3.2
|
||||||
|
version: 3.3.2
|
||||||
favicons-scraper:
|
favicons-scraper:
|
||||||
specifier: ^1.3.2
|
specifier: ^1.3.2
|
||||||
version: 1.3.2
|
version: 1.3.2
|
||||||
@ -174,6 +180,9 @@ importers:
|
|||||||
pnpm-patch-i:
|
pnpm-patch-i:
|
||||||
specifier: ^0.4.1
|
specifier: ^0.4.1
|
||||||
version: 0.4.1
|
version: 0.4.1
|
||||||
|
rollup:
|
||||||
|
specifier: ^4.24.0
|
||||||
|
version: 4.24.0
|
||||||
simple-git-hooks:
|
simple-git-hooks:
|
||||||
specifier: ^2.11.1
|
specifier: ^2.11.1
|
||||||
version: 2.11.1
|
version: 2.11.1
|
||||||
|
@ -2,7 +2,7 @@ import process from "node:process"
|
|||||||
import { TTL } from "@shared/consts"
|
import { TTL } from "@shared/consts"
|
||||||
import type { SourceID, SourceResponse } from "@shared/types"
|
import type { SourceID, SourceResponse } from "@shared/types"
|
||||||
import { sources } from "@shared/sources"
|
import { sources } from "@shared/sources"
|
||||||
import { sourcesGetters } from "#/sources"
|
import { getters } from "#/getters"
|
||||||
import { useCache } from "#/hooks/useCache"
|
import { useCache } from "#/hooks/useCache"
|
||||||
|
|
||||||
export default defineEventHandler(async (event): Promise<SourceResponse> => {
|
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
|
let id = getRouterParam(event, "id") as SourceID
|
||||||
const query = getQuery(event)
|
const query = getQuery(event)
|
||||||
const latest = query.latest !== undefined && query.latest !== "false"
|
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)) {
|
if (isValid(id)) {
|
||||||
const redirectID = sources?.[id]?.redirect
|
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`)
|
logger.success(`fetch ${id} latest`)
|
||||||
if (cacheTable) {
|
if (cacheTable) {
|
||||||
if (event.context.waitUntil) event.context.waitUntil(cacheTable.set(id, data))
|
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 () => {
|
export default defineSource({
|
||||||
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"
|
coolapk: async () => {
|
||||||
const r: Res = await $fetch(url, {
|
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"
|
||||||
headers: await genHeaders(),
|
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 => ({
|
if (!r.data.length) throw new Error("Failed to fetch")
|
||||||
id: i.id,
|
return r.data.filter(k => k.id).map(i => ({
|
||||||
title: i.editor_title || load(i.message).text().split("\n")[0],
|
id: i.id,
|
||||||
url: i.shareUrl,
|
title: i.editor_title || load(i.message).text().split("\n")[0],
|
||||||
extra: {
|
url: i.shareUrl,
|
||||||
info: i.targetRow?.subTitle,
|
extra: {
|
||||||
// date: new Date(i.dateline * 1000).getTime(),
|
info: i.targetRow?.subTitle,
|
||||||
},
|
// date: new Date(i.dateline * 1000).getTime(),
|
||||||
})).slice(0, 30)
|
},
|
||||||
|
}))
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
@ -17,12 +17,11 @@ export default defineSource(async () => {
|
|||||||
cookie: cookie.join("; "),
|
cookie: cookie.join("; "),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return res.data.word_list
|
return res.data.word_list.map((k) => {
|
||||||
.map((k) => {
|
return {
|
||||||
return {
|
id: k.sentence_id,
|
||||||
id: k.sentence_id,
|
title: k.word,
|
||||||
title: k.word,
|
url: `https://www.douyin.com/hot/${k.sentence_id}`,
|
||||||
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 () => {
|
export default defineSource({
|
||||||
const url = "https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=20&desktop=true"
|
zhihu: async () => {
|
||||||
const res: Res = await $fetch(url)
|
const url = "https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=20&desktop=true"
|
||||||
return res.data
|
const res: Res = await $fetch(url)
|
||||||
.slice(0, 30)
|
return res.data
|
||||||
.map((k) => {
|
.slice(0, 30)
|
||||||
return {
|
.map((k) => {
|
||||||
id: k.target.id,
|
return {
|
||||||
title: k.target.title,
|
id: k.target.id,
|
||||||
extra: {
|
title: k.target.title,
|
||||||
icon: k.card_label?.night_icon,
|
extra: {
|
||||||
},
|
icon: k.card_label?.night_icon,
|
||||||
url: `https://www.zhihu.com/question/${k.target.id}`,
|
},
|
||||||
}
|
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/*"]
|
"@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 type { VitePWAOptions } from "vite-plugin-pwa"
|
||||||
import { VitePWA } from "vite-plugin-pwa"
|
import { VitePWA } from "vite-plugin-pwa"
|
||||||
import { projectDir } from "./shared/dir"
|
import { projectDir } from "./shared/dir"
|
||||||
|
import { RollopGlob } from "./tools/rollup-glob"
|
||||||
|
|
||||||
dotenv.config({
|
dotenv.config({
|
||||||
path: join(projectDir, ".env.server"),
|
path: join(projectDir, ".env.server"),
|
||||||
@ -62,6 +63,9 @@ const nitroOption: Parameters<typeof nitro>[0] = {
|
|||||||
experimental: {
|
experimental: {
|
||||||
database: true,
|
database: true,
|
||||||
},
|
},
|
||||||
|
rollupConfig: {
|
||||||
|
plugins: [RollopGlob()],
|
||||||
|
},
|
||||||
sourceMap: false,
|
sourceMap: false,
|
||||||
database: {
|
database: {
|
||||||
default: {
|
default: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user