Skip to Content
  • Website
Codsen
  • Home
  • Open Source
  • Articles
  • About

Thoughts about sort-package-json v2 pure ESM

by Roy Revelt   ·   posted on Monday, 17 October 2022   ·   remixreactmeta

  • the top
  • SUB-KEY IMPOR…
  • TESTING ALL…
  • A FRIENDLIER…
  • TAKEAWAY

The npm package sort-package-jsonopens in a new tab migrated to ES Modules in their v2 release and indirectly broke their named exports which our packages have been consuming. It was a major semver bump, but breaking changes slipped from their changelog and readme radar, so I wanted to explain what actually happened there.


Sub-key imports breaking on ESM

Consider this tiny npm package:

const extras = "bar";
function foo() {
  return true;
}

module.exports = foo;
module.exports.foo = foo;
module.exports.extras = extras;
module.exports.default = foo;

In a non-ESM program, you can consume it via a require:

const foo = require("foo");

// consume the default:
console.log(foo());
// => true ✅

// consume other exports:
console.log(foo.extras);
// => "bar" ✅

Now, we migrate our foo package to pure ES modules, and we get the following:

const extras = "bar";
function foo() {
  return true;
}

export default foo;
export { extras };

The foo is still a default export, and extras is a named export.

But now, people can’t consume foo.extras anymore!

import foo from "foo";

console.log(foo());
// => true ✅

console.log(foo.extras);
// => undefined! 😱

It’s because now, people must import extras separately:

import foo, { extras } from "foo";

console.log(foo());
// => true ✅

console.log(extras);
// => "bar" ✅

That’s what happened to sort-package-json; the v2 release inadvertently broke the default sort options array export sortPackageJson.sortOrder, which in turn, broke our json-sort-cli after bumping dependencies.

Testing all exports

If we dig deeper, sort-package-json readme is still implyingopens in a new tab one can import sortPackageJson.sortOrder:

Fig 1. Current sort-package-json readme
Fig 1. Current sort-package-json readme

My hypothesis is if maintainers had added tests for each export entry during the ESM migration, those tests would have failed and, thus, helped them to remember to update the documentation.

The following test was missing from their test suite from the beginning:

// sort-package-json/tests/main.js
import test from "ava";
import sortPackageJson from "../index.js";

test("main", (t) => {
  t.true(Array.isArray(sortPackageJson.sortOrder), "sortOrder is exported");
}); // ❌

Always test each API export.

For example, here’s yours truly testing the detergent.jsopens in a new tab, checking that the version is still being exported. If you think it’s redundant, it’s not — many things can go wrong there.

A friendlier readme

A friendly readme has three components:

  1. an install — npm install bar;
  2. an import — import { foo } from "bar"; — importing everything that’s available!
  3. a consumption example — const result = foo("bar"); console.log(result);

Let’s check the sort-package-json readme:

Fig 2. But what exactly does this program export?
Fig 2. But what exactly does this program export?

The import statement containing all exports is missing, and the sortOrder is not mentioned. This readme is like a cigarette counter in the UK — it’s shut, and you have to ask what you want first.

I’ll raise a PR later but let’s edit this on Web Developer tools to how it should be:

Fig 3. Clearly defined exports
Fig 3. Clearly defined exports

At this point, we can notice the confusion between two sortOrders — the default order config export and the options setting. I think it was not a wise decision to rename defaultSortOrder in export { defaultSortOrder as sortOrder } hereopens in a new tab. But that’s a next level.

We could also link the text “Pure ES Modules” to where Sindre Sorhus links his, https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99copens in a new tab.

Takeaway

  1. Let’s get rid of default exports from our APIs, especially from npm packages-programs (front-end-related npm packages such as React components are a different story).
  2. Since the ESM move is a major semver change, it’s a good moment to get rid of default exports during the same release, during the same major semver bump.
  3. Let’s unit-test all exports — and it’s irrelevant, is program ESM or not. Unit test coverage won’t catch missing export unit tests, so you must review all exported things manually and make sure they all are covered by unit tests.
  4. Consider developers who are starting to learn the craft — they will struggle if you don’t include all three “ingredients” in your readme: an install, an import and a consumption example.

↑ back to top
older 

Copyright

All rights reserved © Roy Revelt 2026
All our open source packages are under MIT licenceopens in a new tab

Activities

🐛 See a bug? Raise an issueopens in a new tab
💘 Check out the Indiewebopens in a new tab and Libera manifestoopens in a new tab