使用Typescript和ES模塊發佈Node模塊

TypeScript已經成爲一種很是流行的JavaScript語言,這是有緣由的。它的類型系統和編譯器可以在您的軟件運行以前的編譯時捕獲各類bug,而且附加的代碼編輯器功能使它成爲一個很是適合開發人員的高效環境。javascript

可是,當你想用TypeScript編寫一個庫或包,同時又想用JavaScript來發布,這樣你的最終用戶就沒必要手動編譯你的代碼,會發生什麼?咱們如何使用現代的JavaScript功能(如ES模塊)來編寫,同時又能得到TypeScript的全部好處?java

本文旨在解決全部這些問題,併爲你提供一個設置,使你能夠放心地編寫和共享TypeScript庫,併爲包裝的使用者提供輕鬆的體驗。node

獲取本文完整代碼:公衆號中回覆關鍵字: typescript-es-modules

目錄webpack

[toc]web


入門

咱們要作的第一件事是創建一個新項目。在本教程中,咱們將建立一個基本的數學程序包——不是一個服務於任何實際目的的程序包——由於它將讓咱們演示全部咱們須要的TypeScript,而不會偏離程序包的實際功能。typescript

首先,建立一個空目錄並運行 npm init -y 建立一個新項目。這將建立你的 package.json 併爲你提供一個空項目以供處理:shell

$ mkdir maths-package
$ cd maths-package
$ npm init -y

如今,咱們能夠添加第一個也是最重要的依賴項:TypeScript!npm

$ npm install --save-dev typescript

安裝TypeScript後,能夠經過運行 tsc --init 初始化TypeScript項目。 tsc 是「 TypeScript編譯器」的縮寫,是TypeScript的命令行工具。json

爲確保你運行咱們剛剛在本地安裝的TypeScript編譯器,應在命令前加上 npx。npx是個很棒的工具,它將在node_modules 文件夾中查找你提供的命令,所以,經過在命令前面加上前綴,能夠確保咱們使用的是本地版本,而不是你可能已安裝的TypeScript的任何其餘全局版本。瀏覽器

$ npx tsc --init

這將建立一個 tsconfig.json 文件,該文件負責配置咱們的TypeScript項目。您會看到該文件具備數百個選項,其中大多數選項已被註釋掉(TypeScript支持 tsconfig.json 文件中的註釋)。我已將文件縮減爲僅啓用的設置,以下所示:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  }
}

咱們須要對此配置進行一些更改,以使咱們可以使用ES模塊發佈程序包,所以,讓咱們如今來看一下這些選項。

配置tsconfig.json 選項

若是您正在尋找全部可能的 tsconfig 選項的完整列表,能夠在TypeScript網站上找到此方便的 參考

讓咱們從 target 開始,這定義了你將在瀏覽器中提供代碼的JavaScript支持級別。若是您必須使用一組較舊的瀏覽器,這些瀏覽器可能不具備全部最新和最強大的功能,則能夠將其設置爲 ES2015。若是您確實須要最大的瀏覽器覆蓋範圍,TypeScript甚至將支持 ES3

咱們將在此處針對該模塊使用 ES2015,但能夠隨時進行相應更改。例如,若是我爲本身創建一個快速的輔助項目,而且只關心尖端的瀏覽器,那麼我很高興將其設置爲 ES2020

選擇模塊系統

接下來,咱們必須決定將用於該項目的模塊系統。請注意,這不是咱們要編寫的模塊系統,而是TypeScript的編譯器在輸出代碼時將使用的模塊系統。

發佈模塊時我喜歡作的事情是發佈兩個版本:

  • 帶有ES模塊的現代版本,以便捆綁工具能夠巧妙地將未使用的代碼treeshake ,所以支持ES模塊的瀏覽器只需導入文件
  • 使用CommonJS模塊的版本(若是在Node中工做,你將習慣使用 require 代碼),所以較早的構建工具和Node.js環境能夠輕鬆運行該代碼

稍後咱們將介紹如何使用不一樣的選項捆綁兩次,可是如今,讓咱們將TypeScript配置爲輸出ES模塊。咱們能夠經過將 module 設置設置爲 ES2020 來實現。

如今,你的 tsconfig.json 文件應以下所示:

{
  "compilerOptions": {
    "target": "ES2015",
    "module": "ES2020",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  }
}

