node.js - Waarom TypeScript CommonJS importeert in plaats van ESM
Ik heb deze handleiding gevolgd om een dual package te maken. Dit is hoe de structuur van het pakketbestand eruitziet:
my-awesome-lib
package.json
dist
|-- mjs
|-- package.json
|-- index.js
|-- cjs
|-- package.json
|-- index.js
In package.json:
"main": "dist/cjs/index.js",
"module": "dist/mjs/index.js",
"exports": {
".": {
"require": "./dist/cjs/index.js",
"import": "./dist/mjs/index.js"
}
},
Het package.json in de mjs-map:
{
"type": "module"
}
Het package.json in de cjs-map:
{
"type": "commonjs"
}
In mijn Typescript-toepassing heb ik mijn eigen pakket geïnstalleerd en geprobeerd het te importeren. Echter, ik merkte iets vreemds op:
// src/index.ts
import { Class } from 'my-awesome-lib'
import { Class } from '../node_modules/my-awesome-lib/dist/mjs/index.js';
De eerste importverklaring verwijst naar de ../node_modules/my-awesome-lib/dist/cjs/index.js in plaats van naar de mjs-map. De tweede importverklaring is wat ik eigenlijk wil.
Kan iemand me vertellen wat er mis is gegaan? Is het omdat ik mijn bibliotheek op de verkeerde manier heb gepubliceerd?
p.s.
Dit is mijn tsconfig.json
{
"compilerOptions": {
"target": "es2020",
"module": "es2020",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"moduleResolution": "node",
"outDir": "dist"
},
"include": ["src"]
}
p.s. het toevoegen van {"type": "module"} aan de package.json van de applicatie laat deze fout ook niet verdwijnen.
Antwoord #1
Alles werkt zoals het is ontworpen. Het probleem dat je ervaart is een probleem met wat je verwacht. Je "tsconfig.json" bestand is verkeerd geconfigureerd voor een ESM-module, maar je verwacht dat het de ESM-pakket laadt.
Het lijkt erop dat je node16-strictest-esm-combined als referentie gebruikt, en als dat zo is, kan ik je vertellen dat deze configuratie mij ongeveer een jaar geleden ook in de war bracht.
Ik vind de base/tsconfig-repo echt leuk, maar alleen voor CJS-configuraties. Ik weet niet waarom ze een tsconfig configureren die ESM in de naam heeft met "moduleResolution": "node", maar het gebruik van "node" als waarde geeft TypeScript aan om JavaScript te genereren dat wordt opgelost als een CommonJS-module.
Hier is de link naar de officiële moduleResolution documentatie
Het toont aan dat de onderstaande configuratie (die de configuratie is waarvan ik 100% zeker weet dat deze naar ESM transpileert) de juiste configuratie is om te gebruiken.
Dit komt rechtstreeks uit een van mijn projecten dat een "ESM"-pakket is.
{
"compilerOptions": {
"lib": ["ESNext"], // Why not use NodeNext if it compiles down anyways
"target": "ES2021",
// Runtime/Module
"module": "Node16", // <- ESM Module Type
"moduleResolution": "Node16", // <- ESM Resolution
"esModuleInterop": false, // I added this to show what not to do
// Project Structure
"rootDir": "src",
"outDir": "build",
"declarationDir": "types",
"strict": true,
"alwaysStrict": false, // <- you can set this to false if your writing ESM
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*.ts", "src/**/*.mts"], // You might want to change this
"exclude": ["build", "types", "node_modules"]
}
Je hebt 2 opties
NUMMER 1: Om ervoor te zorgen dat je project een ES-Module is: je moet je project opnieuw transpilen. Je hebt twee opties. Je kunt het op de manier doen die ik prefereer, wat de manier is die hieronder wordt getoond: met de build clean & force flags. Die extra flags zorgen ervoor dat alles wat niet nodig is wordt verwijderd, en alles wat nodig is opnieuw wordt getranspileerd, zelfs als het niet is aangepast. Dit is belangrijk, omdat je het type module aan het veranderen bent.
In "ESM" moeten je .js-bestanden worden uitgevoerd met imports die worden geïmporteerd met het import-trefwoord, in plaats van de "CJS" require()-methodeaanroepen. Omdat tsc probeert efficiënt te zijn, zal het de bestanden die require bevatten niet opnieuw compileren, omdat de overeenkomstige .ts-documenten niet zijn gewijzigd.
$ tsc -b --clean
$ tsc -b --force
NUMMER 2: De andere route die je kunt nemen is gewoon je build- en typemapdirectories te verwijderen, en vervolgens $ tsc uit te voeren. Deze manier werkt ook, maar vereist ofwel extra shell-commando's, of het klikken van de muis, terwijl de andere manier twee tsc-commando's is. Ook kan de eerste methode worden toegevoegd aan je package.json-scripts-eigenschap, en later snel weer worden aangeroepen met npm run. Je kunt ook TypeScript-taken maken voor de --build, --force en --clean flags.
Hoe dan ook...
Zorg ervoor dat zodra je je hele project opnieuw hebt getranspiled (inclusief alle bestanden), je JavaScript-bestanden de import & export sleutelwoorden gebruiken, en niet de requires("...") & module.exports eigenschap & methode. Alleen dan weet je dat je daadwerkelijk een ESM-module hebt.
Op dit punt zou de ESM-tak van je pakket moeten laden
Als dat niet het geval is, weet je dat het iets is met de configuratie van je pakketten, in dat geval kun je me een bericht sturen en kan ik kijken of ik verder kan helpen.
