feat(route): web.dev (#14746)

* feat(route): web.dev

* add parameter descriptions

* Update lib/routes/web/articles.ts

Co-authored-by: Tony <TonyRL@users.noreply.github.com>

* Update lib/routes/web/articles.ts

Co-authored-by: Tony <TonyRL@users.noreply.github.com>

* Update lib/routes/web/blog.ts

Co-authored-by: Tony <TonyRL@users.noreply.github.com>

* Update lib/routes/web/series.ts

Co-authored-by: Tony <TonyRL@users.noreply.github.com>

---------
This commit is contained in:
karasu 2024-03-17 21:39:53 +08:00 committed by GitHub
parent cb1ac8e0b4
commit 85b46d7120
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 150 additions and 0 deletions

View File

@ -0,0 +1,23 @@
import type { Data, Route } from '@/types';
import { fetchItems } from './utils';
export const route: Route = {
path: '/articles',
categories: ['programming'],
example: '/web/articles',
radar: [{
source: ['web.dev/articles'],
}],
name: 'Articles',
maintainers: ['KarasuShin'],
handler,
};
async function handler(): Promise<Data> {
return {
title: 'Articles',
link: 'https://web.dev/articles',
image: 'https://web.dev/_pwa/web/icons/icon-144x144.png',
item: await fetchItems('family_url:/articles'),
};
}

23
lib/routes/web/blog.ts Normal file
View File

@ -0,0 +1,23 @@
import type { Data, Route } from '@/types';
import { fetchItems } from './utils';
export const route: Route = {
path: '/blog',
categories: ['programming'],
example: '/web/blog',
radar: [{
source: ['web.dev/blog'],
}],
name: 'Blog',
maintainers: ['KarasuShin'],
handler,
};
async function handler(): Promise<Data> {
return {
title: 'Blog',
link: 'https://web.dev/blog',
image: 'https://web.dev/_pwa/web/icons/icon-144x144.png',
item: await fetchItems('type:blog'),
};
}

View File

@ -0,0 +1,6 @@
import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'web.dev',
url: 'web.dev',
};

30
lib/routes/web/series.ts Normal file
View File

@ -0,0 +1,30 @@
import type { Data, Route } from '@/types';
import { fetchItems, hyphen2Pascal } from './utils';
export const route: Route = {
path: '/series/:seriesName',
parameters: { seriesName: 'topic name in the series section' },
categories: ['programming'],
example: '/web/series/new-to-the-web',
radar: [{
source: ['web.dev/series/:seriesName'],
target: '/series/:seriesName',
}],
name: 'Series',
maintainers: ['KarasuShin'],
handler,
description: `:::tip
The \`seriesName\` can be extracted from the Series page URL: \`https://web.dev/series/:seriesName\`
:::`,
};
async function handler(ctx): Promise<Data> {
const seriesName = ctx.req.param('seriesName');
return {
title: seriesName,
link: `https://web.dev/series/${seriesName}`,
image: 'https://web.dev/_pwa/web/icons/icon-144x144.png',
item: await fetchItems(`category:${hyphen2Pascal(seriesName)}`),
};
}

68
lib/routes/web/utils.ts Normal file
View File

@ -0,0 +1,68 @@
import type { DataItem } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
type ArticleData = [
string,
string,
unknown,
string,
string,
number[],
string,
string,
unknown,
unknown,
string[],
string,
string[],
string,
string,
unknown,
string[],
string,
string,
number,
string[],
unknown,
number,
string[],
number,
number,
];
export async function fetchItems(queryParam: string): Promise<DataItem[]> {
const res = await got<string>('https://web.dev/_d/dynamic_content', {
body: `[null,null,null, "${queryParam}",null,null,null,null,31,null,null,null,2]`,
method: 'post',
});
const data = JSON.parse(res.data.replace(/^[^[]*/, '')) as [ArticleData[], number];
const items = await Promise.all(
data[0].map((item) => {
const link = item[6];
return cache.tryGet(link, async () => {
const { data: articleHtml } = await got.get<string>(link);
const $ = load(articleHtml);
const articleBody = $('.devsite-article-body');
articleBody.find('.wd-authors').remove();
return {
title: item[0],
pubDate: new Date(item[5][0] * 1e3),
description: articleBody.html(),
link,
};
}) as unknown as DataItem;
})
);
return items;
}
export function hyphen2Pascal(value: string) {
return value
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
}