Tackling TypeScript
請支持這本書:購買捐款
(廣告,請不要封鎖。)

8 透過 TypeScript 建立基於 CommonJS 的 npm 套件



本章節說明如何使用 TypeScript 建立基於 CommonJS 模組格式的套件管理員 npm 套件。

  GitHub 儲存庫:ts-demo-npm-cjs

在本章節中,我們將探討 儲存庫 ts-demo-npm-cjs,可以在 GitHub 上下載。(我故意沒有將其作為套件發布到 npm。)

8.1 必備知識

您應該大致熟悉

8.2 限制

在本章節中,我們使用 TypeScript 目前最支援的

特別是在 Node.js 上,TypeScript 目前並不真正支援 ECMAScript 模組和 .js 以外的檔案名稱副檔名。

8.3 儲存庫 ts-demo-npm-cjs

儲存庫 ts-demo-npm-cjs 的結構如下

ts-demo-npm-cjs/
  .gitignore
  .npmignore
  dist/   (created on demand)
  package.json
  ts/
    src/
      index.ts
    test/
      index_test.ts
  tsconfig.json

除了套件的 package.json 外,儲存庫包含

package.json 包含編譯的腳本

這是兩個 TypeScript 檔案編譯結果的放置位置

ts/src/index.ts       --> dist/src/index.js
ts/test/index_test.ts --> dist/test/index_test.js

8.4 .gitignore

此檔案列出我們不想檢查到 git 的目錄

node_modules/
dist/

說明

8.5 .npmignore

在哪些檔案應該上傳到 npm 註冊表,哪些不應該上傳的部分,我們的需求與 git 不同。因此,除了 .gitignore 之外,我們還需要檔案 .npmignore

ts/

兩個不同點是

請注意,npm 預設會忽略目錄 node_modules/

8.6 package.json

package.json 如下所示

{
  ···
  "type": "commonjs",
  "main": "./dist/src/index.js",
  "types": "./dist/src/index.d.ts",
  "scripts": {
    "clean": "shx rm -rf dist/*",
    "build": "tsc",
    "watch": "tsc --watch",
    "test": "mocha --ui qunit",
    "testall": "mocha --ui qunit dist/test",
    "prepack": "npm run clean && npm run build"
  },
  "// devDependencies": {
    "@types/node": "Needed for unit test assertions (assert.equal() etc.)",
    "shx": "Needed for development-time package.json scripts"
  },
  "devDependencies": {
    "@types/lodash": "···",
    "@types/mocha": "···",
    "@types/node": "···",
    "mocha": "···",
    "shx": "···"
  },
  "dependencies": {
    "lodash": "···"
  }
}

我們來看一下屬性

接下來的兩個小節會說明其餘的屬性。

8.6.1 腳本

屬性 scripts 定義各種可透過 npm run 呼叫的命令。例如,腳本 clean 是透過 npm run clean 呼叫的。先前的 package.json 包含下列腳本

請注意,當我們使用 IDE 時,不需要腳本 buildwatch,因為我們可以讓 IDE 建立成品。但是 prepack 腳本需要這些腳本。

8.6.2 dependenciesdevDependencies

dependencies 應該只包含在匯入套件時需要的套件。這會排除用於執行測試等用途的套件。

名稱以 @types/ 開頭的套件會提供沒有任何類型定義的套件的 TypeScript 類型定義。沒有前者,我們就無法使用後者。這些是一般的相依性還是開發相依性?這取決於

8.6.3 關於 package.json 的更多資訊

8.7 tsconfig.json

{
  "compilerOptions": {
    "rootDir": "ts",
    "outDir": "dist",
    "target": "es2019",
    "lib": [
      "es2019"
    ],
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true,
    "declaration": true,
    "sourceMap": true
  }
}

其餘選項由 tsconfig.json 的官方文件 說明。

8.8 TypeScript 程式碼

8.8.1 index.ts

此檔案提供套件的實際功能

import endsWith from 'lodash/endsWith';

export function removeSuffix(str: string, suffix: string) {
  if (!endsWith(str, suffix)) {
    throw new Error(JSON.stringify(suffix)} + ' is not a suffix of ' +
      JSON.stringify(str));
  }
  return str.slice(0, -suffix.length);
}

它使用 函式庫 Lodash 的函式 endsWith()。這就是 Lodash 是正常相依性的原因,因為它在執行階段需要。

8.8.2 index_test.ts

此檔案包含 index.ts 的單元測試

import { strict as assert } from 'assert';
import { removeSuffix } from '../src/index';

test('removeSuffix()', () => {
  assert.equal(
    removeSuffix('myfile.txt', '.txt'),
    'myfile');
  assert.throws(() => removeSuffix('myfile.txt', 'abc'));
});

我們可以像這樣執行測試

npm t dist/test/index_test.js

如你所見,我們執行的是測試的已編譯版本(在目錄 dist/ 中),而不是 TypeScript 程式碼。

有關單元測試框架 Mocha 的更多資訊,請參閱 其首頁