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

@emmanueldemey/training-kit

v0.0.1

Published

Builds a training from a folder of numbered slides and a folder of numbered workshops: a Slidev deck, a Starlight workshops site with an online editor, PDFs and ZIPs

Readme

@emmanueldemey/training-kit

CI

Turns a folder of numbered slides and a folder of numbered workshops into a complete training:

  • a slide deck (Slidev);
  • a workshops site (Starlight) — one page per workshop, with an optional online editor (StackBlitz) loaded with the workshop folder;
  • the deck and the workshops as PDFs;
  • ZIPs: a starter and a solution per workshop, all the solutions, and a participant kit.

You write Markdown and code. There is no list of chapters to maintain: the number at the start of a file name is its place.

The quickest start is the generator, which writes a working training to build on:

npm create training-kit my-training

Install

In an existing project:

npm install -D @emmanueldemey/training-kit @slidev/cli @slidev/theme-default astro @astrojs/starlight sharp
# only with the online editor:
npm install -D @stackblitz/sdk

Slidev, Astro and Starlight are peer dependencies: your training pins their versions, and can move to a newer Slidev without waiting for a training-kit release. Requires Node.js 22.12 or later.

With pnpm, add shamefullyHoist: true to pnpm-workspace.yaml: Astro resolves some of its own dependencies from the folder it builds, which pnpm's strict layout hides.

The layout

my-training/
  training.config.mjs
  slides/
    1-introduction.md         chapter 1
    2-reactivity.md           chapter 2
    10-testing.md             chapter 10 — after 9: the order is numeric
    _11-draft.md              turned off: kept on disk, left out of everything
    components/  public/      anything Slidev reads next to its entry
  workshops/
    README.md                 optional: the introduction of the workshops
    1-devtools/
      README.md               the instructions — the page, and the handbook chapter
      package.json  src/ …    the starter code
    2-composables/
  solutions/
    1-devtools/               the solution of workshops/1-devtools — same name
    2-composables/

The numbering rule

| Name | Read as | | ----------------------------------------- | ------------------------------------------------------------- | | 1-intro.md, 01_intro.md, 1 intro.md | number 1 — any separator, any padding | | 10-testing.md | number 10, after 9 | | _3-optional.md | turned off — out of the deck, the site, the PDFs and the ZIPs | | notes.md | no number — left out, and reported by training-kit list | | 3-a.md next to 03-b.md | an error: two entries cannot share a number |

The same rule applies to the chapter files of slides/ and to the workshop folders of workshops/. A workshop folder without a README.md gets no page and is reported.

A solution is the folder of solutions/ with exactly the same name as its workshop folder. The build reports a workshop without a solution, and a solution folder that matches no workshop (a typo would otherwise silently publish nothing).

Workshop READMEs

The README is the single source of a workshop's instructions. From it:

  • the leading # heading becomes the page title. TP 3 — Routing, Workshop 3: Routing and Routing all appear as 3. Routing in the menu: the number always comes from the folder name;
  • the blockquote right under the heading (otherwise the first paragraph) becomes the page description.
# TP 3 — Routing

> Add a detail page, and keep the back button working.

## Steps

…

Configuration

training.config.mjs, at the root of the project. Only title is required.

import { defineConfig } from '@emmanueldemey/training-kit';

export default defineConfig({
  title: 'Advanced Vue.js',
  author: 'Jane Doe',
  slides: 'slides',
  workshops: 'workshops',
  solutions: 'solutions',
  playground: true,
  repository: 'https://github.com/me/advanced-vue',
  deck: { theme: 'seriph', headmatter: { lineNumbers: true } },
});

| Option | Default | | | ----------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | title | — (required) | the name of the training: deck cover, site title, handbook cover | | slug | the title, kebab-cased | prefixes the downloads: <slug>-slides.pdf | | author | '' | on the deck cover and the handbook cover | | slides | 'slides' | the folder of numbered chapters | | workshops | 'workshops' | the folder of numbered workshop folders | | solutions | 'solutions' | the folder of solutions, one per workshop, same names. false if there are none | | outDir | 'build' | where training-kit build writes | | lang | 'en' | the language of the site | | deck.theme | 'default' | the Slidev theme — install its package | | deck.headmatter | {} | any Slidev headmatter; it wins over theme and title | | playground | false | the online editor on the workshop pages: true, or an object (below) | | repository | none | a URL, or { url, branch = 'main', dir = '' } — dir when the project is a sub-folder of the repository. Adds "edit this page" and "browse the folder" links |

playground as an object:

