主頁 > 企業開發 > typescript+webpack+api-extractor構建一個js庫

typescript+webpack+api-extractor構建一個js庫

2022-07-13 11:53:46 企業開發

  • 依賴說明
  • 入口檔案
  • tsconfig配置
  • webpack組態檔
    • webpack入口檔案配置
    • webpack為typescript和less檔案配置各自的loader
    • webpack的output配置
    • 運行webpack進行打包
    • 測驗驗證
  • 輸出esm模塊
    • 已經輸出了umd格式的js了, 為什么還要輸出esm模塊? ----TreeShaking
    • 用tsc輸出esm和型別宣告檔案
  • 完善package.json檔案
    • package.json中添加exports配置宣告模塊匯出路徑
  • 用api-extractor提取出干凈的.d.ts
    • 配置使用API extractor
    • 更新package.json
    • 用@internal標注只希望在內部使用的class
  • 小結

記錄使用typescript配合webpack打包一個javascript library的配置程序.

目標是構建一個可以同時支持CommonJs, esm, amd這個幾個js模塊系統的javascript庫, 然后還有一個單獨打包出一個css的樣式檔案的需求.

為此以構建一個名為loaf的javascript庫為例; 首先新建專案檔案目錄loaf, 并進入此檔案目錄執行npm init命令, 然后按照控制臺的提示輸入對應的資訊, 完成后就會在loaf目錄下得到一個package.json檔案

image

然后使用npm i命令安裝所需的依賴

npm i -D webpack webpack-cli typescript babel-loader @babel/core @babel/preset-env @babel/preset-typescript ts-node @types/node @types/webpack mini-css-extract-plugin css-minimizer-webpack-plugin less less-loader terser-webpack-plugin

依賴說明

  • webpack webpack-cli: webpack打包工具和webpack命令列介面
  • typescript: 用于支持typescript語言
  • babel-loader @babel/core @babel/preset-env @babel/preset-typescript: babel相關的東西, 主要是需要babel-loader將撰寫的typescript代碼轉譯成es5或es6已獲得更好的瀏覽器兼容性
  • ts-node @types/node @types/webpack: 安裝這幾個包是為了能用typescript撰寫webpack組態檔(webpack.config.ts)
  • mini-css-extract-plugin less less-loader: 編譯提取less檔案到單獨的css檔案的相關依賴
  • css-minimizer-webpack-plugin terser-webpack-plugin: 用于最小化js和css檔案尺寸的webpack插件

入口檔案

通常使用index.ts作為入口, 并將其放到src目錄下, 由于有輸出樣式檔案的需求, 所以還要新建styles/index.less

mkdir src && touch src/index.ts
mkdir src/styles && touch src/styles/index.less

tsconfig配置

新建tsconfig.json檔案

touch tsconfig.json

填入以下配置(部分選項配有注釋):

{
    "compilerOptions": {
        "outDir": "dist/lib",
        "sourceMap": false,
        "noImplicitAny": true,
        "module": "commonjs",
        // 開啟這個選項, 可以讓你直接通過`import`的方式來參考commonjs模塊
        // 這樣你的代碼庫中就可以統一的使用import匯入依賴了, 而不需要另外再使用require匯入commonjs模塊
        "esModuleInterop": true,
        // 是否允許合成默認匯入
        // 開啟后, 依賴的模塊如果沒有匯出默認的模塊
        // 那么typescript會幫你給該模塊自動合成一個默認匯出讓你可以通過默認匯入的方式參考該模塊
        "allowSyntheticDefaultImports": true,
        // 是否生成`.d.ts`的型別宣告檔案
        "declaration": true,
        // 輸出的目標js版本, 這里用es6, 然后配和babel進行轉譯才以獲得良好的瀏覽器兼容
        "target": "es6",
        "allowJs": true,
        "moduleResolution": "node",
        "lib": ["es2015", "dom"],
        "declarationMap": true,
        // 啟用嚴格的null檢查
        "strictNullChecks": true,
        // 啟用嚴格的屬性初始化檢查
        // 啟用后類屬性必須顯示標注為可慷訓賦一個非空的初始值
        "strictPropertyInitialization": true
    },
    "exclude": ["node_modules"],
    "include": ["src/**/*"]
}

