skip to Main Content

I believe I understand the basic functioning of the exports key in package.json files:

// package.json
{
  "exports": {
    ".": {
      // used by typescript
      "types": "./file_with_type_defs.d.ts",
      // used by ESM resolution
      "import": "./file_to_import.mjs",
      // used by CJS resolution
      "require": "./file_to_require.cjs",
      // used by ...others?
      "default": "./file_one_more.js"
    }
  }
}

Question: Does the order of the "types", "import", "require", and "default" keys matter? Normally I would think no way, since JSON object keys are unordered. From json.org:

An object is an unordered set of name/value pairs. An object begins with { …

But Typescript documentation says that "types" must come first:

Entry-point for TypeScript resolution – must occur first!

"types": "./types/index.d.ts"

and the NodeJS documentation says "default" should come last

"default" – the generic fallback that always matches. Can be a CommonJS or ES module file. This condition should always come last.

So… Does the order of the export keys matter? If not, what do the NodeJS and Typescript documentation mean when they talk about "first" and "last"?

Having swapped the order of "types" with other keys, its order seems not to matter.

2

Answers


  1. JSON objects are "unordered set" as "key do not have a particular order(i.e. never have to be sorted)", not as "do not have an order"

    Parsed JS object DOES have an order of keys matching the orger in JSON

    Depending on if the code uses for (let k in json) or if (json.types) the behaviour may be different

    So by whatever reason it’s recommended to order keys in the way some parsers may expect


    In your case I’d recommend you to try swapping them, seeing that nothing happens, and swapping them back

    Login or Signup to reply.
  2. Webpack, which also uses the export key explains this field as follows:

    Notes about ordering

    In an object where each key is a condition, order of properties is significant. Conditions are handled in the order they are specified.

    Rather than see this as a plain object, consider it being like this if-else case:

    let file;
    if (platform_supports('types')) {
          file = "./file_with_type_defs.d.ts";
    } else if (platform_supports('import')) {
          file = "./file_to_import.mjs";
    } else if (platform_supports('require')) {
          file = "./file_to_require.cjs";
    } else if (true) { // default
          file = "./file_one_more.js";
    }
    

    If you were to swap the order, it might be like this:

    let file;
    if (true) { // default
          file = "./file_one_more.js";
    } else if (platform_supports('types')) {
          file = "./file_with_type_defs.d.ts";
    } else if (platform_supports('import')) {
          file = "./file_to_import.mjs";
    } else if (platform_supports('require')) {
          file = "./file_to_require.cjs";
    }
    

    Even though Typescript understands .d.ts files it would use file_one_more.js since it matches first.

    I have tried swapping the order of "types" with other keys. It seems not to matter.

    It might be that Typescript prioritizes the types condition over others. However, I’d stick with what they advise—"must occur first" is not "ought to occur first", after all.


    As a side-note, historically JavaScript object keys are unordered / the order is not guaranteed. In practice, browsers did preserve the key order and this behavior was standardized in ES2015: non-integer keys are preserved in insertion order.

    The JSON standard doesn’t make the same promise, as there are many implementations of JSON decoders in different languages and it the predates ES2015 standard.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search