npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@monark_it/mch-blog

v0.10.2

Published

Runtime de blog pour les sites Monark : liste, article, RSS, sitemap, SEO, thème et revalidation. Tout le contenu vient de la **release publiée par le Monark Content Hub** — aucun fichier Markdown local, aucune base de données côté site.

Readme

@monark_it/mch-blog

Runtime de blog pour les sites Monark : liste, article, RSS, sitemap, SEO, thème et revalidation. Tout le contenu vient de la release publiée par le Monark Content Hub — aucun fichier Markdown local, aucune base de données côté site.

1. Le blog en 30 secondes

  • Hub → release → site. Vous publiez dans le Hub ; le site lit la release et la sert sur /blog.
  • Le package fournit le rendu (liste paginée par 10, article, RSS, sitemap), le SEO (canonical, Open Graph, JSON-LD, redirections 301), le thème et la revalidation (webhook signé).
  • Le site ne garde que des wrappers fins : npm install @monark_it/mch-blog@latest met à jour tout ce comportement sans toucher aux fichiers du site.
  • Peer dependencies : react 18 ou 19, next ≥ 15.

Ce que contient un site équipé :

| Fichier | Rôle | | -------------------------------- | -------------------------------------------------------------------------- | | app/blog/layout.tsx | importe styles.css | | app/blog/page.tsx | liste (BlogListPage) + metadata | | app/blog/[slug]/page.tsx | article (ArticlePage), redirections 301, JSON-LD, generateStaticParams | | app/blog/rss.xml/route.ts | flux RSS (getBlogRssResponse, si RSS activé) | | app/sitemap.ts | sitemap du site + articles (getBlogSitemap, créé ou patché) | | app/api/mch/[...path]/route.ts | route Hub : hello, revalidate, well-known | | app/blog/_overrides/index.ts | registry des composants personnalisés |

2. Démarrage

# 1. wrappers, registry et variables du site, puis installer la dépendance
npx @monark_it/mch-create-blog
npm install

# 2. associer le site au Hub (code à approuver dans le Hub, page /connect)
npx @monark_it/mch-create-blog connect --url https://exemple.com

# 3. copier les 6 variables MCH_* listées par connect dans la plateforme d'hébergement
# 4. déployer
# 5. publier un article depuis le Hub, ouvrir /blog

Le parcours détaillé (questions, options, dépannage) est dans le README de @monark_it/mch-create-blog.

3. Installation et commandes

npm install @monark_it/mch-blog installe le runtime. Peer dependencies : react 18 ou 19 et next ≥ 15 (entrée /next).

Les trois commandes du scaffolder :

| Commande | Rôle | | ------------------------------------------------ | --------------------------------------- | | npx @monark_it/mch-create-blog | wrappers, registry et variables du site | | npx @monark_it/mch-create-blog connect --url … | association au Hub et route API | | npx @monark_it/mch-create-blog customize | éjection d'un composant personnalisable |

Options et dépannage : README de @monark_it/mch-create-blog.

4. Personnalisation

Trois niveaux, du plus simple au plus lourd : le Design dans le Hub (sans code), l'éjection de composants avec customize, puis la page headless.

4.1 Design (sans code)

Dans le Hub, onglet Design du site, l'aperçu est rendu par les composants réels du blog.

| Réglage | Ce qui change | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | | Couleurs | palette claire et palette sombre : accent, surfaces, textes, liens, code, citations, tableaux, focus | | Typographie | taille du texte, interligne, tailles et couleur des titres | | Liste | grille ou liste, nombre de colonnes | | Carte | couverture (haut, côté, aucune), métas, extrait, auteur, catégorie, ordre (titre d'abord ou métas d'abord), survol, fond et bordure | | Article | couverture contenue, pleine largeur ou aucune | | Articles liés | cartes, liste ou compact, avec ou sans couverture | | Page | titre, sous-titre, mode sombre | | Largeur du contenu | largeur maximale de la page (défaut 1200, plage 480–1440, réglable dans Design) | | Markdown | soulignement des liens, alignement et largeur des images, zébrures des tableaux, thème du code, séparateurs, boutons | | Par emplacement | surcharge des réglages liste, carte, article ou liés selon l'endroit |

Deux points à connaître :

  • Contraste : le Hub vérifie les paires texte/fond (AA) et signale celles à corriger ; le bouton « Corriger tout » ajuste les couleurs automatiquement.
  • Mode sombre : une palette sombre définie n'est servie qu'avec darkMode: system (préférence du visiteur). Par défaut, la palette claire s'applique.