編寫一些代碼

在討論捆綁代碼以前,咱們須要寫一些代碼!讓咱們建立兩個小模塊,它們既導出函數,又爲導出全部代碼的模塊提供一個主 entry 文件。

我喜歡將全部TypeScript代碼放在 src 目錄中,由於這意味着咱們能夠直接將TypeScript編譯器指向它,所以,我將使用如下代碼建立 src/add.ts

export const add = (x: number, y:number):number => {
  return x + y;
}

我也將建立 src/subtract.ts

export const subtract = (x: number, y:number):number => {
  return x - y;
}

最後,src/index.ts 將導入咱們全部的API方法並再次導出它們:

import { add } from './add.js'
import { subtract } from './subtract.js'
export {
  add,
  subtract
}

這意味着,用戶能夠經過導入只須要的東西來獲取咱們的功能,也能夠經過獲取全部的東西來獲取。

import { add } from 'maths-package';

import * as MathsPackage from 'maths-package';

請注意,在 src/index.ts 中,個人導入包含文件擴展名。若是隻想支持Node.js和構建工具(例如webpack),則不須要這樣作,可是若是要支持支持ES模塊的瀏覽器,則須要文件擴展名。

使用TypeScript進行編譯

讓咱們看看是否可讓TypeScript編譯咱們的代碼。咱們須要先對 tsconfig.json 文件進行一些調整,而後才能執行如下操做:

{
  "compilerOptions": {
    "target": "ES2015",
    "module": "ES2020",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./lib",
  },
  "include": [
    "./src"
  ]
}

咱們進行了兩項更改:

  • compilerOptions.outDir ——這告訴TypeScript將咱們的代碼編譯到一個目錄中。在這種狀況下,我已經告訴它命名該目錄 lib,可是您能夠根據須要命名它。
  • include ——告訴TypeScript咱們但願在編譯過程當中包含哪些文件。在咱們的例子中,咱們全部的代碼都位於src 目錄中,所以我將其傳入。這就是爲何我喜歡將全部TS源文件保存在一個文件夾中的緣由,這使配置變得很是容易

讓咱們來試一試,看看會發生什麼吧! 我發如今調整個人TypeScript配置時,最適合個人方法是調整、編譯、檢查輸出,而後再調整。不要懼怕嘗試這些設置,看看它們如何影響最終結果。

要編譯TypeScript,咱們將運行 tsc 並使用 -p 標誌(「project」的縮寫)告訴它 tsconfig.json 的位置:

npx tsc -p tsconfig.json

若是你有任何類型錯誤或配置問題,將在此處顯示。若是沒有,您應該什麼也看不到——可是請注意,你有一個新的 lib 目錄,其中有文件!TypeScript編譯時不會將任何文件合併在一塊兒,而是將每一個模塊轉換成對應的JavaScript。

讓咱們看一下輸出的三個文件:

// lib/add.js
export const add = (x, y) => {
    return x + y;
};

// lib/subtract.js
export const subtract = (x, y) => {
    return x - y;
};

// lib/index.js
import { add } from './add.js';
import { subtract } from './subtract.js';
export { add, subtract };

它們看起來和咱們的輸入很是類似,但沒有咱們添加的類型註釋。這是能夠預期的:咱們在ES模塊中編寫了咱們的代碼,並告訴TypeScript也要以這種形式輸出。若是咱們使用了比ES2015更新的任何JavaScript功能,TypeScript會將它們轉換爲ES2015友好的語法,可是在咱們的案例中,咱們沒有使用它,所以TypeScript在很大程度上僅保留了全部內容。

該模塊如今能夠發佈到npm上供其餘用戶使用,可是咱們有兩個問題須要解決:

  • 咱們不會在代碼中發佈任何類型信息。這不會對咱們的用戶形成破壞,但這是一個錯過的機會:若是咱們也發佈咱們的類型信息,那麼使用支持TypeScript的編輯器的人或用TypeScript編寫應用程序的人將得到更好的體驗。
  • Node還不支持開箱即用的ES模塊。發佈CommonJS版本也很好,因此Node不須要額外的工做。ES模塊支持將出如今Node 13和更高的版本中,可是要遇上生態系統還須要一段時間。

發佈類型定義

