使用Typescript和ES模块发布Node模块的方法

本文主要介绍了使用Typescript和ES模块发布Node模块的方法,分享给大家,具体如下:

TypeScript已经成为一种非常流行的JavaScript语言,这是有原因的。它的类型系统和编译器能够在您的软件运行之前的编译时捕获各种bug,并且附加的代码编辑器功能使它成为一个非常适合开发人员的高效环境。

但是,当你想用TypeScript编写一个库或包,同时又想用JavaScript来发布,这样你的最终用户就不必手动编译你的代码,会发生什么?我们如何使用现代的JavaScript功能(如ES模块)来编写,同时又能获得TypeScript的所有好处?

本文旨在解决所有这些问题,并为你提供一个设置,使你可以放心地编写和共享TypeScript库,并为包装的使用者提供轻松的体验。

入门

我们要做的第一件事是建立一个新项目。在本教程中,我们将创建一个基本的数学程序包——不是一个服务于任何实际目的的程序包——因为它将让我们演示所有我们需要的TypeScript,而不会偏离程序包的实际功能。

首先,创建一个空目录并运行 npm init -y 创建一个新项目。这将创建你的 package.json 并为你提供一个空项目以供处理:

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

现在,我们可以添加第一个也是最重要的依赖项:TypeScript!

$ npm install --save-dev typescript

安装TypeScript后,可以通过运行 tsc --init 初始化TypeScript项目。 tsc 是“ TypeScript编译器”的缩写,是TypeScript的命令行工具。

为确保你运行我们刚刚在本地安装的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模块的现代版本,以便捆绑工具可以巧妙地将未使用的代码tree–shake ,因此支持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 文件,如下所示:

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

因此,现在当用户使用我们的模块时,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输出你可能需要的多种格式,而不需要太多麻烦。

