javascript - Hur distribuerar man NodeJS-biblioteket skrivet i TypeScript för både BrowserJS och NodeJS med tillgänglig Webpacks tree shaking?
Liksom lodash föreslår biblioteket @yamato-daiwa/es-extensions de vanliga funktionerna för både BrowserJS och NodeJS, men till skillnad från lodash har det en enda huvudfil index.js med återexporter.
Krav
Det här biblioteket:
- Måste fungera i webbläsarmiljö med Webpack (uppnått ✔️)
- Måste göra Webpacks tree shaking tillgänglig (med andra ord, borttagningen av oanvänd funktionalitet vid produktionsbyggande) för BrowserJS där varje kilobyte räknas (nått ✔️).
- Måste fungera i NodeJS-miljö.
- Måste fungera med ts-node
Huvudproblem
Den motsägelsefulla punkten är modultyperna. För att göra Webpacks tree shaking tillgängligt krävs ES-moduler, men för närvarande stöder NodeJS endast CommonJS-moduler, och i ts-node är stödet för ES-moduler begränsat (särskilt vid export av bibliotek).
Det vi har är:
| Modultyper | BrowserJS (Webpack) | Trädskakning | NodeJS | ts-node |
|---|---|---|---|---|
| CommonJS | Ja | Nej | Ja | Ja |
| ES20XX | Ja | Ja | Nej | Begränsad |
Eftersom tree shaking är avgörande för BroswerJS har det redan beslutats att distribuera biblioteket via ES2020-moduler. Men på detta sätt kommer stödet för NodeJS och ts-node att gå förlorat.
Även om man bygger NodeJS-applikationen med Webpack där det inte rekommenderas att bunta NodeJS-biblioteken (webpack node modules externals används för att exkludera dem), kommer applikationen att krascha om man inte lägger till @yamato-daiwa/es-extensions med dess ES-moduler till exkluderingen av webpack node modules externals.
Repro
I detta exempel kommer npm run "Webpack:ProductionBuild" att bygga filerna BrowserJS.js och NodeJS.js för respektive miljö. Eftersom källkoden endast använder isUndefined-funktionen från biblioteket "@yamato-daiwa/es-extensions", får det inte finnas någon annan funktionalitet i BrowserJS.js (Webpacks tree shaking):
(()=>{"use strict";console.log(!1),console.log("End of the script")})();
Tja, det var den avklarade utmaningen. Ämnet för denna fråga ärNodeJS.js:
(()=>{"use strict";const e=require("@yamato-daiwa/es-extensions");console.log((0,e.isUndefined)("ALPHA")),console.log("End of the script")})();
Om vi försöker köra det kommer vi att få felet:
C:\Users\***\WebpackDoesNotCuttOfTheUnusedFunctionality\ConsumingProject\node_modules\@yamato-dai
wa\es-extensions\Distributable\index.js:6
export { default as formatNumberWith4KetaKanji } from "./Numbers/formatNumberWith4KetaKanji";
Nu låt oss försöka köra det via ts-node (npm run "TSNode test"):
> ts-node index.ts
(node:18148) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
C:\Users\***\WebpackDoesNotCuttOfTheUnusedFunctionality\ConsumingProject\index.ts:1
import { isUndefined } from "@yamato-daiwa/es-extensions";
Betyder det "du måste byta bibliotek"?
Anteckning
Den här frågan har ställts tidigare före TypeScript 4.5-releasen. Kanske kommer dess nya funktionalitet att lösa dessa problem?
Svar #1
Ingången "main" i en package.json-fil bör alltid vara i commonjs-format. Ingången "module" bör alltid vara es-moduler. Just nu pekar du "main" på es-moduler, vilket inte kommer att lösas korrekt (till exempel ditt ts-node-fel).
Generellt, om du vill ge konsumenterna valet, skulle du skapa 2 versioner i distributionspaketet.
(du måste ta bort kommentarerna i dessa json-filer)
// tsconfig-esm.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "ES2020",
// es module build target
"outDir": "Distributable/esm"
}
}
// tsconfig-cjs.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
// commonjs build target
"outDir": "Distributable/cjs",
// the other one will build types, so not needed here
"declaration": false
}
}
Sedan, i din package.json:
"main": "./Distributable/cjs/index.js",
"module": "./Distributable/esm/index.js",
"types": "./Distributable/esm/index.d.ts",
"scripts": {
"build": "rimraf Distributable && tsc -p tsconfig-cjs.json && tsc -p tsconfig-esm.json",
"Lint": "eslint Source Tests"
},
"files": [
"Distributable"
],
"devDependencies": {
"rimraf": "^3.0.2",
Notera:
- du kan kalla dina skript vad du vill, men
buildär idiomatiskt. Mellanslagen skulle göra mig galen, eftersomnpm run Rebuild distributableinte skulle fungera. Du måste antingen använda citattecken somnpm run 'Rebuild distributable'eller undvika tecken somnpm run Rebuild\ distributable - om du har en
"files"-post ipackage.jsonbehöver du inte längre.npmignore. Detta innebär "pacakga bara de angivna filerna", vilket är mycket säkrare än att behöva komma ihåg att lägga till varje ny fil i en ignorera-lista. - Jag lade till
rimraf-beroendet, vilket är ett vanligt verktyg för att ta bort dist-katalogen på ett plattformsoberoende sätt.del-clifungerar endast för Windows.
