node.js - Ordning på nycklar i package.json exports


Jag tror att jag förstår den grundläggande funktionen av exports-nyckeln i package.json-filer:

// 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"
    }
  }
}

Fråga: Spelar ordningen på nycklarna "types", "import", "require" och "default" någon roll? Normalt skulle jag tänka nej, eftersom JSON-objektnycklar är oordnade. Från json.org:

Ett objekt är en oordnad uppsättning av namn/värde-par. Ett objekt börjar med {

Men Typescript-dokumentationen säger att "typer" måste komma först:

Ingångspunkt för TypeScript-upplösning - måste ske först!

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

och NodeJS-dokumentationen säger att "default" ska komma sist

"default" - den generiska standarden som alltid matchar. Kan vara en CommonJS- eller ES-modulfiler. Detta villkor bör alltid komma sist.

Så... spelar ordningen på exportnycklarna någon roll? Om inte, vad menar NodeJS- och Typescript-dokumentationen när de pratar om "först" och "sist"?

Efter att ha bytt plats på ordningen av "typer" med andra nycklar verkar dess ordning inte spela någon roll.

Svar #1

Webpack, som också använder export-nyckeln förklarar detta fält på följande sätt:

Anteckningar om beställning

I ett objekt där varje nyckel är ett villkor är ordningen på egenskaperna betydelsefull. Villkor hanteras i den ordning de anges.

Istället för att se detta som ett vanligt objekt, överväg att det är som detta if-else-fall:

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";
}

Om du skulle byta ordningen, skulle det kunna se ut så här:

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";
}

Även om Typescript förstår .d.ts-filer skulle det använda file_one_more.js eftersom det matchar först.

Jag har försökt byta plats på "types" med andra nycklar. Det verkar inte spela någon roll.

Det kan vara så att Typescript prioriterar types-villkoret över andra. Jag skulle dock hålla mig till vad de rekommenderar—"måste ske först" är trots allt inte samma sak som "bör ske först".


Som en sidonotering, historiskt sett är JavaScript-objektsnycklar oordnade/ordningen är inte garanterad. I praktiken bevarade webbläsare nyckelordningen och detta beteende standardiserades i ES2015: icke-heltalnycklar bevaras i infogningsordning.

JSON-standarden ger inte samma löfte, eftersom det finns många implementationer av JSON-dekodare i olika språk och den föregår ES2015-standarden.

Svar #2

JSON-objekt är ett "oordnat set" eftersom "nycklarna inte har en särskild ordning (dvs. behöver aldrig sorteras)", inte som "har ingen ordning"

Parsat JS-objekt HAR en ordning av nycklar som matchar ordningen i JSON

Beroende på om koden använder for (let k in json) eller if (json.types) kan beteendet vara olika

Så av vilken anledning som helst rekommenderas det att beställa nycklar på det sätt som vissa parser kan förvänta sig


I ditt fall skulle jag rekommendera att du försöker byta dem, se att inget händer, och byta tillbaka dem

Relaterade artiklar