Skip to content

Commit

Permalink
feat(joshwcomeau): add new Routes josh w comeau (DIYgod#18071)
Browse files Browse the repository at this point in the history
* init

* Update lib/routes/joshwcomeau/latest.ts

Co-authored-by: Tony <[email protected]>

* Update lib/routes/joshwcomeau/popular.ts

Co-authored-by: Tony <[email protected]>

* remove date sorting

* update date

---------

Co-authored-by: Rjnishant530 <[email protected]>
  • Loading branch information
Rjnishant530 and Rjnishant530 authored Feb 19, 2025
1 parent d359da4 commit 30061eb
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 0 deletions.
59 changes: 59 additions & 0 deletions lib/routes/joshwcomeau/latest.ts
Original file line number Diff line number Diff line change
@@ -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;
}
8 changes: 8 additions & 0 deletions lib/routes/joshwcomeau/namespace.ts
Original file line number Diff line number Diff line change
@@ -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',
};
38 changes: 38 additions & 0 deletions lib/routes/joshwcomeau/popular.ts
Original file line number Diff line number Diff line change
@@ -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;
}
61 changes: 61 additions & 0 deletions lib/routes/joshwcomeau/utils.ts
Original file line number Diff line number Diff line change
@@ -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');
}

0 comments on commit 30061eb

Please sign in to comment.