webpack組態檔

創建webpack.config.ts

touch webpack.config.ts
webpack.config.ts
import path from "path";
import { Configuration, Entry } from "webpack";
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import CssMinimizer from 'css-minimizer-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin'

const isProd = process.env.NODE_ENV === 'production';

/** 
 * 這里用到了webpack的[Multiple file types per entry](https://webpack.js.org/guides/entry-advanced/)特性
 * 注意`.less`入口檔案必須放在`.ts`檔案前 */
const entryFiles: string[] = ['./src/styles/index.less', './src/index.ts'];
const entry: Entry = {
  index: entryFiles,
  'index.min': entryFiles,
}; 

const config: Configuration = {
  entry,
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({ test: /.min.js$/ }),
      new CssMinimizer({
        test: /.min.css$/,
      }),
    ],
  },
  module: {
    rules: [
      {
        test: /.ts$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {
          presets: ['@babel/env', '@babel/typescript'],
        },
      },
      {
        test: /.less$/,
        use: [
          isProd ? MiniCssExtractPlugin.loader : 'style-loader',
          {
            loader: 'css-loader',
          },
          'postcss-loader',
          'less-loader',
        ],
      },
    ],
  },
  output: {
    path: path.resolve(__dirname, 'dist/umd'),
    library: {
      type: 'umd',
      name: {
        amd: 'loaf',
        commonjs: 'loaf',
        root: 'loaf',
      },
    },
  },
  resolve: {
    extensions: ['.ts', '.less'],
  },
  devtool: 'source-map',
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
    }),
  ],
};


export default config;

webpack入口檔案配置

...
const isProd = process.env.NODE_ENV === 'production';

/** 
 * 這里用到了webpack的[Multiple file types per entry](https://webpack.js.org/guides/entry-advanced/)特性
 * 注意`.less`入口檔案必須放在`.ts`檔案前 */
const entryFiles: string[] = ['./src/styles/index.less', './src/index.ts'];
const entry: Entry = {
  index: entryFiles,
  'index.min': entryFiles,
}; 

const config: Configuration = {
  entry,
  ...
}
...

在上面的webpack.config.json中,我們配置了兩個入口分別是indexindex.min,不難看出,多出的一個index.min入口是為了經過壓縮后js和css檔案,在生產環境使用一般都會使用.min.js結尾的檔案以減少網路傳輸時的尺寸; 實作這個還需要結合optimization相關配置, 如下:

optimization: {
   minimize: true,
   minimizer: [
     new TerserPlugin({ test: /.min.js$/ }),
     new CssMinimizer({
       test: /.min.css$/,
     }),
   ],
 },

另外,indexindex.min的值都是相同的entryFiles物件,這個物件是一個字串陣列,里面放的就是我們的入口檔案相對路徑,這里一定要注意把./src/styles/index.less置于./src/index.ts之前,

webpack為typescript和less檔案配置各自的loader

配置完入口后, 就需要為typescript和less代碼配置各自的loader

module: {
    rules: [
      {
        test: /.ts$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {
          presets: ['@babel/env', '@babel/typescript'],
        },
      },
      {
        test: /.less$/,
        use: [
          isProd ? MiniCssExtractPlugin.loader : 'style-loader',
          {
            loader: 'css-loader',
          },
          'postcss-loader',
          'less-loader',
        ],
      },
    ],
},
  • mini-css-extract-plugin less less-loader: 編譯提取less檔案到單獨的css檔案的相關依賴
    上面的配置為.ts結尾的檔案配置了babel-loader; 為.less結尾的檔案配置一串loader, 使用了use, use中的loader的執行順序是從后往前的, 上面less的配置就是告訴webpack遇到less檔案時, 一次用less-loader->postcss-loader->css-loader->生產環境用 MiniCssExtractPlugin.loader() 否則用 style-loader;

MiniCssExtractPlugin.loader使用前要先在plugins進行初始化

...
const config = {
...
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
    }),
  ],
...
}
...

webpack的output配置

