詳解如何發(fā)布TypeScript編寫的npm包
前言
在這篇文章中,我們將使用TypeScript和Jest從頭開始構(gòu)建和發(fā)布一個(gè)NPM包。
我們將初始化一個(gè)項(xiàng)目,設(shè)置TypeScript,用Jest編寫測試,并將其發(fā)布到NPM。
項(xiàng)目
我們的庫稱為digx
。它允許從嵌套對象中根據(jù)路徑找出值,類似于lodash
中的get
函數(shù)。
比如說:
const source = { my: { nested: [1, 2, 3] } } digx(source, "my.nested[1]") //=> 2
就本文而言,只要它是簡潔的和可測試的,它做什么并不那么重要。
初始化項(xiàng)目
讓我們從創(chuàng)建空目錄并初始化它開始。
mkdir digx cd digx npm init --yes
npm init --yes
命令將為你創(chuàng)建package.json
文件,并填充一些默認(rèn)值。
讓我們也在同一文件夾中設(shè)置一個(gè)git
倉庫。
git init echo "node_modules" >> .gitignore echo "dist" >> .gitignore git add . git commit -m "initial"
構(gòu)建庫
這里會用到TypeScript,我們來安裝它。
npm i -D typescript
使用下面的配置創(chuàng)建tsconfig.json
文件:
{ "files": ["src/index.ts"], "compilerOptions": { "target": "es2015", "module": "es2015", "declaration": true, "outDir": "./dist", "noEmit": false, "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true } }
最重要的設(shè)置是這些:
庫的主文件會位于src
文件夾下,因此需要這么設(shè)置"files": ["src/index.ts"]
。
"target": "es2015"
確保我們的庫支持現(xiàn)代平臺,并且不會攜帶不必要的墊片。
"module": "es2015"
。我們的模塊將是一個(gè)標(biāo)準(zhǔn)的ES
模塊(默認(rèn)是CommonJS
)。ES
模式在現(xiàn)代瀏覽器下沒有任何問題;甚至Node從13版本開始就支持ES
模式。
"declaration": true
- 因?yàn)槲覀兿胍詣由?code>d.ts聲明文件。我們的TypeScript用戶將需要這些聲明文件。
其他大部分選項(xiàng)只是各種可選的TypeScript檢查,我更喜歡開啟這些檢查。
打開package.json
,更新scripts
的內(nèi)容:
"scripts": { "build": "tsc" }
現(xiàn)在我們可以用npm run build
來運(yùn)行構(gòu)建...這樣會失敗的,因?yàn)槲覀冞€沒有任何可以構(gòu)建的代碼。
我們從另一端開始。
添加測試
作為一名負(fù)責(zé)任的開發(fā),我們將從測試開始。我們將使用jest
,因?yàn)樗唵吻液糜谩?/p>
npm i -D jest @types/jest ts-jest
ts-jest
包是Jest理解TypeScript所需要的。另一個(gè)選擇是使用babel
,這將需要更多的配置和額外的模塊。我們就保持簡潔,采用ts-jest
。
使用如下命令初始化jest
配置文件:
./node_modules/.bin/jest --init
一路狂按回車鍵就行,默認(rèn)值就很好。
這會使用一些默認(rèn)選項(xiàng)創(chuàng)建jest.config.js
文件,并添加"test": "jest"
腳本到package.json
中。
打開jest.config.js
,找到以preset
開始的行,并更新為:
{ // ... preset: "ts-jest", // ... }
最后,創(chuàng)建src
目錄,以及測試文件src/digx.test.ts
,填入如下代碼:
import dg from "./index"; test("works with a shallow object", () => { expect(dg({ param: 1 }, "param")).toBe(1); }); test("works with a shallow array", () => { expect(dg([1, 2, 3], "[2]")).toBe(3); }); test("works with a shallow array when shouldThrow is true", () => { expect(dg([1, 2, 3], "[2]", true)).toBe(3); }); test("works with a nested object", () => { const source = { param: [{}, { test: "A" }] }; expect(dg(source, "param[1].test")).toBe("A"); }); test("returns undefined when source is null", () => { expect(dg(null, "param[1].test")).toBeUndefined(); }); test("returns undefined when path is wrong", () => { expect(dg({ param: [] }, "param[1].test")).toBeUndefined(); }); test("throws an exception when path is wrong and shouldThrow is true", () => { expect(() => dg({ param: [] }, "param[1].test", true)).toThrow(); }); test("works tranparently with Sets and Maps", () => { const source = new Map([ ["param", new Set()], ["innerSet", new Set([new Map(), new Map([["innerKey", "value"]])])], ]); expect(dg(source, "innerSet[1].innerKey")).toBe("value"); });
這些單元測試讓我們對正在構(gòu)建的東西有一個(gè)直觀的了解。
我們的模塊導(dǎo)出一個(gè)單一函數(shù),digx
。它接收任意對象,字符串參數(shù)path
,以及可選參數(shù)shouldThrow
,該參數(shù)使得提供的路徑在源對象的嵌套結(jié)構(gòu)中不被允許時(shí),拋出一個(gè)異常。
嵌套結(jié)構(gòu)可以是對象和數(shù)組,也可以是Map和Set。
使用npm t
運(yùn)行測試,當(dāng)然,不出意外會失敗。
現(xiàn)在打開src/index.ts
文件,并寫入下面內(nèi)容:
export default dig; /** * A dig function that takes any object with a nested structure and a path, * and returns the value under that path or undefined when no value is found. * * @param {any} source - A nested objects. * @param {string} path - A path string, for example `my[1].test.field` * @param {boolean} [shouldThrow=false] - Optionally throw an exception when nothing found * */ function dig(source: any, path: string, shouldThrow: boolean = false) { if (source === null || source === undefined) { return undefined; } // split path: "param[3].test" => ["param", 3, "test"] const parts = splitPath(path); return parts.reduce((acc, el) => { if (acc === undefined) { if (shouldThrow) { throw new Error(`Could not dig the value using path: ${path}`); } else { return undefined; } } if (isNum(el)) { // array getter [3] const arrIndex = parseInt(el); if (acc instanceof Set) { return Array.from(acc)[arrIndex]; } else { return acc[arrIndex]; } } else { // object getter if (acc instanceof Map) { return acc.get(el); } else { return acc[el]; } } }, source); } const ALL_DIGITS_REGEX = /^\d+$/; function isNum(str: string) { return str.match(ALL_DIGITS_REGEX); } const PATH_SPLIT_REGEX = /\.|\]|\[/; function splitPath(str: string) { return ( str .split(PATH_SPLIT_REGEX) // remove empty strings .filter((x) => !!x) ); }
這個(gè)實(shí)現(xiàn)可以更好,但對我們來說重要的是,現(xiàn)在測試通過了。自己用npm t
試試吧。
現(xiàn)在,如果運(yùn)行npm run build
,可以看到dist
目錄下會有兩個(gè)文件,index.js
和index.d.ts
。
接下來就來發(fā)布吧。
發(fā)布
如果你還沒有在npm上注冊,就先注冊。
注冊成功后,通過你的終端用npm login
登錄。
我們離發(fā)布我們的新包只有一步之遙。不過,還有幾件事情需要處理。
首先,確保我們的package.json
中擁有正確的元數(shù)據(jù)。
- 確保
main
屬性設(shè)置為打包的文件"main": "dist/index.js"
。 - 為TypeScript用戶添加
"types": "dist/index.d.ts"
。 - 因?yàn)槲覀兊膸鞎鳛镋S Module被使用,因此需要指定
"type": "module"
。 name
和description
也應(yīng)填寫。
接著,我們應(yīng)該處理好我們希望發(fā)布的文件。我不覺得要發(fā)布任何配置文件,也不覺得要發(fā)布源文件和測試文件。
我們可以做的一件事是使用.npmignore
,列出所有我們不想發(fā)布的文件。我更希望有一個(gè)"白名單",所以讓我們使用package.json
中的files
字段來指定我們想要包含的文件。
{ // ... "files": ["dist", "LICENSE", "README.md", "package.json"], // ... }
終于,我們已經(jīng)準(zhǔn)備好發(fā)包了。
運(yùn)行以下命令:
npm publish --dry-run
并確保只包括所需的文件。當(dāng)一切準(zhǔn)備就緒時(shí),就可以運(yùn)行:
npm publish
測試一下
讓我們創(chuàng)建一個(gè)全新的項(xiàng)目并安裝我們的模塊。
npm install --save digx
現(xiàn)在,讓我們寫一個(gè)簡單的程序來測試它。
import dg from "digx" console.log(dg({ test: [1, 2, 3] }, "test[0]"))
結(jié)果非常棒!
然后運(yùn)行node index.js
,你會看到屏幕上打印1
。
總結(jié)
我們從頭開始創(chuàng)建并發(fā)布了一個(gè)簡單的npm包。
我們的庫提供了一個(gè)ESM模塊,TypeScript的類型,使用jest
覆蓋測試用例。
你可能會認(rèn)為,這其實(shí)一點(diǎn)都不難,的確如此。
原文鏈接:www.strictmode.io/articles/bu…
以上就是詳解如何發(fā)布TypeScript編寫的npm包的詳細(xì)內(nèi)容,更多關(guān)于TypeScript npm包發(fā)布的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序中button組件的邊框設(shè)置的實(shí)例詳解
這篇文章主要介紹了微信小程序中button組件的邊框設(shè)置的實(shí)例詳解的相關(guān)資料,希望通過本文大家能夠掌握這部分內(nèi)容,需要的朋友可以參考下2017-09-09前端項(xiàng)目中監(jiān)聽localStorage的變化
這篇文章主要為大家介紹了前端項(xiàng)目中監(jiān)聽localStorage的變化的解決思路詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06dotenv源碼解讀從.env文件中讀取環(huán)境變量
這篇文章主要為大家介紹了dotenv源碼解讀從.env文件中讀取環(huán)境變量示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12