Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(css-tricks): add new routes for css-tricks #18066

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/routes/30secondsofcode/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ async function processItem({ link: articleLink, date }) {
category: tags,
image: `${rootUrl}${image}`,
banner: `${rootUrl}${image}`,
language: 'en-us',
} as DataItem;
});
}
48 changes: 48 additions & 0 deletions lib/routes/css-tricks/articles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Data, Route, ViewType } from '@/types';
Rjnishant530 marked this conversation as resolved.
Show resolved Hide resolved
import { load } from 'cheerio';
import ofetch from '@/utils/ofetch';
import { processCards } from './utils';
export const route: Route = {
path: '/articles',
view: ViewType.Articles,
categories: ['programming'],
example: '/articles',
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['css-tricks.com/category/articles/'],
target: '/articles',
},
],
name: 'Articles',
maintainers: ['Rjnishant530'],
handler,
};

async function handler() {
// const category = ctx.req.param('category') ?? '';
// const subCategory = ctx.req.param('subCategory') ?? '';

const rootUrl = 'https://css-tricks.com';
const currentUrl = `${rootUrl}/category/articles/`;
const response = await ofetch(currentUrl);
const $ = load(response);
const articleCards = $('article.article-card').toArray();
const items = await processCards(articleCards);
return {
title: 'Articles - CSS-Tricks',
description: 'Latest Articles - CSS-Tricks',
link: currentUrl,
item: items,
language: 'en',
logo: `${rootUrl}/favicon.ico`,
icon: `${rootUrl}/favicon.ico`,
} as Data;
}
50 changes: 50 additions & 0 deletions lib/routes/css-tricks/fresh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Data, Route, ViewType } from '@/types';
import { extractMiniCards, processCards, rootUrl } from './utils';
export const route: Route = {
path: '/fresh/:dateSort?',
view: ViewType.Articles,
categories: ['programming'],
example: '/fresh',
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
parameters: {
dateSort: {
description: 'Sort posts by publication date instead of popularity',
default: 'true',
options: [
{ value: 'false', label: 'False' },
{ value: 'true', label: 'True' },
],
},
},
radar: [
{
source: ['css-tricks.com'],
target: '/fresh',
},
],
name: 'Fresh From the Almanac',
maintainers: ['Rjnishant530'],
handler,
};

async function handler(ctx) {
const dateSort = ctx.req.param('dateSort') ? JSON.parse(ctx.req.param('dateSort')) : true;
const popularCards = await extractMiniCards('body > div.page-wrap > section.post-sliders > div:nth-child(4) article.mini-card.module.module-article');
const items = await processCards(popularCards, true, dateSort);
Rjnishant530 marked this conversation as resolved.
Show resolved Hide resolved
return {
title: 'Fresh From the Almanac',
description: 'Properties, selectors, rules, and functions!',
link: rootUrl,
item: items,
language: 'en',
logo: `${rootUrl}/favicon.ico`,
icon: `${rootUrl}/favicon.ico`,
} as Data;
}
39 changes: 39 additions & 0 deletions lib/routes/css-tricks/guides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Data, Route, ViewType } from '@/types';
import { extractMiniCards, processCards, rootUrl } from './utils';
export const route: Route = {
path: '/guides',
view: ViewType.Articles,
categories: ['programming'],
example: '/guides',
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['css-tricks.com'],
target: '/guides',
},
],
name: 'CSS Guides',
maintainers: ['Rjnishant530'],
handler,
};

async function handler() {
const guideCards = await extractMiniCards('body > div.page-wrap > section.post-sliders > div:nth-child(3) article.mini-card.module.module-article');
const items = await processCards(guideCards, true);
Rjnishant530 marked this conversation as resolved.
Show resolved Hide resolved
return {
title: 'Latest CSS Guides',
description: 'Dive deep into CSS features and concepts',
link: rootUrl,
item: items,
language: 'en',
logo: `${rootUrl}/favicon.ico`,
icon: `${rootUrl}/favicon.ico`,
} as Data;
}
8 changes: 8 additions & 0 deletions lib/routes/css-tricks/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: 'CSS-Tricks',
url: 'css-tricks.com',
categories: ['programming'],
lang: 'en',
};
50 changes: 50 additions & 0 deletions lib/routes/css-tricks/popular.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Data, Route, ViewType } from '@/types';
import { extractMiniCards, processCards, rootUrl } from './utils';
export const route: Route = {
path: '/popular/:dateSort?',
view: ViewType.Articles,
categories: ['programming'],
example: '/popular',
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
parameters: {
dateSort: {
description: 'Sort posts by publication date instead of popularity',
default: 'true',
options: [
{ value: 'false', label: 'False' },
{ value: 'true', label: 'True' },
],
},
},
radar: [
{
source: ['css-tricks.com'],
target: '/popular',
},
],
name: 'Popular this month',
maintainers: ['Rjnishant530'],
handler,
};

