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

@shevky/plugin-open-graph

v0.0.1

Published

Shevky plugin for Open Graph, Twitter Card, and schema-aware JSON-LD generation.

Downloads

98

Readme

Shevky Plugin: Open Graph

Open Graph, Twitter Card, and JSON-LD metadata generation for Shevky.

Features

  • Fills page.og, page.twitter, and page.structuredData
  • Supports schema-driven JSON-LD with schemaType
  • Runs in both hooks: content:ready and page:meta
  • Produces graph-based structured data for home/about/contact pages
  • Supports paginated home listing JSON-LD (ItemList)
  • Supports dedicated schemaType: job-listing pages (separate builder)

Installation

npm i @shevky/plugin-open-graph

Usage

Add the plugin in site.json:

{
  "plugins": ["@shevky/plugin-open-graph"],
  "pluginConfigs": {
    "shevky-open-graph": {
      "twitterSite": "@fatihtatoglu",
      "defaultTwitterCard": "summary_large_image",
      "defaultImage": "/assets/images/default-cover.webp",
      "siteName": "Fatih Tatoğlu",
      "publisherType": "person",
      "faqItemsPath": "faq.accordion.items",
      "newsItemsPath": "press.accordion.items",
      "questionCount": 20,
      "force": false,
      "exposePageMeta": true,
      "enableSearchAction": null,
      "searchActionTarget": "/?q={search_term_string}",
      "searchActionQueryInput": "required name=search_term_string"
    }
  }
}

Mustache Example