...
const config = {
...
  output: {
    path: path.resolve(__dirname, 'dist/umd'),
    library: {
      type: 'umd',
      name: {
        amd: 'loaf',
        commonjs: 'loaf',
        root: 'loaf',
      },
    },
  },
...
}
...

這里配置webpack以umd的方式輸出到相對目錄dist/umd目錄中, umdUniversal Module Definition(通用模塊定義)的縮寫, umd格式輸出library允許用戶通過commonjs, AMD, <script src="https://www.cnblogs.com/laggage/p/...">的方式對library進行參考config.library.name可以為不同的模塊系統配置不同的匯出模塊名供客戶端來進行參考; 由于這里的匯出模塊名都是loaf, 所以也可以直接config.library.name設定成loaf.

運行webpack進行打包

現在回到最開始通過npm init生成的package.json檔案, 在修改其內容如下

{
  "name": "loaf",
  "version": "1.0.0",
  "description": "A demo shows how to create & build a javascript library with webpack & typescript",
  "main": "index.js",
  "scripts": {
    "build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
    "test": "npm run test"
  },
  "keywords": [
    "demo"
  ],
  "author": "laggage",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.18.6",
    "@babel/preset-env": "^7.18.6",
    "@babel/preset-typescript": "^7.18.6",
    "@types/node": "^18.0.0",
    "@types/webpack": "^5.28.0",
    "babel-loader": "^8.2.5",
    "css-loader": "^6.7.1",
    "css-minimizer-webpack-plugin": "^4.0.0",
    "less": "^4.1.3",
    "less-loader": "^11.0.0",
    "mini-css-extract-plugin": "^2.6.1",
    "postcss-loader": "^7.0.0",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.3.3",
    "ts-node": "^10.8.2",
    "typescript": "^4.7.4",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0"
  }
}

新增了一個腳本命令 "build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production", 然后命令列到專案目錄下執行npm run build:umd, 不出意外應該就構建成功了, 此時生成的dist目錄結構如下

dist
└── umd
    ├── index.css
    ├── index.js
    ├── index.js.map
    ├── index.min.css
    ├── index.min.js
    └── index.min.js.map

1 directory, 6 files

測驗驗證

新建demo.html進行測驗

mkdir demo && touch demo/demo.html
demo/demo.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="https://www.cnblogs.com/laggage/dist/umd/index.js"></script>
    <script type="text/javascript">
        console.log(loaf, '\n', loaf.Foo)
    </script>
</body>
</html>

用瀏覽器打開demo.html, 然后F12打開控制臺, 可以看到如下輸出則說明初步達成了目標:

Module {__esModule: true, Symbol(Symbol.toStringTag): 'Module'}
demo.html:13 ? Foo() {
    var _bar = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Bar();

    src_classCallCheck(this, Foo);

    this._bar = _bar;
  }

輸出esm模塊

完成上面的步驟后, 我們已經到了一個umd模塊的輸出, 相關檔案都在dist/umd目錄下; 其中包含可供CommonJs ESM AMD模塊系統和script標簽使用的umd格式的javascript檔案和一個單獨的css樣式檔案.

已經輸出了umd格式的js了, 為什么還要輸出esm模塊? ----TreeShaking

Tree shaking is a term commonly used in the JavaScript context for dead-code elimination. It relies on the static structure of ES2015 module syntax, i.e. import and export. The name and concept have been popularized by the ES2015 module bundler rollup.

此庫的使用者也使用了類似webpack之類的支持Tree Shaking
的模塊打包工具,需要讓使用者的打包工具能對這個js庫loaf進行死代碼優化Tree Shaking

從webpack檔案中看出, tree-shaking依賴于ES2015(ES2015 module syntax, ES2015=ES6)的模塊系統, tree-shaking可以對打包體積有不錯優化, 所以為了支持使用者進行tree-shaking, 輸出esm模塊(esm模塊就是指 ES2015 module syntax)是很有必要的.

用tsc輸出esm和型別宣告檔案

tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm

上面的命令使用typescript編譯器命令列介面tsc輸出了ES6模塊格式的javascript檔案到dist/lib-esm目錄下

將這個目錄加入到package.jsonscripts配置中:

package.json
{
  "name": "loaf",
  "version": "1.0.0",
  "description": "A demo shows how to create & build a javascript library with webpack & typescript",
  "main": "index.js",
  "scripts": {
    "build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
    "build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm",
    "test": "npm run test"
  },
  "keywords": [
    "demo"
  ],
  "author": "laggage",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.18.6",
    "@babel/preset-env": "^7.18.6",
    "@babel/preset-typescript": "^7.18.6",
    "@types/node": "^18.0.0",
    "@types/webpack": "^5.28.0",
    "babel-loader": "^8.2.5",
    "css-loader": "^6.7.1",
    "css-minimizer-webpack-plugin": "^4.0.0",
    "less": "^4.1.3",
    "less-loader": "^11.0.0",
    "mini-css-extract-plugin": "^2.6.1",
    "postcss-loader": "^7.0.0",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.3.3",
    "ts-node": "^10.8.2",
    "typescript": "^4.7.4",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0"
  }
}

然后運行: npm run build:lib-esm, 此時dist目錄結構如下:

dist
├── lib-esm
│   ├── bar.js
│   └── index.js
├── typings
│   ├── bar.d.ts
│   ├── bar.d.ts.map
│   ├── index.d.ts
│   └── index.d.ts.map
└── umd
    ├── index.css
    ├── index.js
    ├── index.js.map
    ├── index.min.css
    ├── index.min.js
    └── index.min.js.map

3 directories, 12 files

多出了兩個子目錄分別為lib-esmtypings, 分別放著es6模塊格式的javascript輸出檔案和typescript型別宣告檔案.

完善package.json檔案

到目前為止, package.json的scripts配置中, 已經有了build:umdbuild:lib-esm用于構建umd格式的輸出和esm格式的輸出, 現在我們再向添加一個build用來組合build:umdbuild:lib-esm并進行最終的構建, 再次之前先安裝一個依賴shx, 用于跨平臺執行一些shell腳本: npm i -D shx;

更新package.json檔案:

package.json
{
  "name": "loaf",
  "version": "1.0.0",
  "description": "A demo shows how to create & build a javascript library with webpack & typescript",
  "main": "index.js",
  "scripts": {
    "build": "shx rm -rf dist/** && npm run build:umd && npm run build:lib-esm",
    "build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
    "build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm",
    "test": "npm run test"
  },
  "keywords": [
    "demo"
  ],
  "author": "laggage",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.18.6",
    "@babel/preset-env": "^7.18.6",
    "@babel/preset-typescript": "^7.18.6",
    "@types/node": "^18.0.0",
    "@types/webpack": "^5.28.0",
    "babel-loader": "^8.2.5",
    "css-loader": "^6.7.1",
    "css-minimizer-webpack-plugin": "^4.0.0",
    "less": "^4.1.3",
    "less-loader": "^11.0.0",
    "mini-css-extract-plugin": "^2.6.1",
    "postcss-loader": "^7.0.0",
    "shx": "^0.3.4",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.3.3",
    "ts-node": "^10.8.2",
    "typescript": "^4.7.4",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0"
  }
}

package.json檔案生成typescript宣告檔案所在的路徑(可以參考typescript官網:Including declarations in your npm package):

package.json
{
  "name": "loaf",
  "version": "1.0.0",
  "description": "A demo shows how to create & build a javascript library with webpack & typescript",
  "main": "index.js",
  "typings": "./typings",
  "scripts": {
    "build": "shx rm -rf dist/** && npm run build:umd && npm run build:lib-esm",
    "build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
    "build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm",
    "test": "npm run test"
  },
  "keywords": [
    "demo"
  ],
  "author": "laggage",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.18.6",
    "@babel/preset-env": "^7.18.6",
    "@babel/preset-typescript": "^7.18.6",
    "@types/node": "^18.0.0",
    "@types/webpack": "^5.28.0",
    "babel-loader": "^8.2.5",
    "css-loader": "^6.7.1",
    "css-minimizer-webpack-plugin": "^4.0.0",
    "less": "^4.1.3",
    "less-loader": "^11.0.0",
    "mini-css-extract-plugin": "^2.6.1",
    "postcss-loader": "^7.0.0",
    "shx": "^0.3.4",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.3.3",
    "ts-node": "^10.8.2",
    "typescript": "^4.7.4",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0"
  }
}