咱們能夠經過要求TypeScript在寫代碼的同時發出一個聲明文件來解決類型信息問題。這個文件的結尾是 .d.ts,它將包含關於咱們代碼的類型信息。將它看做源代碼,除了不包含類型和實現以外,它只包含類型。

讓咱們在 tsconfig.json 中添加 "declaration": true(在 "compilerOptions" 部分中),而後再次運行 npx tsc -p tsconfig.json

提示:我想在個人 package.json 文件中添加一個腳原本進行編譯,所以無需輸入如下內容:

"scripts": {
    "tsc": "tsc -p tsconfig.json"
  }

而後我能夠運行 npm run tsc 來編譯個人代碼。

如今,您將看到每一個JavaScript文件(例如 add.js )旁邊都有一個等效的 add.d.ts 文件,以下所示:

// lib/add.d.ts
export declare const add: (x: number, y: number) => number;

所以,如今當用戶使用咱們的模塊時,TypeScript編譯器將可以選擇全部這些類型。

發佈到CommonJS

難題的最後一部分是還將TypeScript配置爲輸出使用CommonJS的代碼版本。爲此,咱們能夠製做兩個 tsconfig.json 文件,一個針對ES模塊,另外一個針對CommonJS。不過,咱們可讓CommonJS配置擴展咱們的默認設置並覆蓋 modules 設置,而不是複製全部配置。

讓咱們建立 tsconfig-cjs.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "CommonJS",
    "outDir": "./lib/cjs"
  },
}

重要的是第一行,這意味着此配置默認狀況下會繼承 tsconfig.json 的全部設置。這很重要,由於你不須要在多個JSON文件之間同步設置。

而後覆蓋須要更改的設置。我相應地更新模塊,而後將 outDir 設置更新到 lib/cjs ,這樣咱們就能夠輸出到lib 中的子文件夾。

此時,我還更新了 package.json 中的 tsc 腳本:

"scripts": {
  "tsc": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json"
}

如今,當咱們運行 npm run tsc 時,咱們將編譯兩次,而且咱們的lib目錄將以下所示:

這個有點亂,讓咱們經過更新 tsconfig 中的 outDir 選項來將ESM輸出更新到 lib/esm

接下來,咱們將設置 module 屬性。這是應該連接到咱們軟件包的ES模塊版本的屬性。支持此功能的工具將可以使用此版本的軟件包。所以,應將其設置爲 ./lib/esm/index.js

接下來,咱們將 files entry 添加到 package.json 中。在這裏,咱們定義了發佈模塊時應包括的全部文件。我喜歡使用這種方法來明肯定義要在最終模塊中推送到npm的文件。

這樣咱們就能夠減少模塊的大小。例如,咱們不會發布 src 文件,而是發佈 lib 目錄。若是你在 files entry 中提供目錄,則默認狀況下會包含其全部文件和子目錄,所以你沒必要所有列出。

提示:若是要查看模塊中將包含哪些文件,請運行 npx pkgfiles 以得到列表。

如今,咱們的 package.json 中包含如下三個附加字段:

"main": "./lib/cjs/index.js",
  "module": "./lib/esm/index.js",
  "files": [
    "lib/"
  ],

還有最後一步。由於咱們要發佈 lib 目錄,因此須要確保在運行 npm publishlib 目錄是最新的。npm文檔中有一節是關於如何作到這一點的——咱們可使用 prepublishOnly 腳本。當咱們運行 npm publish 時,該腳本將自動爲咱們運行:

"scripts": {
  "tsc": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json",
  "prepublish": "npm run tsc"
},

注意,還有一個名爲 prepublish 的腳本,這使選擇哪一個稍微有些混亂。npm文檔提到了這一點:不推薦使用prepublish ,若是隻想在發佈時運行代碼,則應使用prepublishOnly

這樣,運行 npm publish 將運行咱們的TypeScript編譯器並在線發佈模塊!我將該軟件包發佈在 @ jackfranklin/maths-package-for-blog-post 下,雖然我不建議你使用它,可是你能夠瀏覽文件並查看。我還將全部代碼都上傳到了CodeSandbox中,所以您能夠根據須要下載或破解它。

結束

就是這樣!我但願這篇教程已經告訴你,使用TypeScript上手和運行TypeScript並不像最初看起來那麼困難,只要稍加調整,就可讓TypeScript輸出你可能須要的多種格式,而不須要太多麻煩。


subscribe2.png

相關文章
相關標籤/搜索