乐趣区

关于前端:创建现代npm包的最佳实践

本文首发于微信公众号:大迁世界, 我的微信:qq449245884,我会第一工夫和你分享前端行业趋势,学习路径等等。
更多开源作品请看 GitHub https://github.com/qq449245884/xiaozhi,蕴含一线大厂面试残缺考点、材料以及我的系列文章。

技术始终在变动,咱们的流程和做法也须要跟上这些变动。因而,尽管 npm 曾经有 12 年的历史了,但围绕 npm 包创立的做法应该更古代。

在这节课中,咱们应用古代最佳实际(截至 2022 年)一步一步地创立一个 npm 包。首先学习如何创立一个 npm 包,这样你就能够相熟构建和公布一个包到 npm 注册表。

而后,再学习如何通过建设测试框架、继续集成和部署管道、安全检查以及公布的主动语义版本治理,来制作一个更强壮、可用于生产的 npm 包。

简略的 npm 包示例

咱们先通过一个简略的例子来相熟创立和公布 npm 包的过程。

创立我的项目

  1. 创立一个 GitHub 仓库: https://github.com/new
  2. 克隆本地的 repo。
    例如:git clone https://github.com/snyk-labs/simple-npm-package.git
  3. 关上你的终端, 进入到克隆的我的项目文件夹。
    例如:cd simple-npm-package
  4. 运行 npm init -y 来创立 package.json 文件。留神:如果克隆了示例仓库,就不须要做这一步。
  5. 在 package.json 取一个名称,对应 name 字段
  6. 为该包编写你的代码

创立 npm 账户

为了可能让咱们的 npm 包供别人应用,须要一个 npm 账户。

  • 通过 https://www.npmjs.com/signup 注册
  • 为了进步安全性,请在您的 npm 账户上启用 2FA:https://docs.npmjs.com/configuring-two-factor-authentication
  • 应用 npm login 命令在终端中用你的 npm 账户登录,并依照屏幕上的批示操作。
> npm login
npm notice Log in on https://registry.npmjs.org/
Username: clarkio
Password:
Email: (this IS public) <email address>
npm notice Please use the one-time password (OTP) from your authenticator application
Enter one-time password from our authenticator app: <OTP>
Logged in as clarkio on https://registry.npmjs.org/.

如何公布 npm 包

一旦你有了一个 npm 我的项目和一个 npm 账户,你就能够把你的 npm 包公布到公开的官网 npmjs 注册表上,让其他人能够应用。以下是你要遵循的步骤,在执行之前查看将公布的内容,而后运行理论的公布过程。

  1. 在终端,运行 npx npm-packlist 来查看将被蕴含在公布版本的软件包中的内容。

这能够确保咱们没有脱漏任何源代码文件,这些文件是软件包失常运行所须要的。这也是一个好的做法,以确保咱们不会意外地将敏感信息泄露给公众,如带有数据库凭证或 API 密钥的本地配置文件。

> npx npm-packlist
LICENSE
index.js
package.json
README.md

在终端,运行npm publish --dry-run,看看理论运行命令时将会做什么。

> npm publish --dry-run
npm notice
npm notice 📦@clarkio/simple-npm-package@0.0.1
npm notice === Tarball Contents ===
npm notice 1.1kB LICENSE
npm notice 1.2kB README.md
npm notice 95B index.js
npm notice 690B package.json
npm notice === Tarball Details===
npm notice name: @clarkio/simple-npm-package
npm notice version: 0.0.1
npm notice filename:@clarkio/simple-npm-package-0.0.1.tgz
npm notice package size:1.7 kB
npm notice unpacked size: 3.1 kB
npm notice shasum:40ede3ed630fa8857c0c9b8d4c81664374aa811c
npm notice integrity:sha512-QZCyWZTspkcUXL... ]L60ZKBOOBRLTg==
npm notice total files:4
npm notice
+ @clarkio/simple-npm-package@0.0.1
  1. 在终端,运行 npm publish --access=public 来公布软件包到 npm。

留神:–access=public对于作用哉内的包(@clarkio/modern-npm-package)是须要的,因为它们默认是公有的。如果它不是作用哉内的,并且在你的 package.json 中没有将private 字段设置为 true,它也将是公开的。