| Key | Default | | | ------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | template | 'node' | the StackBlitz project template: node boots a WebContainer, with a terminal | | openFile | ['README.md'] | files to open, in order of preference — the ones the workshop has | | extraFiles | {} | { path: content } added to the online copy only, never over the workshop's own files — a package.json for a starter that has none, say | | limits | '' | a sentence shown above the buttons: what does not run online |

Commands

training-kit list            # what the numbering picks up, in order, and what it leaves out
training-kit slides          # the deck, live — Slidev's own options after it: --port 3031
training-kit site            # the workshops site, live — Astro's own options after it
training-kit build           # everything, into build/
training-kit build --no-pdf  # without the two PDF exports (the slow part)
training-kit build --only slides|pdf|site
training-kit deck            # only write the deck entry, to run Slidev by hand

While slides runs, adding, renaming or removing a chapter updates the open deck. While site runs, saving a workshop README updates its page.

--only site rebuilds the site alone and links the downloads of a previous build; --only pdf builds the deck, both PDFs and the ZIPs, without the site.

What the build produces

build/
  index.html                      the workshops site: the overview
  workshops/<n-name>/             one page per workshop
  resources/                      every download, and a table of the workshop ZIPs
  slides/                         the deck
  downloads/
    <slug>-slides.pdf             the deck, printed slide by slide
    <slug>-workshops.pdf          the handbook: cover, contents, one workshop per page
    <slug>-solutions.zip          all the solutions
    <slug>-participants.zip       both PDFs + the workshop folders, no solutions
    tp/<n-name>-starter.zip       one workshop, ready to unzip and work in
    tp/<n-name>-solution.zip      its solution
  playgrounds/<n-name>.json       the online editor's copy of each workshop
  _redirects                      the deck's SPA fallback
  _headers                        cross-origin isolation for the online editor

Every workshop page links its starter and solution ZIPs. A link only appears when its file was produced: both PDF exports are non-fatal, and a failed export simply drops its link and its place in the participant kit.

node_modules, dist, coverage, logs and .env.local files are never zipped.

PDFs and Chromium

Both PDFs are printed with Chromium. On the first build, Playwright's Chromium is downloaded into its usual cache. To use one already on the machine instead:

TRAINING_KIT_CHROME=/path/to/chrome training-kit build

The deck is printed from the built deck, one slide per page, rather than with slidev export: Slidev's export drives a dev server, which a deck of a few hundred slides defeats.

Deploying

build/ is a static site. _redirects and _headers use Netlify's format; on another host, translate them:

  • /slides/* must fall back to /slides/index.html — a reload on slide 12 would 404 otherwise;
  • with the online editor, /workshops/* must be served with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp. Without them the editor still opens in a new tab, but cannot be embedded in the page.

Generated files

| File | | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | slides/deck.generated.md | the Slidev entry: a cover, then one src: import per chapter. Written into the slides folder because Slidev takes the entry's folder as the deck's root — components/, public/, styles/, layouts/ next to the chapters work as Slidev documents them | | .training-kit/site/ | the Starlight project the site is built from |

Both are rewritten on every run: add them to .gitignore, never edit them.

JavaScript API

The CLI is built on these, exported from the package:

import {
  defineConfig, // identity, for editor completion in training.config.mjs
  loadConfig, // (root) => the resolved config of the project at `root`
  resolveConfig, // (raw, root) => defaults filled in, paths made absolute
  listNumbered, // (dir, { type: 'file' | 'directory', extension?, skip? }) => { entries, ignored }
  orderOf, // ('10-testing.md') => 10, ('_3-off.md') => null
  slugOf, // ('03_First Steps.md', '.md') => '3-first-steps'
  readWorkshops, // (config) => { workshops, ignored, withoutReadme, overview }
  parseReadme, // (markdown, fallbackTitle) => { title, description, body }
  labelOf, // ('TP 3 — Routing', 3) => '3. Routing'
  renderDeck, // the deck entry as a string
  writeDeck, // (config) => writes it, only when it changed
  writeSite, // (config) => writes the Starlight project
  renderHandbook, // the handbook as an HTML string
  build, // (config, { only, pdf }) => what `training-kit build` does
} from '@emmanueldemey/training-kit';

Contributing

pnpm install       # also installs the pre-commit hook (husky)
pnpm test          # Vitest
pnpm run check     # oxfmt, oxlint, knip, jscpd and Vitest — what the CI and the hook run

The CI also scaffolds a training with create-training-kit, points it at the training-kit of the commit, and builds it.

License

MIT