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.
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.