> npm publish --access=public
npm notice
npm notice 📦@clarkio/simple-npm-package@0.0.1
npm notice === Tarball Contents ===
npm notice 1.1kB LICENSE
npm notice 1.2kB README.md
npm notice 95B index.js
npm notice 690B package.json
npm notice === Tarball Details===
npm notice name: @clarkio/simple-npm-package
npm notice version: 0.0.1
npm notice filename:@clarkio/simple-npm-package-0.0.1.tgz
npm notice package size:2.1 kB
npm notice unpacked size: 4.1 kB
npm notice shasum:6f335d6254ebb77a5a24ee729650052a69994594
npm notice integrity:sha512-VZ1K1eMFOKeJW[...]7ZjKFVAxLcpdQ==
npm notice total files:4
npm notice
This operation requires a one-time password.
Enter OTP: <OTP>
+ @clarkio/simple-npm-package@0.0.1

当初,咱们曾经实现了构建和部署本人的 npm 包。接下来,咱们来看一下如何制作一个更弱小的包,为生产环境做好筹备,并失去更宽泛的应用。

生产就绪的 npm 包

尽管后面的例子的包能够在生产中应用,但它波及到人工成本来放弃其长期的保护。应用工具和自动化以及适当的测试和安全检查将有助于最大限度地缩小放弃软件包顺利运行的总工作量。让咱们深刻理解一下这其中的内容。

  1. 构建 CommonJS(CJS)和 ECMAScript(ESM)模块
  2. 设置和编写单元测试
  3. 施行安全检查
  4. 实现版本治理和公布的自动化

构建 CommonJS(CJS)和 ECMAScript(ESM)模块

尽管 ECMAScript 模块格局当初在 Node.js 的 12+ 版本中被原生反对,但它还没有被社区宽泛采纳。为了面向未来并反对这两种格局,咱们来看下应用 TypeScript 怎么来配置。

首先,创立一个根本的 TypeScript 配置文件 tsconfig.base.json。这是通用的编译设置,无论你的指标是哪种模块格局,都能够应用。

{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true,
    "checkJs": true,
    "allowJs": true,
    "declaration": true,
    "declarationMap": true,
    "allowSyntheticDefaultImports": true
  },
  "files": ["../src/index.ts"]
}

而后为 CommonJS 格局创立一个 TypeScript 配置文件,命名为tsconfig.cjs.json

  • lib 属性向 TypeScript 指出它应该参考哪些类型。
  • target 属性向 TypeScript 指出要编译的我的项目代码的 JavaScript 版本。
  • module 属性向 TypeScript 指出在编译的我的项目代码时应该应用哪种 JavaScript 模块格局。
  • moduleResolution 属性帮忙 TypeScript 弄清 “import” 语句应该如何被提及。
  • outDirdeclarationDir 属性向 TypeScript 指出了将编译的代码和定义其中应用的类型的后果放在哪里。
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {"lib": ["ES6", "DOM"],
    "target": "ES6",
    "module": "CommonJS",
    "moduleResolution": "Node",
    "outDir": "../lib/cjs",
    "declarationDir": "../lib/cjs/types"
  }
}

之后,为 ECMAScript 格局创立一个 TypeScript 配置文件,命名为tsconfig.esm.json。这里的属性与你在 CommonJS 配置中看到的雷同,但当初针对古代 ECMAScript 模块格局作为其输入。

{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {"lib": ["ES2022", "DOM"],
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "NodeNext",
    "outDir": "../lib/esm",
    "declarationDir": "../lib/esm/types"
  }
}

更新 package.json 文件,减少一个 files 字段,指向 lib 文件夹,外面有 TypeScript 为你构建软件包的后果。

更新 package.json 文件中的 exports 字段,以定义如何依据应用的模块加载器(CJS vs. ESM)查找源文件。

"exports": {
    ".": {
      "import": {
        "types": "./lib/esm/types/index.d.ts",
        "default": "./lib/esm/index.mjs"
      },
      "require": {
        "types": "./lib/cjs/types/index.d.ts",
        "default": "./lib/cjs/index.js"
      }
    }
  },

更新 package.json 文件的 maintypes 字段,以指向软件包的 CJS 版本。这将作为一个默认的、后备的选项。

“types":"./lib/cjs/types/index.d.ts","main":"./lib/cjs/index.js",

package.json 文件中增加一个 files 字段,以表明当 npm 打包你的代码进行公布时,应该包含哪些文件。

"files": ["lib/**/*"],

通过 package.json 中的 scripts 字段创立命令,应用 tsc 并编译包的 CJS 和 ESM 格局,并生成 lib 文件。

clean 命令是用来删除过来构建的输入,并从一个洁净的中央开始。

build:esm命令开端的 mv lib/esm/index.js lib/esm/index.mjs 重命名了文件扩展名,这样 Node.js 模块加载器就晓得它是一个 ESM 模块。