async function handler(ctx) {
const dateSort = ctx.req.param('dateSort') ? JSON.parse(ctx.req.param('dateSort')) : true;
const popularCards = await extractMiniCards('div.popular-articles > div.mini-card-grid article.mini-card.module.module-article');
const items = await processCards(popularCards, true, dateSort);
Rjnishant530 marked this conversation as resolved.
Show resolved Hide resolved
return {
title: 'Popular this month',
description: 'Popular CSS articles this month',
link: rootUrl,
item: items,
language: 'en',
logo: `${rootUrl}/favicon.ico`,
icon: `${rootUrl}/favicon.ico`,
} as Data;
}
98 changes: 98 additions & 0 deletions lib/routes/css-tricks/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { load } from 'cheerio';
import ofetch from '@/utils/ofetch';
import { DataItem } from '@/types';
import { parseDate } from '@/utils/parse-date';
import cache from '@/utils/cache';

export const rootUrl = 'https://css-tricks.com';
type Card = {
id: string;
title: string;
link: string;
thumbnail: string;
};

export async function extractMiniCards(selector) {
const response = await ofetch(rootUrl);
const $ = load(response);
return $(selector).toArray();
}

function extractCardsInfo(cards) {
return cards.map((card) => {
const $ = load(card);
const id = $(card).attr('id');
const thumbnail = $(card).find('div.article-thumbnail-wrap > a >img').attr('src');
const article = $('div.article-article');
const title = article.find('h2 > a').text();
const link = article.find('h2 > a').attr('href');
return {
id,
title,
link,
thumbnail,
} as Card;
});
}

function extractMiniCardsInfo(cards) {
return cards.map((card) => {
const $ = load(card);
const id = $(card).attr('id')?.replace('mini-', '');
const thumbnail = '';
const title = $('h3.mini-card-title').find('a:not(.aal_anchor)').text();
const link = $('h3.mini-card-title').find('a:not(.aal_anchor)').attr('href');
return {
id,
title,
link,
thumbnail,
} as Card;
});
}

export async function processCards(cards, mini: boolean = false, dateSort: boolean = true) {
const cardsWithInfo = mini ? extractMiniCardsInfo(cards) : extractCardsInfo(cards);
const cardsPromise = await Promise.allSettled(
cardsWithInfo.map(async (card: Card) => await fetchCardDetails(card, dateSort))
);
return cardsPromise.filter((card) => card.status === 'fulfilled').map((card) => card.value as DataItem);
}

export async function fetchCardDetails(card: Card, dateSort: boolean) {
return await cache.tryGet(`css-tricks:${card.id}`, async () => {
const response = await ofetch(card.link);
const $ = load(response);
const tags = $('meta[property="article:tag"]')
?.toArray()
.map((tag) => $(tag).attr('content'));
const date = $('meta[property="article:published_time"]').attr('content') || '';
const updateDate = $('meta[property="article:modified_time"]').attr('content') || '';
const summary = $('meta[property="description"]').attr('content') || '';
const authorUrl = $('header.mega-header').find('div.author-row > a').attr('href');
const authorAvatar = $('header.mega-header').find('header.mega-header div.author-row > a >img').attr('src');
const authorName = $('header.mega-header').find('header.mega-header div.author-row > div > a.author-name').text();
const content = $('div.article-content').html() || '';
return {
title: card.title,
link: card.link,
description: content,
banner: card.thumbnail,
image: card.thumbnail,
pubDate: dateSort ? parseDate(date) : '',
updated: dateSort ? parseDate(updateDate) : '',
Rjnishant530 marked this conversation as resolved.
Show resolved Hide resolved
author: [
{
name: authorName || '',
url: authorUrl || '',
avatar: authorAvatar || '',
},
],
content: {
html: content,
text: summary,
},
category: tags,
} as DataItem;
});
}
Loading