Les réglages enregistrés partent avec la prochaine publication du site : publiez depuis le Hub pour les voir en ligne.

4.2 Composants personnalisés (customize)

npx @monark_it/mch-create-blog customize --list
npx @monark_it/mch-create-blog customize --slot article-card

Sept emplacements sont éjectables :

| Slot (--slot) | Fichier éjecté | Ce qui change | | ----------------- | ------------------------------------------------------- | --------------------------------------------------------- | | article-card | app/blog/_overrides/ArticleCard/ArticleCard.tsx | carte d'un article dans la liste | | related-card | app/blog/_overrides/RelatedCard/RelatedCard.tsx | section « Articles liés » | | article-header | app/blog/_overrides/ArticleHeader/ArticleHeader.tsx | titre, métas, tags et couverture de l'article | | article-content | app/blog/_overrides/ArticleContent/ArticleContent.tsx | enveloppe du corps Markdown | | article-layout | app/blog/_overrides/ArticleLayout/ArticleLayout.tsx | ordre des blocs de l'article | | empty-state | app/blog/_overrides/EmptyState/EmptyState.tsx | message d'une liste sans article | | markdown | app/blog/_overrides/Markdown/MarkdownComponents.tsx | rendu des balises du corps (a, img, table, code…) |

Chaque éjection écrit le composant et son CSS dans un sous-dossier _overrides/<Slot>/, puis branche le registry app/blog/_overrides/index.ts :

app/blog/_overrides/
  index.ts                 ← registry (imports + branchements)
  ArticleCard/
    ArticleCard.tsx
    ArticleCard.css
  ArticleLayout/
    ArticleLayout.tsx
    ArticleLayout.css
  ...

Le principe : les pages importent listComponents, articleComponents et markdownComponents depuis le registry. Éditez le composant, le rendu suit ; les pages ne bougent pas.

| Option | Effet | | ------------- | ------------------------------------------------------------------------------------------- | | --list | liste les 7 emplacements, sans écrire | | --slot <id> | éjecte un emplacement (répétable ou séparé par des virgules ; numéro accepté) | | --force | réécrit un composant déjà éjecté (sans confirmation ; en interactif, confirmation demandée) | | --dry-run | affiche les écritures sans écrire |

Sans --force, un composant déjà éjecté est conservé : vos modifications ne sont jamais écrasées. La commande ne lance aucune commande git ; committez les fichiers comme le reste du site.

Après une éjection, le Hub est informé automatiquement. Le registry exporte la liste overrides, la route API la transmet à chaque revalidation et sur /.well-known/mch-release, et le Hub affiche un bandeau « Composants personnalisés » dans Design. Rien à déclarer à la main ; la liste se met à jour au prochain échange entre le Hub et le site.

Recettes

Carte custom — éditez ArticleCard.tsx ; le composant reçoit BlogCardProps (article, config, design, placement) :

import { formatDate, getArticlePath, type BlogCardProps } from "@monark_it/mch-blog/ui";

export function ArticleCard({ article, config }: BlogCardProps) {
  return (
    <a className="ma-carte" href={getArticlePath(config, article.slug)}>
      <h2>{article.title}</h2>
      <time dateTime={article.published_at}>{formatDate(article.published_at, config.locale)}</time>
    </a>
  );
}

Métas en basarticle-layout fournit les blocs Cover, Title, Meta, Tags, Content, Related, déjà liés à l'article :

import type { ArticleLayoutProps } from "@monark_it/mch-blog/ui";

export function ArticleLayout({ blocks }: ArticleLayoutProps) {
  const { Cover, Title, Meta, Tags, Content, Related } = blocks;

  return (
    <>
      <Title />
      <Tags />
      <Cover />
      <Content />
      <Meta />
      <Related />
    </>
  );
}

Bouton personnalisé — le slot markdown surcharge les balises du corps ; un lien rendu en bouton porte la classe mch-button :

import {
  MARKDOWN_BUTTON_CLASS,
  defaultMarkdownComponents,
  type MarkdownComponents,
} from "@monark_it/mch-blog/ui";

export const markdownComponents: MarkdownComponents = {
  ...defaultMarkdownComponents,
  a: ({ className, ...props }) => (
    <a
      {...props}
      className={className?.includes(MARKDOWN_BUTTON_CLASS) ? `${className} ma-marque` : className}
    />
  ),
};