到此这篇关于使用Typescript和ES模块发布Node模块的方法的文章就介绍到这了,更多相关Typescript和ES发布Node模块内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解使用Typescript开发node.js项目(简单的环境配置)

    最近在学习typescript的过程中,想到也许可以使用ts来开发node.js项目.在网上搜了一下,其实已经有很多开发者实践了这方面的内容.这里,我记录一下自己搭建开发环境的简单过程. 使用Typescript开发的好处: 较严格的类型检查和语法检查. 对ES6/ES2015/ES7(部分)支持比较好. 编译后的js文件很干净,也支持多种代码规范. 其他,请参见文档. 准备 node.js v6.9.1 或者任意的新版本,老版本暂时没有试验. tsc typescript编译器,使用npm安装

  • 浅谈TypeScript 用 Webpack/ts-node 运行的配置记录

    公司项目代码是用 TypeScript 写的, 中间遇到有些代码不要放到 Node 里面去跑. 具体场景一些路由配置, 比较大的一块 JSON 数据定义在 TypeScript 里. 我另外有增加脚本, 基于这些 JSON 数据用来生成切换路由的函数. 这就需要运行 TypeScript 了, 而且可能包含一些额外的业务代码. 首先 Node 运行 TypeScript 有提供 ts-node 用来处理. ts-node 会先编译 TypeScript 代码到 JavaScript, 再调用 N

  • 手把手教你使用TypeScript开发Node.js应用

    为什么要使用TypeScript? 为了减少代码编写过程中出现的错误,以及更好的维护你的项目,本文将手把手教你配置一个简单的开发环境来编写Node.js的应用程序,创建这样的一个开发环境有很多方式,这只是其中一种,希望对你有所帮助! 手把手教你使用TypeScript开发Node.js应用 首先配置package.json 因为要在项目中使用Webpack,所以首先得创建一个package.json文件,我们可以使用npm init来生成 { "name": "start&q

  • typescript nodejs 依赖注入实现方法代码详解

    依赖注入通常也是我们所说的ioc模式,今天分享的是用typescript语言实现的ioc模式,这边用到的主要组件是 reflect-metadata 这个组件可以获取或者设置元数据信息,它的作用是拿到原数据后进行对象创建类似C#中的反射,先看第一段代码: import "reflect-metadata"; /** * 对象管理器 */ const _partialContainer = new Map<string, any>(); const PARAMTYPES =

  • TypeScript开发Node.js程序的方法

    当我第一次发现 TypeScript 时,就把它用到了自己的 JavaScript 程序中.使用 TypeScript 有很多好处,现在你要让我在用原生 JavaScript 写任何东西的话,需要给我一个令人信服的理由. 在本文中,我将向你展示如何设置一个简单的开发环境,以便使用 TypeScript 编写 Node.js 应用程序. 首先在 TypeScript 中可能有一千种或更多种不同的方法去创建 Node.js 程序.我只是想展示自己喜欢的方式. 另外你可以在此处找到我的入门项目:htt

  • 使用Typescript和ES模块发布Node模块的方法

    本文主要介绍了使用Typescript和ES模块发布Node模块的方法,分享给大家,具体如下: TypeScript已经成为一种非常流行的JavaScript语言,这是有原因的.它的类型系统和编译器能够在您的软件运行之前的编译时捕获各种bug,并且附加的代码编辑器功能使它成为一个非常适合开发人员的高效环境. 但是,当你想用TypeScript编写一个库或包,同时又想用JavaScript来发布,这样你的最终用户就不必手动编译你的代码,会发生什么?我们如何使用现代的JavaScript功能(如ES

  • node模块机制与异步处理详解

    1.模块机制 commonJS模块机制出现的目的是为了构建js在web服务器,桌面程序,浏览器等方面形成生态系统.而node js就是这种规范的一种实现,用requird来引入其他文件,同样,npm也遵循了commonJS定义的包规范,从而形成了一套完整的生态系统. 模块定义并导出 例如有如下一个名为circle.js的文件 exports.getName = function(name) { return name } 模块载入 var circle = require('/circle.js

  • Node 模块原理与用法详解

    本文实例讲述了Node 模块原理与用法.分享给大家供大家参考,具体如下: 简介 V8引擎本身就是用于Chrome浏览器的JS解释部分,但是Ryan Dahl,把V8搬到服务器,用于做服务器的软件. Node是一个专注于实现高性能Web服务器优化的专家,在遇到V8而诞生的项目 没有历史包袱,没有同步I/O.不会出现一个同步I/O导致事件循环性能急剧降低的情况. V8性能足够好,远远比Python,Ruby等其它脚本语言的引擎快. JavaScript语言的闭包特性非常方便,比C中的回调函数好用.

  • node.js中http模块和url模块的简单介绍

    前言 本文主要给大家介绍了关于node.js中http模块与url模块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 一.http模块的简单介绍 node.js当中的http内置模块可以用于创建http服务器与http客户端. 1.引包 const http = require('http'); 2.创建http服务器 var server = http.createServer((req,res)=>{ }); 使用http的.createServer()方法可以

  • 详解Node.js模块间共享数据库连接的方法

    这个标题本身就是一个命题,因为使用默认方式的情况下,一个 Node.js 应用里的各个模块都是共享的同一个数据库连接.但是如果姿势不对,可能会很丑陋,甚至可能会出错. 你可以忽略下面这部分,直接切入正题. 背景 最近在做专业课程设计,题目是"机票预订管理系统".需求比较简单,就试着拿最近在学的 Node.js 来做了.本来还在调研用何种 Node.js 框架比较合适,看了几个框架之后发现这是杀鸡用牛刀,有看文档查资料的时间还不如直接动手写了.最后写完我会把代码放到 Github 上,欢

  • Node.js中HTTP模块与事件模块详解

    Node.js的http服务器 通过使用HTTP模块的低级API,Node.js允许我们创建服务器和客户端.刚开始学node的时候,我们都会遇到如下代码: 复制代码 代码如下: var http = require('http'); http.createServer(function (req,res) {     res.end('Hello World\n'); }).listen(3000,"127.0.0.1"); console.log("Server funni

  • 深入理解Commonjs规范及Node模块实现

    前面的话 Node在实现中并非完全按照CommonJS规范实现,而是对模块规范进行了一定的取舍,同时也增加了少许自身需要的特性.本文将详细介绍NodeJS的模块实现 引入 nodejs是区别于javascript的,在javascript中的顶层对象是window,而在node中的顶层对象是global [注意]实际上,javascript也存在global对象,只是其并不对外访问,而使用window对象指向global对象而已 在javascript中,通过var a = 100:是可以通过w

  • 浅谈Node模块系统及其模式

    模块是构建应用程序的基础,也使得函数和变量私有化,不直接对外暴露出来,接下来我们就要介绍Node的模块化系统和它最常用的模式 为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统. 模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的.换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码.JSON 或者编译过的C/C++ 扩展. module的本质 我们都知道,JavaScript有一个很大的缺陷就是缺少namespa

  • 浅谈node模块与npm包管理工具

    在Node.js中,以模块为单位划分所有的功能,并且提供了一个完整的模块加载机制,所以我们可以将应用程序划分为各个不同的部分,并且对这些部分进行很好的协同管理.通过将各种可重用代码编写在各种模块中的方法,可以大大减少应用程序的代码量,提高应用程序的开发效率以及应用程序代码的可读性.通过模块加载机制,可以将各种第三方模块引入到我们的应用程序中. 在node.js中,提供npm包管理工具,用于从第三方网站上下载各种Node.js包. 一.模块 1.1 加载模块 在Node.js中,以模块为单位划分所

  • Node.js readline模块与util模块的使用

    1. 使用readline模块逐行读取流数据 1.1. 创建Interface对象 在readline模块中,通过Interface对象的使用来实现逐行读取流数据的处理.因此首先要创建Interface对象,在readline模块中,可以通过createInterface方法来创建Interface对象.readline.createInterface(options),options为一个对象,属性如下 input: 属性值为一个可用来读取流数据的对象,用于指定读入数据的来源. output:

随机推荐