Retour à la liste

Comment faire du cross-post sur Dev.to avec un flux RSS de votre blog Next.JS ?

Cover Image for Comment faire du cross-post sur Dev.to avec un flux RSS de votre blog Next.JS ?
Dylan Ballandras
Dylan Ballandras

J'utilise NextJS depuis deux ans maintenant pour mon site/blog professionnel. C'est vraiment intuitif de faire ce que l'on souhaite car on repart de la base avec NodeJS. Je déploie mon site web sur Vercel et utilise une simple commande yarn build pour cela (La commande va executer un ensemble d'autres scripts yarn extract:i18n && yarn compile:i18n && yarn build:rss && yarn build:sitemap && next build). Aujourd'hui, je vais partager le script, assez simple, derrière la commande build:rss.

import fs from 'fs';
import path from 'path';
import RSS from 'rss';
import siteMeta from 'site.config.js';
import {getAllPosts, getPostBySlug} from '@lib/api';
import markdownToHtml from '@lib/markdownToHtml.mjs';

const {title, description, siteUrl, author} = siteMeta;

const feed = new RSS({
  title,
  description,
  feed_url: `${siteUrl}/feed.xml`,
  site_url: siteUrl,
  webMaster: `no-reply@kayneth.dev (${author.name})`,
  language: 'en',
});

const allPosts = getAllPosts(['slug'], process.env.DEFAULT_LANGUAGE);

const feedItemsPromises = allPosts.map(async post => {
  const {title, content, slug, date} = getPostBySlug(
    post.slug,
    ['title', 'slug', 'date', 'content'],
    process.env.DEFAULT_LANGUAGE
  );

  const description = await markdownToHtml(content, true);
  return {
    title,
    description: description,
    url: `${siteUrl}/blog/${slug}`,
    date,
  }
});

Promise.allSettled(feedItemsPromises)
  .then(feedItems => feedItems.forEach(item => feed.item(item.value)))
  .then(() => {
    const xml = feed.xml();

    fs.writeFileSync(path.join('./public', 'feed.xml'), xml);
  });

J'exporte uniquement les articles de mon blog. Vous pouvez envisager de modifier ce script pour l'adapter à vos besoins et à la structure de votre projet. En parlant de la structure de mon projet, j'ai essayé d'écrire un api.js adapté du template de blog NextJS par défaut mais pour plusieurs langues. J'écris actuellement en anglais et en français car je veux que mon contenu soit accessible à un public international et français. Mes articles de blog sont situés dans le répertoire _posts. Et ressemble à ceci :

$ tree _posts
_posts
├── a-french-blog-post.mdx # using frontmatter to define the language
├── a-multilingual-post
│   ├── index.en.md
│   └── index.fr.md
└── a-blog-post-in-english.mdx

Et mon script, qui peut sembler laid pour les développeurs NodeJS (désolé), ressemble à ceci :

import fs from 'fs';
import {join} from 'path';
import matter from 'gray-matter';

const postsDirectory = join(process.cwd(), '_posts');
const pagesDirectory = join(process.cwd(), '_pages');

export function getPostSlugs() {
  return fs.readdirSync(postsDirectory);
}

export function getPostBySlug(
  slug,
  fields = [],
  locale = process.env.DEFAULT_LANGUAGE
) {
  const realSlug = slug.replace(/\.md(x)?$/, '');
  let fullPath = join(postsDirectory, realSlug);
  let localeInPath = false;

  if (fs.existsSync(fullPath) && fs.lstatSync(fullPath).isDirectory()) {
    localeInPath = true;
    fullPath = join(postsDirectory, `${realSlug}`, `index.${locale}`);
  }

  fullPath = `${fullPath}.md`;

  if (!fs.existsSync(fullPath)) {
    fullPath += 'x';

    if (!fs.existsSync(fullPath)) {
      return null;
    }
  }

  const fileContents = fs.readFileSync(fullPath, 'utf8');
  const {data, content} = matter(fileContents);

  if (data['language'] && locale !== data['language']) {
    return null;
  } else if (
    !data.hasOwnProperty('language') &&
    locale !== process.env.DEFAULT_LANGUAGE &&
    !localeInPath
  ) {
    return null;
  }

  const items = {};

  // Ensure only the minimal needed data is exposed
  fields.forEach(field => {
    if (field === 'slug') {
      items[field] = realSlug;
    }
    if (field === 'content') {
      items[field] = content;
    }

    if (data[field]) {
      items[field] = data[field];
    }
  });

  return items;
}

export function getPage(slug) {
  const realSlug = slug;
  const fullPath = join(pagesDirectory, realSlug);

  if (!fs.existsSync(fullPath)) {
    return {content: null};
  }

  const fileContents = fs.readFileSync(fullPath, 'utf8');
  const {data, content} = matter(fileContents);

  return {content: content};
}

export function getAllPosts(
  fields = [],
  locale = process.env.DEFAULT_LANGUAGE,
  maxLimit = null
) {
  const slugs = getPostSlugs();

  const posts = slugs
    .map(slug => getPostBySlug(slug, fields, locale))
    .filter(i => null !== i)
    // sort posts by date in descending order
    .sort((post1, post2) => (post1.date > post2.date ? -1 : 1));

  if (maxLimit) {
    return posts.slice(0, maxLimit);
  }

  return posts;
}

Enfin, ces deux scripts avec mon architecture de projet permet de générer un feed.xml disponible sur le web après la phase de build. Vous pouvez maintenant ajouter cette URL à la configuration de votre profil dev.to sous Publishing to DEV Community from RSS.

J'espère sincèrement que cela vous aidera à faire de la publication croisée (en bon français) sur dev.to ou toute autre plateforme ! Merci pour votre temps 😁 🙏