4.3 Page headless

Pour une page 100 % custom, resolveBlogContent donne les articles et le design ; designStyle et Markdown conservent le thème et le rendu du corps.

import {
  Markdown,
  designStyle,
  resolveBlogContent,
  resolveEnvBlogConfig,
} from "@monark_it/mch-blog";

export default async function Page() {
  const config = resolveEnvBlogConfig();
  const { articles, design } = await resolveBlogContent(config);

  return (
    <main style={designStyle(design)}>
      {articles.map((article) => (
        <article key={article.id}>
          <h2>{article.title}</h2>
          <Markdown design={design}>{article.body}</Markdown>
        </article>
      ))}
    </main>
  );
}

Compromis : vous renoncez à la pagination, aux metadata, au JSON-LD, aux redirections et à la revalidation clés en main. À réserver aux pages très spécifiques ; pour le reste, partez des wrappers et éjectez un composant.

4.4 Ajouter votre header/footer autour du blog

Le blog vit hors de vos éventuels segments de locale (/blog), donc votre chrome de site ne s'y applique pas automatiquement. C'est voulu : app/blog/layout.tsx vous appartient et le composant BlogLayout du package est un simple passe-plat ({children}), précisément pour que chaque site mette son header et son footer.

// app/blog/layout.tsx
import "@monark_it/mch-blog/styles.css"; // requis : styles du blog
import { BlogLayout } from "@monark_it/mch-blog/next";
import { SiteHeader } from "@/components/site-header";
import { SiteFooter } from "@/components/site-footer";

export default function BlogShell({ children }: { children: React.ReactNode }) {
  return (
    <>
      <SiteHeader />
      <BlogLayout>{children}</BlogLayout>
      <SiteFooter />
    </>
  );
}

Notes :

  • Gardez l'import @monark_it/mch-blog/styles.css : c'est lui qui porte les styles et les variables --mch-*.
  • Si votre header/footer dépend d'un contexte (traductions, providers), gardez ces providers dans ce layout. Le blog n'étant pas préfixé par la locale, il utilise votre locale par défaut (MCH_LOCALE).
  • Le package ne fournit aucun chrome : header, footer, bannière cookies, etc. restent ceux de votre site.
  • Évitez les doublons globaux (ex. données structurées déjà rendues par votre layout racine) : un seul endroit suffit.

5. Variables d'environnement

Six variables, toutes serveur uniquement.

| Variable | Défaut | Rôle | | ----------------- | ------- | ------------------------------------------ | | MCH_SITE_KEY | — | identifiant du site, titre par défaut | | MCH_BASE_URL | — | origine publique (canonical, RSS, JSON-LD) | | MCH_BLOG_PATH | /blog | chemin public du blog | | MCH_LOCALE | fr | locale des dates | | MCH_HUB_URL | — | Hub qui publie la release | | MCH_HUB_SITE_ID | — | identifiant du site dans le Hub |

  • Jamais de préfixe NEXT_PUBLIC_ : ces valeurs sont lues au build et au rendu serveur, pas dans le navigateur.
  • Une valeur modifiée exige un rebuild.
  • En local, elles vivent dans .env.local (écrit par le CLI) ; en production, copiez-les dans les variables de votre plateforme d'hébergement.
  • Pour une préversion : ajoutez MCH_HUB_ENV=staging au déploiement de staging.
  • Sans les variables du Hub, le rendu échoue avec CONFIG_INVALID : il n'existe aucun repli vers des fichiers locaux.

6. Comprendre le rendu

6.1 Entrées du package

| Entrée | Pour | Contenu | | -------------------------------- | ------------- | --------------------------------------------------------------------------------------------- | | @monark_it/mch-blog | serveur/build | config, release, SEO, revalidation | | @monark_it/mch-blog/next | App Router | BlogLayout, BlogListPage, ArticlePage, metadata, sitemap, RSS, mchApiGet/mchApiPost | | @monark_it/mch-blog/ui | navigateur | composants, Markdown, designStyle, types | | @monark_it/mch-blog/styles.css | site | styles mch-* |

L'entrée /ui ne contient aucun import node:* ni next/* : elle peut tourner dans un navigateur (aperçu du Hub). La racine reste l'entrée serveur.

