Búsqueda de sitios web

node.js - el campo `exports` en package.json no funciona con TypeScript


La versión 13.2 de Node.js permite módulos ESM y un nuevo campo package.json, llamado exports, para seleccionar y reescribir archivos exportados.

Antes de la versión 13.2, estaba importando archivos desde la carpeta dist de mi biblioteca con:

import myfile from 'mylibrary/dist/myfile'

Con la 13.2, agregué esto a mi package.json:

exports: {
    "./": "./dist/"
}

Y trató de importar archivos con:

import myfile from 'mylibrary/myfile'

Pero Typescript lanza el error 2307, diciendo que el módulo no se encuentra.

Respuesta #1

Finalmente se ha vuelto disponible en Typescript 4.7

Tu biblioteca:

// package.json
"exports": {
  ".": "./dist/index.js",
  "./svg": "./dist/svg.js",
  "./html": "./dist/html.js",
  "./package.json": "./package.json"
},

Su código de cliente debe tener:

// tsconfig.json
"compilerOptions": {
  "moduleResolution": "node16" // or "nodenext"
}

Antes de TypeScript 4.7, podías utilizar typesVersions:

// package.json
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
  ".": "./dist/index.js",
  "./svg": "./dist/svg.js",
  "./html": "./dist/html.js",
  "./package.json": "./package.json"
},
"typesVersions": {
  "*": {
    "svg": ["dist/svg.d.ts"],
    "html": ["dist/html.d.ts"]
  }
}

Respuesta #2

Solución completa de TypeScript 4.7+

La respuesta principal actual no fue suficiente para mí: obtendría el siguiente error al intentar usar la biblioteca en un paquete que tiene módulo: bundler.

error TS7016: No se pudo encontrar un archivo de declaración para el módulo X. Implícitamente tiene un tipo 'any'. Hay tipos en X, pero este resultado no se pudo resolver al respetar los 'exports' de package.json. Es posible que la biblioteca X necesite actualizar su package.json o sus typings.

La definición completa en package.json requiere una declaración de tipos en los exports. Además, si distribuyes tanto ESM como CommonJS, necesitas usar un archivo de definiciones de tipos distinto (por alguna razón desconocida): resolví esto usando rollup-plugin-copy

package.json

  ...
  "main": "./dist/index.common.js",
  "module": "./dist/index.mjs",
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.mts",
        "default": "./dist/index.mjs"
      },
      "require": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.common.js"
      }
    }
  },
  "types": "./dist/index.d.ts",
  ...

types y module son opcionales, para compatibilidad con versiones antiguas. main también probablemente es opcional si se definen exports; si el type en package.json no está definido como 'module', entonces debería ser Common.js, de lo contrario ESM. También ten en cuenta que es preferible usar la extensión .mjs para módulos; también existe la extensión cjs para CommonJS (y .d.cts), pero el .js regular está bien y es el más compatible hacia atrás.

Tenga en cuenta que la condición de "types" siempre debe ir primero en "exports". [0]

Si usas Rollup, puedes usar rollup-plugin-copy para copiar el archivo de declaración:

rollup.config.mjs

import copy from 'rollup-plugin-copy';

export default [
  // ...
  {
    // ...
    plugins: [
      // ...     
      copy({
        targets: [
          { src: 'dist/index.d.ts', dest: 'dist', rename: 'index.d.mts' },
        ],
      }),
    ]
  }
];

Además, esta utilidad puede ser de ayuda para asegurarte de que lo obtienes correctamente.

[0] https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/

Respuesta #3

Para cualquiera que llegue aquí después de una odisea aparentemente interminable de frustración y confusión:

Importante:
Las importaciones de paquetes de Node solo son compatibles cuando moduleResolution está configurado en "nodenext" o "node16".

  • tsconfig.json
{
  "compilerOptions": {
    //                  ∨
    "moduleResolution": "nodenext"
    // <... config>
  }
}

Si eso está configurado, funcionará sin problemas. Un ejemplo de cómo esto puede verse en un proyecto:

  • package.json
{
  "type": "module",
  "imports": {
    "#util.define": "./src/util/define.ts"
    // <... etc.>
  }
  // <... config>
}
  • main.ts
import * as define from '#utils.define'

class Main {
  static {
    define.symbol(Main.prototype, Symbol.toStringTag, "Main")
  }
  // <... etc.>
}

Artículos relacionados