package.json „exports” și „imports” în Node.js

Module și package.json Node.js 12.7 (exports), 14.6 (imports)

Alege ce fișiere ale unui pachet pot fi importate din afară (exports) și definește alias-uri interne cu # (imports).

Sintaxă
"exports": {
  ".": "./src/index.js",
  "./format": "./src/format.js"
},
"imports": {
  "#utile/*": "./src/utile/*.js"
}

Ce face package.json "exports" și "imports"

Câmpul "exports" spune ce poate importa cineva din pachetul tău. "." este intrarea principală (import "pachet"), iar celelalte chei sunt subcăi (import "pachet/format"). Orice fișier care nu apare în exports este ascuns: încercarea de a-l importa dă ERR_PACKAGE_PATH_NOT_EXPORTED. Când există, exports are prioritate față de main.

Valorile pot fi condiționale: {"import": "./index.mjs", "require": "./index.cjs"} dă alt fișier pentru import și pentru require, iar "default" este varianta de rezervă. Ordinea cheilor contează: prima potrivire câștigă, deci "default" stă ultima.

Câmpul "imports" definește alias-uri private, care încep obligatoriu cu #: import { slug } from "#utile/text" în loc de ../../utile/text.js. Merg doar din interiorul pachetului. Ambele câmpuri acceptă * ca tipar.

Exemple

Exemplul 1: Un pachet local cu exports

Creăm un pachet în node_modules/preturi-ro, cu intrarea principală și subcalea ./format. Fișierul intern.js nu este exportat, deci nu poate fi importat.

import { mkdirSync, writeFileSync } from "node:fs";

const dir = "node_modules/preturi-ro";
mkdirSync(dir, { recursive: true });
writeFileSync(`${dir}/package.json`, JSON.stringify({
  name: "preturi-ro",
  type: "module",
  exports: { ".": "./index.js", "./format": "./format.js" },
}));
writeFileSync(`${dir}/index.js`, "export const tva = 0.21;");
writeFileSync(`${dir}/format.js`, 'export const lei = (s) => s.toFixed(2) + " lei";');
writeFileSync(`${dir}/intern.js`, "export const secret = 42;");

const { tva } = await import("preturi-ro");
const { lei } = await import("preturi-ro/format");
console.log(tva, lei(99.9));

try {
  await import("preturi-ro/intern.js");
} catch (eroare) {
  console.log(eroare.code);
}

Rezultat

0.21 99.90 lei
ERR_PACKAGE_PATH_NOT_EXPORTED

Exemplul 2: Alias-uri interne cu imports

import { mkdirSync, writeFileSync } from "node:fs";

mkdirSync("app/src/utile", { recursive: true });
writeFileSync("app/package.json", JSON.stringify({
  name: "app",
  type: "module",
  imports: { "#utile/*": "./src/utile/*.js" },
}));
writeFileSync("app/src/utile/text.js",
  'export const slug = (s) => s.toLowerCase().normalize("NFD").replace(/[\\u0300-\\u036f]/g, "").replaceAll(" ", "-");');
writeFileSync("app/src/index.js", 'import { slug } from "#utile/text"; console.log(slug("Piața Unirii Iași"));');

await import("./app/src/index.js");

Rezultat

piata-unirii-iasi

Exemplul 3: Exporturi condiționale pentru import și require

{
  "name": "preturi-ro",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "default": "./dist/index.mjs"
    },
    "./package.json": "./package.json"
  }
}

Greșeli frecvente

  • Adaugi exports într-un pachet deja publicat și utilizatorii care importau pachet/lib/ceva.js primesc ERR_PACKAGE_PATH_NOT_EXPORTED. Este o schimbare incompatibilă: publică o versiune majoră nouă sau exportă și acele căi.
  • Pui "default" înaintea lui "import": prima condiție potrivită câștigă, deci "import" nu mai este folosit niciodată.
  • Scrii căile din exports fără ./ în față ("index.js"): Node.js le respinge. Toate țintele încep cu ./.

Comenzi înrudite

  • package.json: Fișierul de configurare al unui proiect Node.js: nume, versiune, tipul modulelor, scripturi, dependențe și versiunea Node cerută.
  • Module ES (import) și CommonJS (require): Cele două sisteme de module din Node.js: import/export (ES) și require/module.exports (CommonJS), și cum le combini.
  • npm publish: Publică un pachet în registrul npm; cu npm pack –dry-run vezi înainte exact ce fișiere pleacă.
  • npm workspaces: Mai multe pachete într-un singur depozit (monorepo), cu un singur node_modules și comenzi rulate în toate deodată.