6.2 Thème et styles

  • Le thème est publié avec la release. designStyle(design) pose les tokens --mch-* (--mch-accent, --mch-ink, --mch-surface, --mch-content-width…) sur les conteneurs .mch-blog-page / .mch-article. Aucune variable globale n'est définie sur le site hôte.
  • Dans l'article, l'en-tête et le corps Markdown (texte, images, tableaux) occupent toute la largeur du contenu (--mch-content-width) ; seuls les tableaux et les blocs de code passent en scroll horizontal quand leur contenu dépasse.
  • Toutes les classes du package sont préfixées mch-* et stylées par styles.css, importé par app/blog/layout.tsx. Aucun scan Tailwind n'est nécessaire.
  • Les images (couverture, corps Markdown, articles liés) sont sans bordure par défaut ; un hôte qui en veut une pose --mch-image-border.
  • Le mode sombre est piloté par data-has-dark-palette (palette définie) et data-apply-dark-palette (palette appliquée au visiteur).

6.3 Lecture d'une release

resolveBlogContent(config) lit le pointeur live puis le snapshot de la génération publiée et renvoie :

  • articles : articles triés du plus récent au plus ancien, médias en URLs absolues ;
  • design : thème publié, sinon les défauts ;
  • generation : numéro de génération publiée (0 si rien n'est publié) ;
  • redirects : redirections des anciens slugs (previous_slugs), servies en 301.

En développement, la release est relue à chaque requête (publication visible immédiatement). En production, les lectures passent par le cache Next tagué mch-release (TTL 60 s).

Site connecté sans aucune release publiée : le blog est vide sans erreur (articles: [], design par défaut). Toutes les autres erreurs remontent en BlogError typée : CONFIG_INVALID, RELEASE_FETCH_FAILED, RELEASE_NOT_FOUND, RELEASE_CONFLICT, RELEASE_HASH_MISMATCH, RELEASE_INCOMPATIBLE, RELEASE_STALE_GENERATION, RELEASE_INVALID.

6.4 Revalidation

Après chaque publication, le Hub envoie un webhook signé (JWS) à /api/mch/revalidate. Le site vérifie la signature, relit la génération, puis invalide le tag mch-release et la route du blog : liste, articles, RSS et sitemap repartent de la release à jour.

Deux réponses déclarent l'état du site :

  • l'accusé de revalidation (202) ;
  • /.well-known/mch-release, qui expose la génération servie et la liste overrides.

Le Hub s'en sert pour vérifier le binding automatiquement après déploiement et pour afficher les composants personnalisés dans Design.

6.5 Médias

Les images de la release (couverture et images du corps) sont servies en URLs absolues depuis le Hub :

<MCH_HUB_URL>/api/v1/public/sites/<MCH_HUB_SITE_ID>/media/<hash>.webp

Aucune route locale d'images n'est nécessaire. Quand featured_image est vide, carte, en-tête et articles liés affichent une illustration SVG inline (DefaultCover) qui suit la palette claire ou sombre : jamais d'image cassée.

7. Dépannage

| Symptôme | Cause probable | Action | | ------------------------------------------ | ----------------------------------------------- | ------------------------------------------------------------------------------------ | | Site non configuré / Site non connecté | variables MCH_* manquantes | définir les variables puis rebuild | | /blog sans article | aucune release publiée | publier depuis le Hub | | Article publié pas à jour | webhook non reçu ou cache 60 s | attendre la revalidation ; vérifier le binding dans le Hub | | Image manquante | média non publié ou variables Hub absentes | republier le média ; définir les variables | | Page sans style | styles.css non importé | import "@monark_it/mch-blog/styles.css"; dans app/blog/layout.tsx | | Contraste AA insuffisant dans Design | paires texte/fond sous le seuil | bouton « Corriger tout » ou ajuster les couleurs | | Mode sombre sans effet | darkMode vaut off, ou aucune palette sombre | activer darkMode: system avec une palette sombre définie | | Design ne liste pas un composant éjecté | déclaration au prochain échange | attendre le prochain échange Hub ↔ site ; route ancienne : relancer init/connect |

8. Migration (anciens sites)

Pour un site d'une génération précédente (config fichier, scope @barry_monarkit, dossier images/blog), relancez :

npx @monark_it/mch-create-blog
npx @monark_it/mch-create-blog connect --url https://exemple.com

Les wrappers historiques sont migrés vers le registry _overrides, les artefacts des générations précédentes sont nettoyés, et vos composants modifiés sont conservés.