prepack命令是 npm 在打包 npm 包筹备公布到注册表之前应用的。

    "clean": "rm -rf ./lib",
    "build": "npm run clean && npm run build:esm && npm run build:cjs",
    "build:esm": "tsc -p ./configs/tsconfig.esm.json && mv lib/esm/index.js lib/esm/index.mjs",
    "build:cjs": "tsc -p ./configs/tsconfig.cjs.json",
    "prepack": "npm run build"

当初能够在终端运行 npm run build,让 TypeScript 构建你的我的项目,为应用和公布做筹备

这就是应用 TypeScript 构建 npm 包所须要做的所有设置,它同时反对 CommonJS 和 ECMAScript 模块格局。

设置和增加测试

为了对代码的行为和后果有信念,咱们须要有一个测试过程。测试迫使在第一次创立代码时,在 happy-path 之外,以不同的形式思考代码的性能。举个例子,能够想方法突破一个函数,使它抛出一个谬误或产生一个非预期的后果。这样做将使你的应用程序更有弹性和可持续性,并确保在增加更多内容时不会呈现问题。

单元测试

要确保库以咱们想要的形式运行,须要针对代码编写测试。咱们须要一些工具来帮忙设置咱们我的项目来运行单元测试并显示后果。

这些工具有 Mocha.jsChai.jsts-node。Mocha.js 是一个测试运行器,Chai.js 是一个断言库,帮忙确定你是否从你的代码中失去你所冀望的后果,而 ts-node 帮忙咱们在 TypeScript 我的项目中应用这些工具。依照上面的步骤,为 npm 包设置和运行测试。

  • 在终端中应用以下命令装置开发者的依赖:
    npm i -D mocha @type/mocha chai @types/chai ts-node
  • 在我的项目的根目录下创立一个新文件 .mocharc.json,内容如下:

    {"extension": ["ts"],
      "spec": "./**/*.spec.ts",
      "require": "ts-node/register"
    }
  • 在我的项目的根目录下创立一个 tests 文件夹。
  • index.spec.ts 文件中写单元测试来测试 index.ts 中的代码。
  • package.json 文件的 scripts 局部增加一个 test 属性,给它一个 mocha 的值。
  "scripts": {
    "clean": "rm -rf ./lib",
    "build": "npm run clean && npm run build:esm && npm run build:cjs",
    "build:esm": "tsc -p ./configs/tsconfig.esm.json && mv lib/esm/index.js lib/esm/index.mjs",
    "build:cjs": "tsc -p ./configs/tsconfig.cjs.json",
    "prepack": "npm run build",
    "test": "mocha"
  },
  • 最初,在终端运行 npm test
bc@mbp-snyk modern-npm-package % npm test

> @clarkio/modern-npm-package@0.0.0-development test
> mocha

  NPM Package
    ✔️ should be an object
    ✔️ should have a helloworld property

  Hello World Function
    ✔️  should be a function
    ✔️ should return the hello world message

4 passing (22ms)

管道中的测试

依照上面的步骤,创立一个测试工作流,作为我的项目管道的一部分。

  1. 为仓库创立一个新的 GitHub Action:https://github.com/<your-account-or-organization>/<your-repo-name>/actions/new
  2. 将工作流程重命名为 test.yml
  3. 在工作流程文件中插入以下 Snyk 动作脚本:
name: Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [12.x, 14.x, 16.x, 18.x]

    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{matrix.node-version}}
        uses: actions/setup-node@v3
        with:
          node-version: ${{matrix.node-version}}
      - run: npm ci
      - run: npm test

这个 YAML 脚本查看出你的最新代码,装置其依赖性,并运行 npm test命令来执行测试。它对 node-version 字段中列出的每一个 Node.js 版本都会这样做,所以能够确保代码在每次运行时都能按预期工作。

当初曾经实现了对我的项目的设置,以便对 npm 包的代码进行运行和评估测试。然而,你可能在想 “ 我如何在另一个我的项目中应用我的 npm 包进行测试?” 让咱们来看看。

包测试

包上传实现后,除了单元测试外,咱们还要测试在另一个我的项目引入咱们包应用的状况,看看是否像咱们所冀望那样。这里有五种能够测试的办法:

  1. 通过 npm pack 输入装置
  2. 通过相对路径装置
  3. 通过 npm 链接装置
  4. 通过注册表装置(如 npmjs.com 的 npm 公共注册表)。
  5. 应用 Verdaccio(一个开源的 npm 公有 npm 注册我的项目)来运行端到端的软件包公布和装置步骤,作为你 CI 的一部分。

npm pack

