feat(joshwcomeau): add new Routes josh w comeau (#18071)
* init * Update lib/routes/joshwcomeau/latest.ts Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update lib/routes/joshwcomeau/popular.ts Co-authored-by: Tony <TonyRL@users.noreply.github.com> * remove date sorting * update date --------- Co-authored-by: Rjnishant530 <singhnisant530@gmail.com>
This commit is contained in:
parent
d359da431d
commit
30061eb532
|
|
@ -0,0 +1,59 @@
|
|||
import { Data, Route } from '@/types';
|
||||
import { getRelativeUrlList, processList, rootUrl } from './utils';
|
||||
|
||||
export const route: Route = {
|
||||
path: '/latest/:category?',
|
||||
categories: ['programming'],
|
||||
example: '/joshwcomeau/latest/css',
|
||||
features: {
|
||||
requireConfig: false,
|
||||
requirePuppeteer: false,
|
||||
antiCrawler: false,
|
||||
supportBT: false,
|
||||
supportPodcast: false,
|
||||
supportScihub: false,
|
||||
},
|
||||
parameters: {
|
||||
category: {
|
||||
description: 'Category',
|
||||
options: [
|
||||
{ value: 'css', label: 'CSS' },
|
||||
{ value: 'react', label: 'React' },
|
||||
{ value: 'animation', label: 'Animation' },
|
||||
{ value: 'javascript', label: 'JavaScript' },
|
||||
{ value: 'career', label: 'Career' },
|
||||
{ value: 'blog', label: 'Blog' },
|
||||
],
|
||||
},
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
source: ['joshwcomeau.com/'],
|
||||
target: '/latest',
|
||||
},
|
||||
{
|
||||
source: ['joshwcomeau.com/:category'],
|
||||
target: '/latest/:category',
|
||||
},
|
||||
],
|
||||
name: 'Articles and Tutorials',
|
||||
maintainers: ['Rjnishant530'],
|
||||
handler,
|
||||
};
|
||||
|
||||
async function handler(ctx) {
|
||||
const category: string = ctx.req.param('category') || '';
|
||||
const currentUrl = category ? `${rootUrl}/${category}` : rootUrl;
|
||||
const selector = category ? 'div > article > a:first-child' : 'article[data-include-enter-animation="false"] > a:first-child';
|
||||
const { heading, urls } = await getRelativeUrlList(currentUrl, selector);
|
||||
const items = await processList(urls);
|
||||
const title = category ? `${heading} | ` : '';
|
||||
return {
|
||||
title: `${title}Articles and Tutorials | Josh W. Comeau`,
|
||||
description: `Friendly tutorials for developers. Focus on ${category ? title : 'React, CSS, Animation, and more!'}`,
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
icon: `${rootUrl}/favicon.png`,
|
||||
logo: `${rootUrl}/favicon.png`,
|
||||
} as Data;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import type { Namespace } from '@/types';
|
||||
|
||||
export const namespace: Namespace = {
|
||||
name: 'Josh W Comeau',
|
||||
url: 'www.joshwcomeau.com',
|
||||
categories: ['programming'],
|
||||
lang: 'en',
|
||||
};
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import { Data, Route } from '@/types';
|
||||
import { getRelativeUrlList, processList, rootUrl } from './utils';
|
||||
|
||||
export const route: Route = {
|
||||
path: '/popular',
|
||||
categories: ['programming'],
|
||||
example: '/joshwcomeau/popular',
|
||||
features: {
|
||||
requireConfig: false,
|
||||
requirePuppeteer: false,
|
||||
antiCrawler: false,
|
||||
supportBT: false,
|
||||
supportPodcast: false,
|
||||
supportScihub: false,
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
source: ['joshwcomeau.com/'],
|
||||
target: '/popular',
|
||||
},
|
||||
],
|
||||
name: 'Popular Content',
|
||||
maintainers: ['Rjnishant530'],
|
||||
handler,
|
||||
};
|
||||
|
||||
async function handler() {
|
||||
const { urls } = await getRelativeUrlList(rootUrl, 'section > ol > li > a');
|
||||
const items = await processList(urls);
|
||||
return {
|
||||
title: 'Popular Content | Josh W. Comeau',
|
||||
description: 'Friendly tutorials for developers. Focus on React, CSS, Animation, and more!',
|
||||
link: rootUrl,
|
||||
item: items,
|
||||
icon: `${rootUrl}/favicon.png`,
|
||||
logo: `${rootUrl}/favicon.png`,
|
||||
} as Data;
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
import { DataItem } from '@/types';
|
||||
import { load } from 'cheerio';
|
||||
import ofetch from '@/utils/ofetch';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import cache from '@/utils/cache';
|
||||
|
||||
export const rootUrl = 'https://www.joshwcomeau.com';
|
||||
|
||||
export async function getRelativeUrlList(url, selector) {
|
||||
const response = await ofetch(url);
|
||||
const $ = load(response);
|
||||
const heading = $('header>h1').text();
|
||||
const urls = $(selector)
|
||||
.toArray()
|
||||
.map((element) => {
|
||||
const itemRelativeUrl = $(element).attr('href');
|
||||
const cardTitle = $(element).find('span').text();
|
||||
return { url: itemRelativeUrl as string, cardTitle };
|
||||
});
|
||||
return { heading, urls };
|
||||
}
|
||||
|
||||
export async function processList(list) {
|
||||
const listPromise = await Promise.allSettled(list.map(async (item) => await cache.tryGet(`joshwcomeau:${item.url}`, async () => await getPostContent(item))));
|
||||
return listPromise.map((item, index) => (item.status === 'fulfilled' ? item.value : ({ title: 'Error Reading Item', link: `${rootUrl}${list[index]?.url}` } as DataItem)));
|
||||
}
|
||||
|
||||
export async function getPostContent({ url, cardTitle }) {
|
||||
if (url.startsWith('https')) {
|
||||
return {
|
||||
title: cardTitle ?? 'External Content',
|
||||
description: 'Read it on external Site',
|
||||
link: url,
|
||||
} as DataItem;
|
||||
}
|
||||
const response = await ofetch(`${rootUrl}${url}`);
|
||||
const $ = load(response);
|
||||
const title = $('meta[property="og:title"]').attr('content')?.replace('• Josh W. Comeau', '');
|
||||
const summary = $('meta[property="og:description"]').attr('content');
|
||||
const author = $('meta[name="author"]').attr('content');
|
||||
const dateDiv = $('div[data-parent-layout]');
|
||||
const tag = dateDiv.find('dl:first-child > dd > a').text();
|
||||
const pubDate = dateDiv.find('dl:first-child > dd:has(span):not(:last-child)').text();
|
||||
const updateDate = dateDiv.find('dl:last-child > dd:has(span):not(:last-child)').text();
|
||||
const description = $('main > article').html();
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
author,
|
||||
pubDate: processDate(pubDate),
|
||||
updated: processDate(updateDate),
|
||||
link: `${rootUrl}${url}`,
|
||||
content: { html: description, text: summary },
|
||||
category: [tag],
|
||||
} as DataItem;
|
||||
}
|
||||
|
||||
function processDate(date: string) {
|
||||
const dateWithSlash = date.trim().replaceAll(' ', '/').replace(',', '');
|
||||
return parseDate(dateWithSlash, 'MMMM/Do/YYYY', 'en');
|
||||
}
|
||||
Loading…
Reference in New Issue