mux and npm logos

@mux-elements are getting a new (old) home on npm: @mux!

07/13/2022July 13, 2022(5 months ago)
8 min read
07/13/2022July 13, 2022(5 months ago)
8 min read

@mux-elements are getting a new (old) home on npm: @mux!

tl;drLink to tl;dr

All of our Mux Elements are moving from @mux-elements to @mux on npm. In this post, we explain why we’re making this move and introduce some steps we took and tools we built to help with your migration, just in case!

First off, what are Mux Elements?Link to First off, what are Mux Elements?

Mux Elements is a collection of open source components we’re creating to make it easier for developers to build their applications with Mux. We’ve been quietly building these out in the muxinc/elements repo. The most significant element so far is Mux Player, which is currently in public alpha and soon to be in public beta.

Until now, we’ve been publishing Mux Elements under the @mux-elements npm scope. That approach had a few benefits. It felt nice to have all the elements grouped together in their own npm scope separate from the @mux scope.

What's happening?Link to What's happening?

Mux Elements are moving to a new home... or rather an old home, but new to them... or rather moving into their parents' home... err... @mux-elements are moving to @mux on npm! While there are things we really liked about having our own special npm scope for @mux-elements (discoverability being at the top of that list), after some debate, we decided moving to @mux was the right decision. Part of this was certainly to make sure we didn't introduce more complexities on our side. But another, related reason was to make sure we continue to earn your trust as users of our tools, codebases, and products.

If you're just here to find out how to have a smooth migration and don't want the @mux scope migration's whole life story, go ahead and scroll down to "Introducing mux-elements-codemod" below. Otherwise, read on.

Why now?Link to Why now?

Here at Mux, we’ve always taken security seriously. But over the past year, we’ve been leveling up our security infrastructure and processes. For just a couple of examples, we’ve recently gotten our ISO/IEC 27001 certification and introduced vulnerability bounties. Right now, one of our current initiatives is to improve supply chain security, including for open source projects.

This brings us to our current predicament. While there are many great things about npm, its organization and scope management are unfortunately a bit constrained. The root of the problem is captured in this single sentence:

Every organization is granted an organization scope, a unique namespace for packages owned by the organization that matches the organization name. (https://docs.npmjs.com/about-organization-scopes-and-packages, emphasis added)

A 1:1 relationship between organizations and scopes means we'd need to manage and monitor multiple organizations when, for example, applying token rotations or forced invalidation across other systems such as GitHub. And more organizations means more potential points of failure.

Given these constraints, we've decided our best path forward is to move everything to the mux org and its corresponding @mux scope. And, although there isn't any strong pressure from a security perspective to do this now, we wanted to make sure this happened before our official Mux Player public beta so most of the pain of migration would be felt by us here at Mux.

But what about folks who would feel the pain?Link to But what about folks who would feel the pain?

Completing this migration before Mux Player's public beta will help prevent some pain for a lot of folks. But what about all of you early adopters (including folks at Mux) who've already started using Mux Elements? We're super grateful for everyone who's explored and experimented and provided feedback to help make Mux Elements better. And, as you all hopefully know by now, we also take the Developer Experience very seriously. Everyone on the Mux Elements/Mux Player team has been at this long enough that we all have battle scars from a major dependency refactor. So once we made the decision to migrate, we wanted to also make sure we weren’t inflicting any fresh wounds on anyone using Mux Player or other Mux Elements.

To accomplish this goal, we came up with a game plan:

  1. Get all of our Mux Elements packages migrated to the @mux scope.
  2. Build and publish some tooling to make migration as painless as possible (more on this below).
  3. Use migration tooling on one or more Mux-owned codebases that have Mux Elements dependencies to confirm there are no glaring kinks, bugs, or assumptions and to make sure the migration process will be simple.
  4. Write this blog post and provide any additional documentation needed for a smooth migration and context.
  5. Do one final release on the @mux-elements scope for our Mux Elements packages to formally announce its deprecation and add some additional in-code console errors with helpful links when loading the deprecated code in your codebase.

By taking the last step, our goal was to provide as much visibility about the scope change as possible. In fact, you may be here as a result of the links we provided in the console.warn when loading the packages or in the deprecation notices for the packages themselves. If you're curious how we rolled this out, see our final PR for the @mux-elements scope.

Central to reducing friction, however, was building a new CLI tool: mux-elements-codemod.

Introducing mux-elements-codemodLink to Introducing mux-elements-codemod

mux-elements-codemod is effectively a gussied-up dependency updater plus a gussied-up sed or "find and replace all." Because we only have imports and similar syntax that need migrating, we didn't really have to worry about more complex code syntax or integration with ASTs. This means that, if you are only using mux-elements in a few well-known places, mux-elements-codemod may be overkill; you can probably go ahead and "find and replace all" however you see fit.

Otherwise, mux-elements-codemod simplifies things by targeting specified file extensions (or "js ts jsx tsx html mjs cjs" by default) in specified directories (or "./" from wherever it's run by default) with the option of ignoring certain directories (and always ignoring node_modules). It also provides a dry run by default so that you can see what changes will be made before applying them.

Here's a simple example of how you might use the package dependency codemod command plus the imports codemod command from the root of a standard Next.JS application using npx:

bash
(From project root)
% npx @mux/mux-elements-codemod --package
(Confirm dry run changes)
% npx @mux/mux-elements-codemod --package --force
% npx @mux/mux-elements-codemod --imports --ignore .next
(Confirm dry run changes)
% npx @mux/mux-elements-codemod --imports --ignore .next --force

And here's an example of how you might use it from the root of a standard Create React App:

bash
(From project root)
% npx @mux/mux-elements-codemod --package
(Confirm dry run changes)
% npx @mux/mux-elements-codemod --package --force
% npx @mux/mux-elements-codemod --imports ./src
(Confirm dry run changes)
% npx @mux/mux-elements-codemod --imports ./src --force

Also, we're planning on keeping this CLI tool around, just in case additional major migrations come up in the future that we can make less painful via programmatic code updates.

For more details, you can check out the project, including its readme (of course we made it open source!).

A concrete example: migrating stream.newLink to A concrete example: migrating stream.new

Here's what an actual migration looks like for stream.new using mux-elements-codemod.

First, from the project's root directory, do a dry run of the package update codemod using npx to run @mux/mux-elements-codemod:

bash
% npx @mux/mux-elements-codemod --package
The following dependencies will be removed and re-added with the updated @mux/ scope:
@mux-elements/mux-player-react
@mux-elements/mux-video-react

Second, if everything looks right, run the same codemod with --force to apply the dependency updates:

bash
npx @mux/mux-elements-codemod --package --force
Running yarn remove on @mux-elements/mux-player-react @mux-elements/mux-video-react
yarn remove v1.22.17
[1/3] Removing module @mux-elements/mux-player-react...
[2/3] Removing module @mux-elements/mux-video-react...
[3/3] Regenerating lockfile and installing missing dependencies...
...
success Uninstalled packages.
Done in 4.00s.
Running yarn add on @mux-elements/mux-player-react @mux-elements/mux-video-react
yarn add v1.22.17
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 6 new dependencies.
info Direct dependencies
├─ @mux/mux-player-react@0.1.0-beta.22
└─ @mux/mux-video-react@0.5.0
info All dependencies
├─ @github/template-parts@0.5.3
├─ @mux/mux-player-react@0.1.0-beta.22
├─ @mux/mux-player@0.1.0-beta.22
├─ @mux/mux-video-react@0.5.0
├─ @mux/mux-video@0.8.1
└─ media-chrome@0.6.9

Third, run the imports codemod to now do a dry run of updating the codebase from @mux-elements to @mux. This won't actually modify any files. Since stream.new is a Next.js app with a fairly standard directory structure (i.e., there's no top-level /src directory), we're going to target the project root directory (the default target directory is ./) and then use --ignore to exclude relevant directories:

bash
% npx @mux/mux-elements-codemod --imports --ignore .next
Running in dry run mode. The following files will be modified:
components/mux-player.tsx
Before:
1:import MuxPlayer from '@mux-elements/mux-player-react';
2:import type MuxPlayerElement from '@mux-elements/mux-player';
After:
1:import MuxPlayer from '@mux/mux-player-react';
2:import type MuxPlayerElement from '@mux/mux-player';
components/mux-video.tsx
Before:
1:import MuxVideo from '@mux-elements/mux-video-react';
After:
1:import MuxVideo from '@mux/mux-video-react';
components/player-loader.tsx
Before:
2:import type MuxPlayerElement from '@mux-elements/mux-player';
After:
2:import type MuxPlayerElement from '@mux/mux-player';
types/index.ts
Before:
0:import type MuxPlayerElement from '@mux-elements/mux-player';
1:import type MuxVideoElement from '@mux-elements/mux-video';
After:
0:import type MuxPlayerElement from '@mux/mux-player';
1:import type MuxVideoElement from '@mux/mux-video';

Finally, if everything looks good, run the same command with the --force flag:

bash
% npx @mux/mux-elements-codemod --imports --ignore .next --force
Modifying the following files to replace `@mux-elements/` scope with `@mux/`:
components/mux-player.tsx
components/mux-video.tsx
components/player-loader.tsx
types/index.ts

You can check out the final result in this PR.

ConclusionLink to Conclusion

If we did everything right, we've hopefully saved at least a few folks some pain without sacrificing our priorities of trust and security. We've also laid some groundwork that we can share with others and build on ourselves if any new migration pains ever come up. As always, if you have any questions or run into any problems, reach out!

Create an account

No credit card to start. $20 in free credits when you're ready.

Get started free

Subscribe to the Mux newsletter