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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@-xun/release

v24.2.2

Published

A semantic-release fork with support for annotated tags and monorepos

Downloads

481

Readme

Black Lives Matter! Last commit timestamp Codecov Source license Uses Semantic Release!

NPM version Monthly Downloads

xrelease (@-xun/release)

This semantic-release fork slightly tweaks the original so that it can work with both polyrepos and monorepos (see below).

[!NOTE]

The only reason to use xrelease over semantic-release is if you are using an symbiote-powered project, your repository uses annotated tags, you need the bug fixes, or your repository is a monorepo. Otherwise, just use semantic-release.


Install

To install xrelease:

npm install --save-dev semantic-release@npm:@-xun/release

If you want to use a specific version of xrelease, provide its semver:

npm install --save-dev semantic-release@npm:@-xun/[email protected]

[!NOTE]

xrelease installations reuse the "semantic-release" name so that plugins with semantic-release as a peer dependency are able to recognize xrelease's presence.

Additional Features

xrelease offers a couple improvements over upstream:

Lightweight and Annotated Tag Support

Both lightweight and annotated tags are supported.

man git-tag says:

Annotated tags are meant for release while lightweight tags are meant for private or temporary object labels.

Support for Monorepos

Monorepo support is implemented via the existing tagFormat configuration option and the introduction of two new options: branchRangePrefix and gitLogOptions.

[!WARNING]

These options have only been tested in release configuration files and might not be available via CLI.

Once properly configured, xrelease should be run once per package to be released, with the current working directory set to the root of each respective package.

For monorepos, if the current working directory does not contain the repository's release configuration file, use --extends to refer to its location explicitly (e.g. --extends ../../release.config.js); xrelease supports using --extends to load plugins from /node_modules/ directories higher up in the repository tree. Further, a tool like Turbo can be used to orchestrate package releases in dependency order.

[!NOTE]

See babel-plugin-tester's release.config.js (polyrepo), symbiote's release.config.js (hybridrepo) or unified-utils's release.config.js (monorepo) for complete functional examples of xrelease configurations in the wild.

See the symbiote wiki or the git diff between this repo and upstream for technical details.

The extended configuration options are:

tagFormat

Type: string
Default: "v${version}"

The git tag format used by xrelease to create and identify releases. When cutting a new release, its corresponding tag name will be generated using tagFormat.

tagFormat is key to proper monorepo support since it dictates which existing tags belong to the current package to be released and which belong to other packages that should be filtered out.

To support a simple monorepo that uses "@"-syntax for its release tags (e.g. [email protected]), your release configuration might include:

// Tell xrelease what package-specific tags look like
tagFormat: `${cwdPackageName}@\${version}`;

[!CAUTION]

\${version} (or ${version} in a non-template string literal) is a Lodash template variable while ${cwdPackageName} is a variable in a template string literal. That is: you are responsible for defining cwdPackageName, while \${version} is replaced by xrelease. Additionally, each tagFormat value must contain the version variable exactly once, and the whole tagFormat value must compile to a valid git reference or commit-ish.

To refactor a polyrepo (that uses the standard semantic-release "v"-syntax for its tags) into a monorepo (that uses the "@"-syntax for its tags), optionally with a root package, use @-xun/scripts's "renovate" command: npx symbiote project renovate --regenerate-assets --preset ....

gitLogOptions

Type: { paths?: string | string[], flags?: string | string[] }
Default: "v${version}"

The git log command line arguments used by xrelease to select commits for further analysis. Currently, gitLogOptions has two valid options: flags and paths, which correspond to git log <flags> -- <paths>.

gitLogOptions is key to proper monorepo support since it dictates what commits belong to the current package to be released and which belong to other packages that should be filtered out.

To support a monorepo attempting to release a new version of my-package-1, your release configuration might include:

gitLogOptions: {
  // Tell xrelease to consider only commits that modify files under these paths
  paths: [
    ':(exclude)../my-package-2',
    ':(exclude)../my-package-3',
    ':(exclude)../my-package-4'
  ];
}

In this example, we used exclusion pathspecs to create a denylist of paths we didn't want instead of an allowlist of paths we do want. Either approach is viable depending on project structure; however, using exclusions ensures important changes that happen outside the package's root directory (such as changes to a shared unpublished library) are considered by xrelease when analyzing commits.

Note how the given pathspecs are relative to e.g. /home/user/my-project/packages/my-package-1. That's because xrelease should always be run at the root of the package to be released.

The pathspec syntax we're using happens to work for releasing each of the other packages as well, assuming they are all share the same parent directory, e.g. /home/user/my-project/packages. If we wanted to release my-package-2 next, we would replace ':(exclude)../my-package-2' with ':(exclude)../my-package-1' and run xrelease again.

To support a polyrepo instead, your release configuration might include:

gitLogOptions: {
  // Tell xrelease not to filter commits at all and instead consider everything
  paths: [];
}

Or we could omit the gitLogOptions object from release.config.js entirely, which would be equivalent.

Finally, you can use flags to ensure git log is invoked with certain flags. For instance, we can tell xrelease to ignore all commits reachable by an "initial" commit (including said commit itself). This could be useful if we forked a large project with many thousands of commits (conventional or otherwise) that should be ignored by the commit analyzer:

gitLogOptions: {
  // Tell xrelease to filter commits created after the latest commit (including
  // itself) with "[INIT]" suffixing its subject, a reference we acquired by
  // running: `git log -1 --pretty=format:%H --fixed-strings --grep '\\[INIT]$'`
  flags: ref ? [`^${ref}`] : [];
}

You can pass any flag that git log understands.

branchRangePrefix

Type: string
Default: ""

The value that prefixes the names of relevant maintenance branches. This is used internally by xrelease to generate the proper branches configurations for maintenance branches that refer to particular packages in a monorepo, and can be left undefined in a polyrepo.

[!CAUTION]

The branchRangePrefix string must only match maintenance branches! If you also define a non-maintenance branch with a name starting with branchRangePrefix, xrelease's behavior is undefined.

To support a simple monorepo that uses "[email protected]"-syntax for its maintenance branch names (e.g. [email protected]), your release configuration might include:

// Tell xrelease to remove this string from maintenance branch names when
// resolving their respective ranges and channels
branchRangePrefix: `${cwdPackageName}@`,
branches: [
  // Tell xrelease what package-specific maintenance branch names look like.
  // Specifically: they must begin with `branchRangePrefix`
  `${cwdPackageName}@+([0-9])?(.{+([0-9]),x}).x`,
  'main'
]

To refactor a polyrepo (that uses the standard semantic-release "x.y.z"-syntax for its maintenance branch names) into a monorepo (that uses the "[email protected]"-syntax for its maintenance branch names), optionally with a root package, use @-xun/scripts's "renovate" command: npx symbiote project renovate --regenerate-assets --preset ....

Example

Putting the new configuration options together, we could use what follows to release packages from the /home/user/my-project hybridrepo (a monorepo with a root package), which was formerly a polyrepo that we turned into a monorepo by giving its root package.json file a workspaces key and creating scoped aliases of existing maintenance branches and tags.

The my-project repo contains the four packages my-package-1 through my-package-4 under /home/user/my-project/packages/* along with a /home/user/my-project/package.json file containing the name of the root package (my-root-package) and a /home/user/my-project/src directory containing the root package's source code.

We can push changes to main, which is our primary release branch that publishes to one or more packages' respective @latest release channel (the default for NPM projects). Or we can push changes to canary, which will publish to one or more packages' respective @canary release channel. We can also push changes to a [email protected] branch, where package-name represents the name of the monorepo package and x.y.z represents the maintenance branch range.

// ./release.config.js

function makeConfiguration() {
  const { cwdPackage } = getCurrentWorkingDirectoryPackageInformation();
  const isCwdPackageTheRootPackage = isRootPackage(cwdPackage);
  const gitLogPathspecs = getExcludedDirectoryPathspecs(cwdPackage);
  const cwdPackageName = cwdPackage.json.name;

  return {
    // Tell xrelease what package-specific tags look like
    tagFormat: `${cwdPackageName}@\${version}`,
    // Tell xrelease to remove this string from maintenance branch names when
    // resolving their respective ranges and channels
    branchRangePrefix: `${cwdPackageName}@`,
    gitLogOptions: {
      // Tell xrelease to exclude commits from the other packages
      paths: gitLogPathspecs
    },
    branches: [
      // Tell xrelease what package-specific maintenance branch names look like.
      // Specifically: they must begin with `branchRangePrefix`
      `${cwdPackageName}@+([0-9])?(.{+([0-9]),x}).x`,
      // If this is the root package, we could accept old-style branch names for
      // the sake of compatibility. But it's usually better to just create new
      // branch names aliases matching the `${cwdPackageName}@` pattern
      //...(isCwdPackageTheRootPackage ? ['+([0-9])?(.{+([0-9]),x}).x'] : []),
      'main',
      {
        name: 'canary',
        channel: 'canary',
        prerelease: true
      }
    ],
    plugins: [
      // ...
    ]
  };
}

module.exports = makeConfiguration();

Appendix

Published Package Details

This is an ESM-only package built by Babel for use in Node.js versions that are not end-of-life. For TypeScript users, this package supports both "Node10" and "Node16" module resolution strategies.

That means ESM source will load this package via import { ... } from ... or await import(...) and CJS source will load this package via dynamic import(). This has several benefits, the foremost being: less code shipped/smaller package size, avoiding dual package hazard entirely, distributables are not packed/bundled/uglified, and a drastically less complex build process.

The glaring downside, which may or may not be relevant, is that CJS consumers cannot require() this package and can only use import() in an asynchronous context. This means, in effect, CJS consumers may not be able to use this package at all.

Each entry point (i.e. ENTRY) in package.json's exports[ENTRY] object includes one or more export conditions. These entries may or may not include: an exports[ENTRY].types condition pointing to a type declaration file for TypeScript and IDEs, a exports[ENTRY].module condition pointing to (usually ESM) source for Webpack/Rollup, a exports[ENTRY].node and/or exports[ENTRY].default condition pointing to (usually CJS2) source for Node.js require/import and for browsers and other environments, and other conditions not enumerated here. Check the package.json file to see which export conditions are supported.

Note that, regardless of the { "type": "..." } specified in package.json, any JavaScript files written in ESM syntax (including distributables) will always have the .mjs extension. Note also that package.json may include the sideEffects key, which is almost always false for optimal tree shaking where appropriate.

License

See LICENSE.

Contributing and Support

Consider contributing to upstream semantic-release instead.

Contributors

All Contributors

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!