详解如何发布TypeScript编写的npm包
前言
在这篇文章中,我们将使用TypeScript和Jest从头开始构建和发布一个NPM包。
我们将初始化一个项目,设置TypeScript,用Jest编写测试,并将其发布到NPM。
项目
我们的库称为digx
。它允许从嵌套对象中根据路径找出值,类似于lodash
中的get
函数。
比如说:
const source = { my: { nested: [1, 2, 3] } } digx(source, "my.nested[1]") //=> 2
就本文而言,只要它是简洁的和可测试的,它做什么并不那么重要。
npm包可以在这里找到。GitHub仓库地址在这里。
初始化项目
让我们从创建空目录并初始化它开始。
mkdir digx cd digx npm init --yes
npm init --yes
命令将为你创建package.json
文件,并填充一些默认值。
让我们也在同一文件夹中设置一个git
仓库。
git init echo "node_modules" >> .gitignore echo "dist" >> .gitignore git add . git commit -m "initial"
构建库
这里会用到TypeScript,我们来安装它。
npm i -D typescript
使用下面的配置创建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 } }
最重要的设置是这些:
库的主文件会位于src
文件夹下,因此需要这么设置"files": ["src/index.ts"]
。
"target": "es2015"
确保我们的库支持现代平台,并且不会携带不必要的垫片。
"module": "es2015"
。我们的模块将是一个标准的ES
模块(默认是CommonJS
)。ES
模式在现代浏览器下没有任何问题;甚至Node从13版本开始就支持ES
模式。
"declaration": true
- 因为我们想要自动生成d.ts
声明文件。我们的TypeScript用户将需要这些声明文件。
其他大部分选项只是各种可选的TypeScript检查,我更喜欢开启这些检查。
打开package.json
,更新scripts
的内容:
"scripts": { "build": "tsc" }
现在我们可以用npm run build
来运行构建...这样会失败的,因为我们还没有任何可以构建的代码。
我们从另一端开始。
添加测试
作为一名负责任的开发,我们将从测试开始。我们将使用jest
,因为它简单且好用。
npm i -D jest @types/jest ts-jest
ts-jest
包是Jest理解TypeScript所需要的。另一个选择是使用babel
,这将需要更多的配置和额外的模块。我们就保持简洁,采用ts-jest
。
使用如下命令初始化jest
配置文件:
./node_modules/.bin/jest --init
一路狂按回车键就行,默认值就很好。
这会使用一些默认选项创建jest.config.js
文件,并添加"test": "jest"
脚本到package.json
中。
打开jest.config.js
,找到以preset
开始的行,并更新为:
{ // ... preset: "ts-jest", // ... }
最后,创建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"); });
这些单元测试让我们对正在构建的东西有一个直观的了解。
我们的模块导出一个单一函数,digx
。它接收任意对象,字符串参数path
,以及可选参数shouldThrow
,该参数使得提供的路径在源对象的嵌套结构中不被允许时,抛出一个异常。
嵌套结构可以是对象和数组,也可以是Map和Set。
使用npm t
运行测试,当然,不出意外会失败。
现在打开src/index.ts
文件,并写入下面内容:
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) ); }
这个实现可以更好,但对我们来说重要的是,现在测试通过了。自己用npm t
试试吧。
现在,如果运行npm run build
,可以看到dist
目录下会有两个文件,index.js
和index.d.ts
。
接下来就来发布吧。
发布
如果你还没有在npm上注册,就先注册。
注册成功后,通过你的终端用npm login
登录。
我们离发布我们的新包只有一步之遥。不过,还有几件事情需要处理。
首先,确保我们的package.json
中拥有正确的元数据。
- 确保
main
属性设置为打包的文件"main": "dist/index.js"
。 - 为TypeScript用户添加
"types": "dist/index.d.ts"
。 - 因为我们的库会作为ES Module被使用,因此需要指定
"type": "module"
。 name
和description
也应填写。
接着,我们应该处理好我们希望发布的文件。我不觉得要发布任何配置文件,也不觉得要发布源文件和测试文件。
我们可以做的一件事是使用.npmignore
,列出所有我们不想发布的文件。我更希望有一个"白名单",所以让我们使用package.json
中的files
字段来指定我们想要包含的文件。
{ // ... "files": ["dist", "LICENSE", "README.md", "package.json"], // ... }
终于,我们已经准备好发包了。
运行以下命令:
npm publish --dry-run
并确保只包括所需的文件。当一切准备就绪时,就可以运行:
npm publish
测试一下
让我们创建一个全新的项目并安装我们的模块。
npm install --save digx
现在,让我们写一个简单的程序来测试它。
import dg from "digx" console.log(dg({ test: [1, 2, 3] }, "test[0]"))
结果非常棒!
然后运行node index.js
,你会看到屏幕上打印1
。
总结
我们从头开始创建并发布了一个简单的npm包。
我们的库提供了一个ESM模块,TypeScript的类型,使用jest
覆盖测试用例。
你可能会认为,这其实一点都不难,的确如此。
以上就是详解如何发布TypeScript编写的npm包的详细内容,更多关于TypeScript npm包发布的资料请关注猪先飞其它相关文章!
原文出处:https://juejin.cn/post/7171814858629840926
相关文章
- PHP+Ajax有许多的功能都会用到它小编今天就有使用PHP+Ajax实现的一个微信登录功能了,下面我们来看一个PHP+Ajax手机发红包的程序例子,具体如下所示。 PHP发红包基本...2016-11-25
- 这篇文章主要介绍了详解如何将c语言文件打包成exe可执行程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-25
- 这篇文章主要介绍了解决idea2020 maven无法自动导包的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-13
- 这篇文章主要给大家分享的是TypeScript 索引签名的理解,索引签名由方括号中的索引名称及其类型组成,后面是冒号和值类型:{ [indexName: KeyType]: ValueType }, KeyType 可以是一个 string、number 或 symbol,而ValueType 可以是任何类型,下面就俩简单了解一下吧...2021-10-15
将c#编写的程序打包成应用程序的实现步骤分享(安装,卸载) 图文
时常会写用c#一些程序,但如何将他们和photoshop一样的大型软件打成一个压缩包,以便于发布....2020-06-25- 这篇文章主要介绍了Pyinstaller打包文件太大的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-09
Postman安装与使用详细教程 附postman离线安装包
这篇文章主要介绍了Postman安装与使用详细教程 附postman离线安装包,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-05- 这篇文章主要介绍了PHP实现创建以太坊钱包转账等功能,对以太坊感兴趣的同学,可以参考下...2021-04-20
- 本文主要介绍了手把手教你uniapp和小程序分包,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-02
- 这篇文章主要介绍了vue cli4.0项目引入typescript的方法,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-07-17
- 这篇文章主要介绍了C# 实现抓包的方法,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-08-04
- 将外部作用域中的局部变量封闭起来的函数对象称为闭包(Closure),被封闭起来的变量与封闭它的函数对象有相同的生命周期,这在JavaScript中比较难理解而且十分重要,接下来带大家一起来全面理解JavaScript中的闭包:...2016-05-14
- 这篇文章主要介绍了使用TS来编写express服务器的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-30
详解Vue3.0 + TypeScript + Vite初体验
这篇文章主要介绍了详解Vue3.0 + TypeScript + Vite初体验,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-22- 这篇文章主要介绍了TypeScript魔法堂之枚举的超实用手册,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-29
- 这篇文章主要介绍了改变R语言默认存储包的路径操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-06
- 这篇文章主要介绍了你不知道的 TypeScript 高级类型(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-08-29
intellij idea如何将web项目打成war包的实现
这篇文章主要介绍了intellij idea如何将web项目打成war包的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-04- 在本篇文章里小编给大家分享的是一篇关于R语言“包”的知识点总结内容,有兴趣的朋友们可以学习下。...2021-05-06
- 这篇文章主要介绍了python打包生成so文件的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-30