这种办法将利用 npm pack 命令将 npm 包打包并压缩成一个文件(<package-name>.tgz)。而后你能够到你想应用该包的我的项目中,通过这个文件装置它。这样做的步骤如下。

  • 终端运行 npm pack。留神它产生的 .tgz 文件和它的地位。
  • 扭转目录到你想应用 npm 包的我的项目目录。例如:cd /path/to/project
  • 运行npm install /path/to/package.tgz
  • 而后就能够在我的项目中应用该包来测试货色了

npm link

利用 npm link 命令来装置本地包:

  1. 在以后包目录中,在终端运行 npm link
  2. 扭转目录到你想应用 npm 包的我的项目目录。例如:cd /path/to/project
  3. 在我的项目中运行 npm link <name-of-your-package>

这样在我的项目中就能够应用咱们的包。

相对路径

这种相似于 npm link。

  • 在终端运行 npm install /path/to/your/package

npm link 的办法相似,这容许咱们在我的项目中疾速测试包的性能,但不会给你残缺的相似生产的体验。这是因为它指向残缺的软件包源代码目录,而不是你在 npm 注册表中找到的软件包的构建版本。

npm registry

这种办法利用了 npm 包的公共(或你本人)注册表。它波及到公布的包,并像你通常对任何其余 npm 包那样进行装置。

  • 应用本文后面概述的步骤,通过 npm publish 命令公布 npm 包
  • 扭转目录到想应用 npm 包的我的项目目录。例如:cd /path/to/project
  • 在我的项目目录中运行 npm install <name-of-your-package>

施行安全检查

就像你不心愿在本人的我的项目中呈现安全漏洞一样,你也不心愿在其他人的我的项目中引入破绽。构建一个预计会在许多其余我的项目中应用的 npm 包,这就减少了确保事件平安的责任。你须要有安全检查,以帮忙监测、揭示和提供帮忙来缩小破绽。这就是像 Snyk 这样的工具能够简化实现这些需要所需的工作的中央。

对于这个例子中的 npm 包,你应用 GitHub 作为你的源码管制管理工具,所以利用它的 GitHub Actions 性能将 Snyk 整合到工作流程中。Snyk 有一个 GitHub Actions 参考我的项目,能够帮忙启动这方面的工作,并为你的我的项目可能应用的其余编程语言和工具提供例子。

  1. Snyk 是收费的,这里能够进行注册。
  2. 在 GitHub 上将你的 Snyk API 令牌增加为仓库机密:https://github.com/<your-account-or-organization>/<your-repo-name>/settings/secrets/actions/new
  3. 仓库创立一个新的 GitHub Action: https://github.com/<your-account-or-organization>/<your-repo-name>/actions/new
  4. 将 workflow 重命名为 snyk.yml
  5. 在 workflow 文件中插入以下 Snyk Action 脚本:
name: Snyk Security Check
on: [push,pull_request]
jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@main
      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{secrets.SNYK_TOKEN}}
  1. 提交你的批改。
  2. 验证 Action 胜利运行: https://github.com/<your-account-or-organization>/<your-repo-name>/actions

有了这个设置,任何时候任何人推送到你的版本库或针对它关上一个拉动申请,都会进行安全检查,以确保它不会在软件包中引入任何破绽。如果发现了问题,口头将失败,并揭示你发现的平安问题的细节。接下来,你将围绕版本治理和公布你的 npm 包进行自动化解决。

对于目前的设置,须要留神的一点是,它只利用了 Snyk 开源(SCA)产品,而不是 Snyk 代码(SAST)。Snyk Code 是咱们的代码平安产品,你须要首先通过你的 Snyk 账户启用它(收费),而后在这里增加到你的工作流程脚本中,以充分利用它。

实现版本治理和公布的自动化

每当在主分支中合并变动时,咱们不想每次都手动更新 npm 包的版本并公布它。相同,会想让这个过程主动产生。如果你还记得本篇文章后面那个简略的 npm 包的例子,用以下命令来更新 npm 包的版本,而后公布它。

npm version <major|minor|patch>
npm publish

什么是语义版本治理?

语义版本治理规定,版本要用三个占位符进行编号。第一个是次要版本,第二个是主要版本,而最初一个是补丁版本。