package.json中添加exports配置宣告模塊匯出路徑

package.json中的exports欄位用于告訴使用者參考此庫時從哪里尋找對應的模塊檔案. 比如使用者可能通過esm模塊參考此庫:

import { Foo } from 'loaf';

const foo = new Foo();

此時如果我們的package.json中沒有指定exports欄位, 那么模塊系統會去尋找node_modules/index.js, 結果肯定是找不到的, 因為我們真正的esm格式的輸出檔案應該是在node_modules/loaf/lib-esm中的

于是我們可以這樣來配置exports:

package.json
{
  "name": "loaf",
  "version": "1.0.0",
  "description": "A demo shows how to create & build a javascript library with webpack & typescript",
  "main": "index.js",
  "typings": "./typings",
  "exports": {
    "./*": "./lib-esm/*",
    "./umd/*": "./umd"
  },
  "scripts": {
    "build": "shx rm -rf dist/** && npm run build:umd && npm run build:lib-esm",
    "build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
    "build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm",
    "test": "npm run test"
  },
  "keywords": [
    "demo"
  ],
  "author": "laggage",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.18.6",
    "@babel/preset-env": "^7.18.6",
    "@babel/preset-typescript": "^7.18.6",
    "@types/node": "^18.0.0",
    "@types/webpack": "^5.28.0",
    "babel-loader": "^8.2.5",
    "css-loader": "^6.7.1",
    "css-minimizer-webpack-plugin": "^4.0.0",
    "less": "^4.1.3",
    "less-loader": "^11.0.0",
    "mini-css-extract-plugin": "^2.6.1",
    "postcss-loader": "^7.0.0",
    "shx": "^0.3.4",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.3.3",
    "ts-node": "^10.8.2",
    "typescript": "^4.7.4",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0"
  }
}

用api-extractor提取出干凈的.d.ts

在上面的用tsc輸出esm和型別宣告檔案這一段中, 我們通過tsc命令輸出了typescript了型別宣告檔案到dist/types目錄下, 這個目錄下有兩個.d.ts檔案, 分別是bar.d.tsfoo.d.ts, 通常是希望這些宣告檔案都在一個檔案index.d.ts中的, 如果他們分散開了, 以本庫為例, 如果我要使用本庫中的Bar類, 那么我可能需要這樣來匯入:

import { Bar } from 'loaf/typings/bar';

我不覺得的這種匯入方式是好的做法, 理想的匯入方式應該像下面這樣:

import { Bar } from 'loaf';

所以接下來, 還要引入微軟提供的api-extractor

配置使用API extractor

安裝依賴:

npm install -D @microsoft/api-extractor

再全域安裝下:

npm install -g @microsoft/api-extractor

生成api-extractor.json

api-extractor init

稍微修改下api-extractor.json

<

