The Will Will Web | 發行 Angular Schematics 程式產生器與設定 .npmignore 的注意事項

The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

發行 Angular Schematics 程式產生器與設定 .npmignore 的注意事項

昨天寫了一個小工具,可以自動將現有 Angular CLI 建立的專案,轉換成有 Hot Module Replacement (HMR) 功能的版本,只要簡單一個命令 ng add hmr-enabled 就可以搞定,非常方便。不過我在發布專案到 npm registry 的過程中,卻意外遺漏了幾個重要的檔案,原來是有檔案被 .npmignore 給忽略掉了,到了今天傍晚才修復。我們就立刻來看看這個錯誤��怎樣發生的!

建立 Angular Schematics 專案

這邊我就先示範一個簡單的 Schematics 程式產生器設計過程。

  1. 安裝開發工具

    npm install -g @angular-devkit/schematics-cli
    
  2. 建立專案骨架

    schematics blank --name=willh
    

    如果你想建立一個含有範例程式的專案,也可以考慮用以下命令建立範本:

    schematics schematics --name=willh-with-example
    

    上述兩種語法,也有另外一種寫法,不過比原本的指令會長很多:

    schematics @schematics/schematics:blank     --name my-schematics-1
    schematics @schematics/schematics:schematic --name my-schematics-2
    
  3. 安裝 npm 套件

    cd willh
    npm install
    

建立第一支程式碼產生器

這邊我來示範一個可以自動產生 /src/environments/environment.{name}.ts 檔案的程式產生器,而且只要執行 ng g willh:env hmr 就會自動產生 /src/environments/environment.hmr.ts 檔案。我們第一版先不用做太複雜,產生固定的內容就好。

  1. 檢查 package.json 所定義的 schematics 設定,預設會指向 ./src/collection.json 這個檔案。

  2. 調整 src/collection.json 檔案內容如下:

    {
      "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
      "schematics": {
        "env": {
          "description": "generates environment file",
          "factory": "./env/index#env",
          "schema": "./env/schema.json"
        }
      }
    }
    
  3. 建立 src/env/schema.json 檔案,讓程式可以接受 name 參數,並且讓 name 參數直接接在參數列上,不用特別透過 --name 選項進行設定。檔案內容如下:

    {
      "$schema": "http://json-schema.org/schema",
      "id": "EnvSchema",
      "title": "Env Schematics Schema",
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "description": "The name of the environment.",
          "$default": {
            "$source": "argv",
            "index": 0
          }
        }
      }
    }
    
  4. 建立 src/env/index.ts 檔案內容如下:

    import {
      Rule, mergeWith, template, apply, url
    } from '@angular-devkit/schematics';
    
    export function env(options: any): Rule {
      return () => {
        return mergeWith(
          apply(url('./files'), [
            template({
              name: options.name
            })
          ])
        );
      };
    }
    
  5. 建立一個 src/env/files/src/environments/environment.__name__.ts 檔案,檔名的部分 __name__ 會在產生檔案的時候,動態置換成 name 參數的內容。

    // This file can be replaced during build by using the `fileReplacements` array.
    // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
    // The list of file replacements can be found in `angular.json`.
    
    export const environment = {
      production: false
    };
    
    /*
    * In development mode, for easier debugging, you can ignore zone related error
    * stack frames such as `zone.run`/`zoneDelegate.invokeTask` by importing the
    * below file. Don't forget to comment it out in production mode
    * because it will have a performance impact when errors are thrown
    */
    // import 'zone.js/dist/zone-error';  // Included with Angular CLI.
    
  6. 建置專案

    npm run build
    
  7. 本地測試安裝

    npm link
    

    執行 npm link 會將目前專案自動連結到 npm 的全域模組中。

  8. 本地測試執行

    透過全域的 schematics 工具就可以執行這個命令。如下範例命令執行完後,就會自動產生 /src/environments/environment.ok.ts 檔案:

    schematics willh:env ok
    
  9. 在 Angular CLI 專案中透過 ng generate 進行測試

    要在特定專案中執行 ng generate 的話,必須透過本地 npm 安裝才能使用。你可以直接透過 npm install <folder> 直接將開發中的目錄,當成套件安裝到目前的 Angular 開發專案目錄中。

    npm install G:\Projects\willh
    

    安裝好之後,就可以順利的使用 ng g willh:env ok 命令來產生檔案。

準備發行 npm 套件

一切準備就緒的時候,就是魔鬼出現的時刻!

我今天就是發現到,透過 schematics blank 產生的專案範本,預設所建立的 .npmignore 檔案是有問題的。這份檔案,主要是將 *.ts 檔案從發行檔案中排除,只留下 TypeScript 編譯後的 *.js 檔。但留下所有 *.d.ts 模組定義檔。檔案內容如下:

# Ignores TypeScript files, but keeps definitions.
*.ts
!*.d.ts

這個 .npmignore 檔案,最主要的目的,就是讓 npm 套件在發布的時候,將特定檔案/目錄排除在發行的檔案清單中。正常來說,排除掉 *.ts 與保留 *.d.ts 是蠻合理的。但在我們本次建立的 Angular Schematics 專案中,有個 src/env/files/ 目錄,裡面的檔案預設會原封不動的合併到目的專案中,所以無論如何,這裡面的檔案,都是不能被排除的。

原則上來說,.npmignore 的內容格式與 .gitignore 完全一致,應該不會太難理解才對。我從發現這個問題後,就做出了幾種不同的嘗試,但是都是不對的,感覺頗地雷,各位要小心!

首先,先教大家怎樣在 npm 發行之前,如何才能預先知道有哪些套件會上傳到 npm registry 之中。這個命令,就是:

npm pack

這個命令執行完後,預設就會將所有要發行的檔案,打包成 {name}-{version}.tgz 的壓縮檔,你從壓縮檔案中,就可以得知會發布哪些檔案,不需要等 npm publish 之後才進行測試。

以下我列出幾個錯誤的嘗試:

  1. 嘗試將 /src/env/files/ 目錄排除在忽略清單外,避免 /src/env/files/ 目錄下的 *.ts 檔被排除掉了!(失敗)

    *.ts
    !*.d.ts
    !/src/env/files/
    
  2. 由於設定完全無效,所以調整一下 /src/env/files/ 的順序到第一順位,但依然無效!(失敗)

    !/src/env/files/
    *.ts
    !*.d.ts
    

到最後,我終於理解到一件官網文件沒有寫的細節。那就是 如果你將特定檔案類型加入到忽略清單裝,如果要透過 ! 再把檔案加回來,那就必須要寫成針對定檔案進行描述,不能直接透過「特定目錄」進行設定

最後,正確的寫法如下:

*.ts
!*.d.ts
!src/*/files/**/*

寫成這樣也可以:

*.ts
!*.d.ts
!src/**/files/**/*

是不是很雷!真的遇到了才會知道啊!

相關連結