Semantic Release 的工具能够与 GitHub Actions 整合来帮忙咱们主动批改版本并公布。实现这一过程自动化的要害是,你在向我的项目提交变更时应用所谓的惯例提交。这使得自动化可能相应地更新所有,并晓得如何为你筹备我的项目的下一个版本。

  1. 运行:npm i -D semantic-release
  2. npx semantic-release-cli setup
  3. 依照终端的提醒,提供所需的令牌

    • 须要一个来自 GitHub 的集体拜访令牌。要创立一个,请到 https://github.com/<your-name-or-github-organization>/<your-repo-name>/settings/secrets/actions/new
    • 在创立此令牌时,请应用以下作用域
  1. 还须要一个来自 npm 的自动化类型的拜访令牌,只在 CI 环境中应用,这样它就能绕过你的账户的 2FA。要创立一个,请到 https://www.npmjs.com/settings/<your-npm-account>/tokens。请确保抉择 “Automation” 类型,因为这将用于 CI/CD 工作流程中。
bc@mbp-snyk modern-npm-package % npx semantic-release-cli setup
? What is your npm registry? https://registry.npmjs.org/
? What is vour nom username? clarkio
? What is your pm password? [hidden]
? What is your NPM two-factor authentication code? <2FA code>
Provide a GitHub Personal Access Token (create a token at https://github.com/settings/tokens/new?scopes=repo
<token>
? What CI are you using? Github Actions
bc@mbp-snyk modern-npm-package %
  1. 将 npm 令牌作为仓库机密增加到 GitHub 仓库中:https://github.com/<your-name-or-organization/<your-repository>/settings/secrets/actions/new。将机密的名称设置为NPM_TOKEN,其值是你在后面步骤中检索到的
  1. 回到我的项目中,进入 package.json 文件,像上面这样增加一个 release 键。如果你的版本库的主分支依然叫 master 而不是main,那么就相应地更新上述分支的值。
"release": {"branches": ["main"]
  }
  1. package.json 文件中也增加一个 publishConfig 键。
"publishConfig": {"access": "public"}
  1. 通过应用 semantic-release npm 脚本进行模仿运行来测试所有。采纳以下命令,并将 NPM_TOKEN=GH_TOKEN=值设置为应用您各自的令牌值。而后在你的终端中复制并运行残缺的命令,看看所有是否运行失常。你会看到过程被记录在终端的输入中。如果呈现任何问题,它们会在这里显示进去,并提供解决这些问题的细节。
  2. 在确认试运行胜利后,能够为 GitHub 仓库设置一个新的 GitHub 动作来为你解决公布过程。转到你在 GitHub 上的仓库,点击 “Actions”。
  3. 点击新建工作流程选项。
  4. 将工作流程重命名为 release.yml。
  5. 在新的工作流程文件中退出以下 YAML 脚本。这个脚本次要是说,一旦 Snyk 安全检查工作胜利实现,就运行公布工作。公布作业会查看代码,设置 Node.js 环境,装置你的依赖项,而后应用你的 GitHub 和 npm 令牌运行语义公布。
name: Release
on:
  workflow_run:
    workflows: ['Snyk Security Check', 'Tests']
    branches: [main]
    types:
      - completed

jobs:
  release:
    name: Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: 'lts/*'
      - name: Install dependencies
        run: npm ci
      - name: Release
        env:
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
          NPM_TOKEN: ${{secrets.NPM_TOKEN}}
        run: npx semantic-release

13. 提交你的本地批改并推送到你的 GitHub 仓库

  • 能够通过在终端运行命令 git commit -am '<your commit message>',而后 git push 来实现。
  • 也能够在 VS Code 中通过其版本控制性能做到这一点。
  1. 在所有这些设置实现后,当初能够应用传统的提交形式将批改推送到你的主分支(或通过合并拉动申请),而后公布工作流就会运行(当然是在 Snyk 安全检查之后)。你能够在 modern-npm-package 版本库工作流程的例子中看到这种状况。

总结

咱们总结一下在本文中学到的所有。首先,相熟了设置、创立和部署一个简略的 npm 包。这对于相熟首次公布本人的 npm 包来说是很好的。然而,如果想制作一个供生产应用的 npm 包,这样做是相当费劲的,也是不可继续的。

为了实现制作一个可用于生产的包,随后学会了如何为 CommonJS(CJS)和 ECMAScript(ESM)模块格局进行构建,设置和编写单元测试,实现安全检查,并主动进行版本治理和公布。有了这些常识,当初曾经筹备好制作更多属于你本人的 npm 包了,这些包很容易被社区或你的公司所应用。

起源:https://snyk.io/blog/best-practices-create-modern-npm-package/

代码部署后可能存在的 BUG 没法实时晓得,预先为了解决这些 BUG,花了大量的工夫进行 log 调试,这边顺便给大家举荐一个好用的 BUG 监控工具 Fundebug。

交换

有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

退出移动版