api-extractor.json
/**
 * Config file for API Extractor.  For more info, please visit: https://api-extractor.com
 */
{
  "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
  /**
   * Optionally specifies another JSON config file that this file extends from.  This provides a way for
   * standard settings to be shared across multiple projects.
   *
   * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains
   * the "extends" field.  Otherwise, the first path segment is interpreted as an NPM package name, and will be
   * resolved using NodeJS require().
   *
   * SUPPORTED TOKENS: none
   * DEFAULT VALUE: ""
   */
  // "extends": "./shared/api-extractor-base.json"
  // "extends": "my-package/include/api-extractor-base.json"

  /**
   * Determines the "<projectFolder>" token that can be used with other config file settings.  The project folder
   * typically contains the tsconfig.json and package.json config files, but the path is user-defined.
   *
   * The path is resolved relative to the folder of the config file that contains the setting.
   *
   * The default value for "projectFolder" is the token "<lookup>", which means the folder is determined by traversing
   * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder
   * that contains a tsconfig.json file.  If a tsconfig.json file cannot be found in this way, then an error
   * will be reported.
   *
   * SUPPORTED TOKENS: <lookup>
   * DEFAULT VALUE: "<lookup>"
   */
  // "projectFolder": "..",

  /**
   * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis.  API Extractor
   * analyzes the symbols exported by this module.
   *
   * The file extension must be ".d.ts" and not ".ts".
   *
   * The path is resolved relative to the folder of the config file that contains the setting; to change this,
   * prepend a folder token such as "<projectFolder>".
   *
   * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
   */
  "mainEntryPointFilePath": "<projectFolder>/dist/typings-temp/index.d.ts",

  /**
   * A list of NPM package names whose exports should be treated as part of this package.
   *
   * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1",
   * and another NPM package "library2" is embedded in this bundle.  Some types from library2 may become part
   * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly
   * imports library2.  To avoid this, we can specify:
   *
   *   "bundledPackages": [ "library2" ],
   *
   * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been
   * local files for library1.
   */
  "bundledPackages": [],

  /**
   * Determines how the TypeScript compiler engine will be invoked by API Extractor.
   */
  "compiler": {
    /**
     * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project.
     *
     * The path is resolved relative to the folder of the config file that contains the setting; to change this,
     * prepend a folder token such as "<projectFolder>".
     *
     * Note: This setting will be ignored if "overrideTsconfig" is used.
     *
     * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
     * DEFAULT VALUE: "<projectFolder>/tsconfig.json"
     */
    // "tsconfigFilePath": "<projectFolder>/tsconfig.json",
    /**
     * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk.
     * The object must conform to the TypeScript tsconfig schema:
     *
     * http://json.schemastore.org/tsconfig
     *
     * If omitted, then the tsconfig.json file will be read from the "projectFolder".
     *
     * DEFAULT VALUE: no overrideTsconfig section
     */
    // "overrideTsconfig": {
    //   . . .
    // }
    /**
     * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended
     * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when
     * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses
     * for its analysis.  Where possible, the underlying issue should be fixed rather than relying on skipLibCheck.
     *
     * DEFAULT VALUE: false
     */
    // "skipLibCheck": true,
  },

  /**
   * Configures how the API report file (*.api.md) will be generated.
   */
  "apiReport": {
    /**
     * (REQUIRED) Whether to generate an API report.
     */
    "enabled": true

    /**
     * The filename for the API report files.  It will be combined with "reportFolder" or "reportTempFolder" to produce
     * a full file path.
     *
     * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/".
     *
     * SUPPORTED TOKENS: <packageName>, <unscopedPackageName>
     * DEFAULT VALUE: "<unscopedPackageName>.api.md"
     */
    // "reportFileName": "<unscopedPackageName>.api.md",

    /**
     * Specifies the folder where the API report file is written.  The file name portion is determined by
     * the "reportFileName" setting.
     *
     * The API report file is normally tracked by Git.  Changes to it can be used to trigger a branch policy,
     * e.g. for an API review.
     *
     * The path is resolved relative to the folder of the config file that contains the setting; to change this,
     * prepend a folder token such as "<projectFolder>".
     *
     * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
     * DEFAULT VALUE: "<projectFolder>/etc/"
     */
    // "reportFolder": "<projectFolder>/etc/",

    /**
     * Specifies the folder where the temporary report file is written.  The file name portion is determined by
     * the "reportFileName" setting.
     *
     * After the temporary file is written to disk, it is compared with the file in the "reportFolder".
     * If they are different, a production build will fail.
     *
     * The path is resolved relative to the folder of the config file that contains the setting; to change this,
     * prepend a folder token such as "<projectFolder>".
     *
     * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
     * DEFAULT VALUE: "<projectFolder>/temp/"
     */
    // "reportTempFolder": "<projectFolder>/temp/"
  },

  /**
   * Configures how the doc model file (*.api.json) will be generated.
   */
  "docModel": {
    /**
     * (REQUIRED) Whether to generate a doc model file.
     */
    "enabled": true

    /**
     * The output path for the doc model file.  The file extension should be ".api.json".
     *
     * The path is resolved relative to the folder of the config file that contains the setting; to change this,
     * prepend a folder token such as "<projectFolder>".
     *
     * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
     * DEFAULT VALUE: "<projectFolder>/temp/<unscopedPackageName>.api.json"
     */
    // "apiJsonFilePath": "<projectFolder>/temp/<unscopedPackageName>.api.json"
  },

  /**
   * Configures how the .d.ts rollup file will be generated.
   */
  "dtsRollup": {
    /**
     * (REQUIRED) Whether to generate the .d.ts rollup file.
     */
    "enabled": true,

    /**
     * Specifies the output path for a .d.ts rollup file to be generated without any trimming.
     * This file will include all declarations that are exported by the main entry point.
     *
     * If the path is an empty string, then this file will not be written.
     *
     * The path is resolved relative to the folder of the config file that contains the setting; to change this,
     * prepend a folder token such as "<projectFolder>".
     *
     * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
     * DEFAULT VALUE: "<projectFolder>/dist/<unscopedPackageName>.d.ts"
     */
    "untrimmedFilePath": "<projectFolder>/dist/typing/index.d.ts"

    /**
     * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release.
     * This file will include only declarations that are marked as "@public" or "@beta".
     *
     * The path is resolved relative to the folder of the config file that contains the setting; to change this,
     * prepend a folder token such as "<projectFolder>".
     *
     * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
     * DEFAULT VALUE: ""
     */
    // "betaTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-beta.d.ts",

    /**
     * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release.
     * This file will include only declarations that are marked as "@public".
     *
     * If the path is an empty string, then this file will not be written.
     *
     * The path is resolved relative to the folder of the config file that contains the setting; to change this,
     * prepend a folder token such as "<projectFolder>".
     *
     * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
     * DEFAULT VALUE: ""
     */
    // "publicTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-public.d.ts",

    /**
     * When a declaration is trimmed, by default it will be replaced by a code comment such as
     * "Excluded from this release type: exampleMember".  Set "omitTrimmingComments" to true to remove the
     * declaration completely.
     *
     * DEFAULT VALUE: false
     */
    // "omitTrimmingComments": true
  },

  /**
   * Configures how the tsdoc-metadata.json file will be generated.
   */
  "tsdocMetadata": {
    /**
     * Whether to generate the tsdoc-metadata.json file.
     *
     * DEFAULT VALUE: true
     */
    // "enabled": true,
    /**
     * Specifies where the TSDoc metadata file should be written.
     *
     * The path is resolved relative to the folder of the config file that contains the setting; to change this,
     * prepend a folder token such as "<projectFolder>".
     *
     * The default value is "<lookup>", which causes the path to be automatically inferred from the "tsdocMetadata",
     * "typings" or "main" fields of the project's package.json.  If none of these fields are set, the lookup
     * falls back to "tsdoc-metadata.json" in the package folder.
     *
     * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
     * DEFAULT VALUE: "<lookup>"
     */
    // "tsdocMetadataFilePath": "<projectFolder>/dist/tsdoc-metadata.json"
  },

  /**
   * Specifies what type of newlines API Extractor should use when writing output files.  By default, the output files
   * will be written with Windows-style newlines.  To use POSIX-style newlines, specify "lf" instead.
   * To use the OS's default newline kind, specify "os".
   *
   * DEFAULT VALUE: "crlf"
   */
  // "newlineKind": "crlf",

  /**
   * Configures how API Extractor reports error and warning messages produced during analysis.
   *
   * There are three sources of messages:  compiler messages, API Extractor messages, and TSDoc messages.
   */
  "messages": {
    /**
     * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing
     * the input .d.ts files.
     *
     * TypeScript message identifiers start with "TS" followed by an integer.  For example: "TS2551"
     *
     * DEFAULT VALUE:  A single "default" entry with logLevel=warning.
     */
    "compilerMessageReporting": {
      /**
       * Configures the default routing for messages that don't match an explicit rule in this table.
       */
      "default": {
        /**
         * Specifies whether the message should be written to the the tool's output log.  Note that
         * the "addToApiReportFile" property may supersede this option.
         *
         * Possible values: "error", "warning", "none"
         *
         * Errors cause the build to fail and return a nonzero exit code.  Warnings cause a production build fail
         * and return a nonzero exit code.  For a non-production build (e.g. when "api-extractor run" includes
         * the "--local" option), the warning is displayed but the build will not fail.
         *
         * DEFAULT VALUE: "warning"
         */
        "logLevel": "warning"

        /**
         * When addToApiReportFile is true:  If API Extractor is configured to write an API report file (.api.md),
         * then the message will be written inside that file; otherwise, the message is instead logged according to
         * the "logLevel" option.
         *
         * DEFAULT VALUE: false
         */
        // "addToApiReportFile": false
      }

      // "TS2551": {
      //   "logLevel": "warning",
      //   "addToApiReportFile": true
      // },
      //
      // . . .
    },

    /**
     * Configures handling of messages reported by API Extractor during its analysis.
     *
     * API Extractor message identifiers start with "ae-".  For example: "ae-extra-release-tag"
     *
     * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings
     */
    "extractorMessageReporting": {
      "default": {
        "logLevel": "warning"
        // "addToApiReportFile": false
      }

      // "ae-extra-release-tag": {
      //   "logLevel": "warning",
      //   "addToApiReportFile": true
      // },
      //
      // . . .
    },

    /**
     * Configures handling of messages reported by the TSDoc parser when analyzing code comments.
     *
     * TSDoc message identifiers start with "tsdoc-".  For example: "tsdoc-link-tag-unescaped-text"
     *
     * DEFAULT VALUE:  A single "default" entry with logLevel=warning.
     */
    "tsdocMessageReporting": {
      "default": {
        "logLevel": "warning"
        // "addToApiReportFile": false
      }

      // "tsdoc-link-tag-unescaped-text": {
      //   "logLevel": "warning",
      //   "addToApiReportFile": true
      // },
      //
      // . . .
    }
  }
}