<!-- Open Graph -->
<meta property="og:type" content="{{page.og.type}}">
<meta property="og:site_name" content="{{site.title}}">
<meta property="og:title" content="{{page.og.title}}">
<meta property="og:description" content="{{page.og.description}}">
<meta property="og:image" content="{{{page.og.image}}}">
<meta property="og:image:secure_url" content="{{{page.og.image}}}">
<meta property="og:url" content="{{{page.og.url}}}">
<meta property="og:locale" content="{{page.og.locale}}">
{{#page.og.altLocale}}
<meta property="og:locale:alternate" content="{{.}}">
{{/page.og.altLocale}}

<!-- Twitter -->
<meta name="twitter:card" content="{{page.twitter.card}}">
<meta name="twitter:title" content="{{page.twitter.title}}">
<meta name="twitter:description" content="{{page.twitter.description}}">
<meta name="twitter:image" content="{{{page.twitter.image}}}">
<meta name="twitter:url" content="{{{page.twitter.url}}}">
{{#page.twitter.site}}
<meta name="twitter:site" content="{{.}}">
{{/page.twitter.site}}

{{#page.structuredData}}
<script type="application/ld+json">
  {{{page.structuredData}}}
</script>
{{/page.structuredData}}

schemaType

Use explicit schemaType in front matter:

  • post -> graph: WebSite + Person + WebPage + BlogPosting (+ ImageObject when present)
  • job-post -> graph: WebSite + publisher entity + WebPage + JobPosting (job detail)
  • job-listing -> graph: WebSite + publisher entity + WebPage/CollectionPage + ItemList
  • home -> graph: WebSite + (Person or Organization) + WebPage/CollectionPage/HomePage
  • collection + collectionType: home -> paginated home graph
  • about -> graph: WebSite + (Person or Organization) + AboutPage
  • contact -> graph: WebSite + (Person or Organization) + ContactPage
  • press -> graph: WebSite + (Person or Organization) + WebPage/CollectionPage (+ ItemList when entries exist)
  • help -> graph: WebSite + (Person or Organization) + WebPage (or FAQPage when FAQ entries exist)
  • collection -> graph: WebSite + Person + WebPage/CollectionPage (+ mainEntity: ItemList when entries exist)
  • page -> graph: WebSite + publisher entity + WebPage
  • policy -> graph: WebSite + Person + WebPage (policy-aware genre)

Post extras (for schemaType: post)

  • wordCount is included only when wordCount exists in front matter
  • categoryUrl + categoryLabel add:
    • WebPage.breadcrumb (Home -> Category -> Post)
    • BlogPosting.about reference to category webpage (<categoryUrl>#webpage)
  • ImageObject.width and ImageObject.height are included when one of these exists:
    • primaryImageWidth / primaryImageHeight
    • ogImageWidth / ogImageHeight
    • coverWidth / coverHeight

Job extras (detail + listing)

  • Job detail pages emit JobPosting with:
    • title, description, datePosted, url
    • optional validThrough (or fallback from jobValidThroughDays)
    • optional employmentType, identifier, directApply, baseSalary
    • optional hiringOrganization and jobLocation
  • Listing pages (home, collectionType: home, job-listing) emit ItemList entries
    • list item @type becomes JobPosting for detected job entries
  • Type switch is resolved from listed page data (front matter / derived front matter / header.raw)
  • employmentType is read directly from front matter:
    • first: schemaEmploymentType / employmentTypeSchema / employmentSchema / jobEmploymentType
    • then: employmentType / employment / workType / jobType / contractType / positionType
    • numeric values are supported and can be resolved from i18n key: seo.home.jobs.employmentTypes.<value>
    • if missing: "-"
  • jobLocation.address.addressCountry comes from i18n map
    • if country is unknown/unmapped, addressCountry is omitted from JSON-LD
  • Country names are normalized to ISO alpha-2 when possible (e.g. Türkiye -> TR, Dubai -> AE, England -> GB)
  • Country code mapping comes from i18n key seo.jobs.countryCodeMap
  • Job detection tokens and locality country-strip tokens are configurable from plugin config:
    • jobCategoryTokens
    • jobUrlTokens
    • localityCountryTokens
  • Example:
    • "seo": { "jobs": { "countryCodeMap": { "dubai": "AE", "türkiye": "TR", "england": "GB" } } }
  • jobLocation.address.addressLocality is normalized to city-only value (e.g. Istanbul, Turkiye -> Istanbul)
  • Optional identifier is emitted as PropertyValue (from jobId / jobIdentifier / identifier / id / slug / URL slug)
  • Optional hiringOrganization.url and hiringOrganization.sameAs are emitted when present
  • jobLocation.address.streetAddress can be sourced from addressText (front matter shortcut)
  • resolveListingPageName and resolveHomeItemListName are i18n-driven; fallback is always -
  • Suggested keys: seo.home.page.name, seo.home.page.pageName, seo.home.jobs.itemList.name, seo.home.jobs.itemList.pageName, seo.home.itemList.name, seo.home.itemList.pageName

Example front matter for schemaType: job-post:

schemaType: job-post
description: "Kısa ama net ilan açıklaması"
jobLocation:
  address:
    addressLocality: "İstanbul"
    addressRegion: "İstanbul"
    addressCountry: "TR"
    addressText: "Levent, Büyükdere Cad. No: 123"

Help / FAQ extras (for schemaType: help, schemaType: faq, /help/, /faq/)

  • Output is @graph (WebSite + publisher entity + page node)
  • Page node uses name (headline is not used) and does not include mainEntityOfPage
  • If FAQ entries are found, page type becomes ["WebPage", "FAQPage"] and mainEntity is emitted as Question[]
  • FAQ source path can be configured from plugin config:
    • faqItemsPath: single dot-path (example: faq.accordion.items)
    • faqItemsPaths: multiple dot-paths (example: ["faq.accordion.items", "blocks.faq.items"])
  • questionCount limits mainEntity count only for schemaType: help (faq type always lists all questions)
  • FAQ sources (front matter / derived front matter):
    • faqs, faq, questions, qna, helpFaqs
    • each item supports question|q|name|title and answer|a|text|content|description|acceptedAnswer.text

Press extras (for schemaType: press)

  • Output is @graph (WebSite + publisher entity + ["WebPage","CollectionPage"])
  • Page node uses name and does not include mainEntityOfPage
  • Press entries are read from front matter / derived front matter sources:
    • configurable path: newsItemsPath / newsItemsPaths (example: press.accordion.items)
    • pressItems, pressList, press, mediaMentions, mediaCoverage, news
  • If valid entries are found, mainEntity is emitted as ItemList with ListItem -> NewsArticle items
  • Entries pointing to the press page itself / site home are ignored; if no valid press links remain, mainEntity is omitted
  • Item fields:
    • headline (from headline|title|name)
    • url (from url|link|href|canonical)
    • optional datePublished (from datePublished|publishedAt|published|publishDate|date)
    • optional publisher name (from publisherName|publisher.name|publisher|sourceName|source)
  • ItemList name prefers i18n keys: seo.press.itemList.name, seo.press.itemList.title

Collection i18n keys

Recommended grouped structure:

  • seo.collections.tag.description
  • seo.collections.tag.itemList.name
  • seo.collections.category.description
  • seo.collections.category.itemList.name
  • seo.collections.series.description
  • seo.collections.series.itemList.name
  • seo.collections.default.itemList.name (optional fallback)

Templates can include {{label}}.

Policy Type and i18n (for schemaType: policy)

You can classify policy pages in two ways:

  • Explicit: set policyType (recommended)
  • Automatic: configure keyword hints in i18n and let plugin infer from title/slug/category

Supported i18n keys:

  • seo.policy.hints -> object map (<type> -> list of match tokens)
  • seo.policy.genre.<type> -> genre array for that type
  • seo.policy.genre.default -> fallback genre

Example:

{
  "seo": {
    "policy": {
      "hints": {
        "cookie": {
          "tr": ["çerez", "cookies"],
          "en": ["cookie", "cookies"]
        },
        "terms": {
          "tr": ["kullanım koşulları", "kullanim kosullari"],
          "en": ["terms of use", "terms"]
        },
        "disclaimer": {
          "tr": ["sorumluluk reddi"],
          "en": ["disclaimer"]
        }
      },
      "genre": {
        "cookie": {
          "tr": ["Çerez Politikası", "Hukuki"],
          "en": ["Cookie Policy", "Legal"]
        },
        "terms": {
          "tr": ["Kullanım Koşulları", "Hukuki"],
          "en": ["Terms of Use", "Legal"]
        },
        "disclaimer": {
          "tr": ["Sorumluluk Reddi", "Hukuki"],
          "en": ["Disclaimer", "Legal"]
        },
        "default": {
          "tr": ["Politika", "Hukuki"],
          "en": ["Policy", "Legal"]
        }
      }
    }
  }
}

Example:

---
lang: en
title: NVIDIA Jetson Orin Nano Setup
slug: nvidia-jetson-orin-nano-setup
schemaType: post
template: post
cover: /assets/images/jetson-orin-nano-super-developer-kit.webp
description: Step-by-step setup guide.
date: 2026-01-14
updated: 2026-01-15
keywords:
  - jetson
  - jetpack
---

Config Options

  • twitterSite: X/Twitter handle for twitter:site
  • defaultTwitterCard: default card type (summary_large_image)
  • defaultImage: fallback image when page has no cover
  • siteName: optional override for og:site_name
  • publisherType: person (default) or organization
  • jobCategoryTokens: keyword list for category/type based job detection
  • jobUrlTokens: keyword list for URL/slug based job detection
  • faqItemsPath / faqItemsPaths: dot-path to FAQ item list in front matter (faq.accordion.items)
  • newsItemsPath / newsItemsPaths: dot-path to press/news item list in front matter (press.accordion.items)
  • questionCount: max FAQ item count for schemaType: help (default -1, no limit)
  • jobValidThroughDays: fallback day count for JobPosting.validThrough when no explicit end date (default 60)
  • localityCountryTokens: tokens removed from addressLocality to keep city-only value
  • force: overwrite existing front matter meta fields
  • exposePageMeta: expose computed object as page.pageMeta
  • enableSearchAction: force enable/disable SearchAction on home graph
  • searchActionTarget: URL template for search action
  • searchActionQueryInput: query-input string for SearchAction

For publisherType: organization, source fields are fixed:

  • Organization.name <- identity.author
  • Organization.url <- identity.url
  • Organization.logo <- identity.profileImage
  • Organization.sameAs <- identity.social.*

Notes

  • Core should stay focused on markdown-to-html. This plugin handles SEO meta generation.
  • If plugin is not used, only front-matter-driven meta fields are available.

License

MIT