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

@wtf/semantic-release-trains

v7.0.0

Published

Several release trains of one monorepo, by semantic-release alone: each train its own folders, tags, version, notes and changelog, a commit counted for the trains whose files it changes. The plugin (.), one package, listed once for each role: every entry

Readme

@wtf/semantic-release-trains

Several release trains from one monorepo, with semantic-release alone. A train is a set of folders that is released together, with its own tags, version, release notes and changelog. A commit counts for each train whose files it changes, and for no other train.

  • No other tooling: an ordinary plugin of semantic-release, in an ordinary config that is yours.
  • Any plugin, any preset: an entry of the plugin of wrap gives the commits of the train to the plugin it wraps, as the commit analyzer, the release notes generator and the plugin of the host, with their options.
  • One package, of JSON options, listed once for each role, so a config of literals serves a global install of semantic-release, as the semantic-release component of to-be-continuous makes one.
  • Safe order: a train that depends on another train warns, or fails, when it would release against versions that are not released.
  • A thin adapter over @wtf/release-trains, the library, which runs on any system its caller gives it.

Install

npm install --save-dev semantic-release @wtf/semantic-release-trains \
  @semantic-release/changelog @semantic-release/git @semantic-release/gitlab

Use

Declare the trains once, at the root of the config: semantic-release gives each option of the root to every plugin. The train of a run is RELEASE_TRAIN of its environment, which each plugin reads, as a plugin of semantic-release reads a variable of its own; an option train of the root wins over it. Beside them, ecosystems, required, names the module of each ecosystem of the repo, as @wtf/release-npm, whose packages the trains read: each is loaded of the repo, as an entry of wrap loads the plugin it wraps, by its name, its path from the repo, or its file: URL (EECOSYSTEM of one that cannot be loaded, or is no ecosystem).

// release.config.mjs
import { BREAKING, trainChangelog, trainTagFormat } from '@wtf/semantic-release-trains/config'

const trains = {
  trains: {
    paths: ['packages/release-trains', 'packages/semantic-release-trains'],
    shared: ['pnpm-lock.yaml']
  },
  replace: {
    paths: ['packages/release-replace', 'packages/semantic-release-replace'],
    shared: ['pnpm-lock.yaml']
  }
}
const train = process.env.RELEASE_TRAIN
const preset = { preset: 'conventionalcommits' }

export default {
  trains,
  ecosystems: ['@wtf/release-npm'],
  tagFormat: trainTagFormat(trains, train),
  ...BREAKING,
  plugins: [
    ['@wtf/semantic-release-trains', { wrap: ['@semantic-release/commit-analyzer', preset] }],
    [
      '@wtf/semantic-release-trains',
      { wrap: ['@semantic-release/release-notes-generator', preset] }
    ],
    ['@semantic-release/changelog', { changelogFile: trainChangelog(trains, train) }],
    ['@semantic-release/git', { message: `chore(release): ${train} \${nextRelease.version}` }],
    ['@wtf/semantic-release-trains', { wrap: '@semantic-release/gitlab' }]
  ]
}
RELEASE_TRAIN=trains npx semantic-release
RELEASE_TRAIN=replace npx semantic-release

Each run releases one train, as this repo releases its packages: the tags trains-v4.0.0 and replace-v3.1.0, the changelogs packages/release-trains/CHANGELOG.md and packages/release-replace/CHANGELOG.md, and a release commit chore(release): trains 4.0.0.

The entries of the plugin

The plugin is one package, which a config lists once for each role, each entry where its role runs, as a config lists @semantic-release/exec once for each command. Each entry does its role alone, and every entry checks the trains, in verifyConditions, once a run:

| Entry | What it does | | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | '@wtf/semantic-release-trains' | Checks the train of the run, the trains, the tag format, overlapping paths, and the ranges on other trains | | ['@wtf/semantic-release-trains', { wrap: <plugin> }] | The same, and gives the plugin it wraps the commits of the train, in each step it has; of an analyzer, the dependency trains checked first | | ['@wtf/semantic-release-trains', { generated: true }] | The same, and stages the folders the train's build generates, in prepare, as each .generated lists them |

An entry has one role at most: wrap and generated together fail with EOPTIONS, as each runs in a place of its own, the wrapped plugin where it reads the commits, and generated after the build that writes the folders.

An entry of wrap that wraps a notes generator takes heading: 'train': the first heading of the notes names the train before the rest of its line, as ## trains [4.0.0](…) (2026-11-02), of any generator, and notes of no heading are headed ## trains 4.0.0. None by default. See Changelogs.

On the train

semantic-release gives each plugin its own copy of the context of a run, with every commit of the repo since the train's last tag: no plugin can filter the commits of another. It releases by the highest type of release any analyzer returns, joins the notes of every generator, and the plugin of the host comments on the merge request of each commit when it succeeds. So wrap each plugin that reads the commits in an entry of wrap, which gives each of its steps the commits of the train alone, and list no commit analyzer or release notes generator unwrapped.

wrap names the plugin as an entry of plugins names one: its name alone, or its name and its options, as preset or releaseRules. The entry is JSON, which a config of literals, of no import, and a JSON config write as it is:

{
  "branches": ["main"],
  "ecosystems": ["@wtf/release-npm"],
  "trains": {
    "trains": { "paths": ["packages/release-trains", "packages/semantic-release-trains"] },
    "replace": { "paths": ["packages/release-replace", "packages/semantic-release-replace"] }
  },
  "plugins": [
    [
      "@wtf/semantic-release-trains",
      { "wrap": ["@semantic-release/commit-analyzer", { "preset": "conventionalcommits" }] }
    ],
    ["@wtf/semantic-release-trains", { "wrap": "@semantic-release/release-notes-generator" }],
    ["@wtf/semantic-release-trains", { "wrap": "@semantic-release/gitlab" }]
  ]
}
RELEASE_TRAIN=trains npx semantic-release --tag-format 'trains-v${version}'