更新package.json

{
...
  "scripts": {
    "build": "shx rm -rf dist/** && npm run build:umd && npm run build:lib-esm && npm run build:extract-api",
    "build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
    "build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings-temp -m es6 --outDir dist/lib-esm",
    "build:extract-api": "api-extractor run && shx rm -rf dist/typings-temp",
    "build:extract-api-local": "shx mkdir -p ./etc && npm run build:lib-esm && api-extractor run -l",
    "test": "npm run test"
  },
 ...
}

注意, 這里處理新增了一個build:extract-api到scripts配置中, 還修改了build:lib-esm的配置, 將其輸出的typescript型別宣告檔案放到了, typings-temp目錄中, 最后這個目錄是要洗掉; 還要注意, 每次提交代碼到版本管理工具前, 要先運行npm run build:extract-api-local, 這個命令會生成./etc/<libraryName>.api.md檔案, 這個檔案是api-extractor生成的api檔案, 應該要放到版本管理工具中去的, 以便可以看到每次提交代碼時API的變化.

用@internal標注只希望在內部使用的class

比如, 我希望Bar類不能被此庫的使用者使用, 我可以加上下面這段注釋

/**
 *
 * @internal
 */
export class Bar {
  bar() {}
}

然后來看看生成的index.d.ts檔案:

/**
 *
 * @internal
 */
declare class Bar {
    bar(): void;
}

export declare class Foo {
    private _bar;
    constructor(_bar?: Bar);
    foo(): void;
    loaf(): void;
}

export { }

可以看出index.d.ts檔案中雖然declare了Bar, 但是并未匯出Bar

這個特性是由api-extractor提供的, 更多api-extractor的內容移步官方檔案

小結

至此, 我們就可以構建一個可以通過諸如AMD CommonJs esm等js模塊系統或是使用script標簽的方式參考的js庫了, 主要用到了webpack typescript api-extractor這些工具. 完整的示例代碼可以訪問github-laggage/loaf查看.

作者:Laggage

出處:https://www.cnblogs.com/laggage/p/build-js-library-with-webpack-and-typescript-and-api-extractor.html

說明:轉載請注明來源

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/498876.html

標籤:JavaScript

上一篇:圖文看懂JavaScritpt引擎V8與JS執行程序

下一篇:記錄--JavaScript原型和原型鏈復習筆記

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more