A JSON config cannot write the tag format of the train: give it on the command line, or the run fails with ETAGFORMAT, as it would read the tags of another train. A config of literals writes it out, as `${process.env.RELEASE_TRAIN}-v\${version}`: see Configuring trains.

The wrapped plugin is given the options of the root of the config, as parserOpts of BREAKING, and its own over them, as semantic-release gives any plugin. The entry loads it as semantic-release does: of the plugins semantic-release depends on, as the commit analyzer and the release notes generator, in the node_modules of the semantic-release beside it, of a global install too; or else of the repo, which installs any other; or else beside this package, of a global install. The analyzer it wraps also checks the trains the train depends on, by dependencies of the root: warn, fail or ignore. By cascade of the root, true by default, it releases the train when a dependency train's last release is outside a range of a workspace: form that the train's last release published: at least a major of a peer that a major left, else a patch; and the notes generator it wraps has a section ### Dependencies of each after its notes. See Dependency rules.

semantic-release calls each step of the plugin of every entry. Of a step an entry has no role in, or the plugin it wraps has not, it gives what semantic-release takes as nothing: no type of release, no notes (''), and of publish and addChannel false, which semantic-release lists as a release of no name, of the plugin's; the plugins of the host, which list the releases of a run by their names, leave it out.

Exports

| Export | What it is | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | @wtf/semantic-release-trains | The plugin (the default export), its steps, and the types of the options | | @wtf/semantic-release-trains/config | Values a config is written with: trainTagFormat, trainChangelog, trainAssets, trainOfTag, skipPlugins, stepId, BRANCHES, BREAKING | | @wtf/semantic-release-trains/schema | The validators of the options of its plugin, and of its part of a config; jsonSchema, its schema.json | | @wtf/semantic-release-trains/errors | The codes of its errors, PLUGIN_ERRORS | | @wtf/semantic-release-trains/tag | releaseTag, the release of a tag semantic-release made, by the plugins of the tag's run; its bin, semantic-release-trains-tag | | @wtf/semantic-release-trains/info | infoOf, dotenvOf and decideTrain: the next release of each train, for a build to inline; its bin, semantic-release-trains-info |

./config adds no plugin: which plugins a run has, and in which order, is the config's.

The other plugins

Each other plugin is a package of its own, which a config names whole, and a global install of semantic-release installs by that name: @wtf/semantic-release-trains-changelog, the changelog of each package; @wtf/semantic-release-npm, every package of the train published; and, of no train, @wtf/semantic-release-replace, text replaced in files, as the version of the badge of a README, and @wtf/semantic-release-validate, the config validated.

From the tag

A release in two steps publishes in the pipeline of the tag, not in semantic-release's run of the branch, by the same plugins: the config gives each run its own, by RELEASE_TAG, which semantic-release-trains-tag sets of the tag before it loads the config. semantic-release releases no tag it has, so the bin runs semantic-release's own config, plugins and branches, but its decision and its tag: the version is the tag's, the branch and channel those semantic-release released it on, of its note of the tag, and the notes those the notes generator writes again. Then the steps of the tag's plugins, verifyConditions to success.

semantic-release-trains-tag [<tag>] [--dry-run]   # the tag, else CI_COMMIT_TAG or GITHUB_REF_NAME

See the CI recipes for the config and the job.

The next release of each train

semantic-release-trains-info writes what the next release of each train will be, for a build to inline, as a dotenv file: of each train, in the order of the config, SEMREL_INFO_<TRAIN>_LAST_VERSION, SEMREL_INFO_<TRAIN>_NEXT_VERSION and SEMREL_INFO_<TRAIN>_NEXT_VERSION_TYPE. Each train is a dry run of its own, after the tags the bin stood in on HEAD for the releases it predicted of the trains before it, which it removes when it is done: a train sees the trains it depends on as released, as it does in the release job, and the releases they owe it cascade. It tags, commits and pushes nothing, and of SIGINT or SIGTERM it removes the tags it stood in before it exits. A train with nothing to release has an empty next version and type; its last is the release semantic-release counts of the branch, so a prerelease of a merged branch is not the last of main.

semantic-release-trains-info [--output <file>]   # semrel.out.env by default

See the CI recipes for the job.

As a library

What the plugin runs is @wtf/release-trains, of no release tool: verifyTrain and unreleasedDependencies of ./verify, trainOfRun and trainsOfOptions of ./trains, filterCommits of ./paths, and stageGeneratedFolders of ./generated, each by the system of @wtf/release-system its caller gives it:

import type { System } from '@wtf/release-system/system'
import { stageGeneratedFolders } from '@wtf/release-trains/generated'

export function stageReference(system: System) {
  return stageGeneratedFolders({ folders: ['packages/docs/api'], system })
}

Documentation

The guides are in the docs package: getting started, configuring trains, dependency rules, publishing, changelogs, CI recipes for GitLab CI and GitHub Actions, the system, and troubleshooting.

Schema

@wtf/semantic-release-trains/schema.json is the JSON Schema of its part of a config of semantic-release, the entries of plugins of its plugin, for an editor; @wtf/semantic-release-trains/schema has the same as a validator, a Standard Schema, for code, with the validator of the options the plugin is given, as it checks them. Compose the schemas of the packages a repo installs in a release.schema.json, by allOf: see Validating a config.

License

MIT