用 yeoman 打造自己的專案腳手架

迅雷前端發表於2018-08-20

作者: 珈藍 from 迅雷前端

原文地址: greenfavo.github.io/blog/docs/0…

當新建專案的時候,我們通常需要設計目錄結構、配各種配置、處理打包編譯,而且每次新建都要重來一遍,或把原來的專案 copy 一份再改改。那能不能自己寫個模板,然後還可以支援個性化自動建立呢?今天我就來和大家一起分享如何定製一套自己的專案腳手架,提升開發效率。

這裡需要引入腳手架的概念,什麼是腳手架呢?腳手架如同一個專案的模板,可以幫我們快速開始專案,就像 vue-cli,提供一個終端的互動介面讓使用者自定義專案內容。

yeoman 介紹

Yeoman 是一個通用的腳手架系統,允許建立任何型別的應用程式(Web,Java,Python,C#等)。用 yeoman 寫腳手架非常簡單, yeoman 提供了 yeoman-generator 讓我們快速生成一個腳手架模板,我們的主要工作就是把模板檔案寫好。現在我們來用 yeoman 寫一個生成 javascript 外掛的腳手架吧。

腳手架功能:

  • 自動構建編譯和打包
  • 支援 es6 語法
  • 支援單元測試
  • 支援 jsdoc 生成文件
  • 支援 eslint 語法檢查
  • 自動生成 changelog

準備工作

首先需要全域性安裝 yo 和 generator-generator

npm install yo -g
npm install generator-generator -g
複製程式碼

生成腳手架模板

yo generator
複製程式碼

用 yeoman 打造自己的專案腳手架

在這個終端介面裡輸入專案名、描述等專案資訊。注意專案名稱要寫成generator-xxx的格式,這樣使用者就可以通過yo xxx安裝你的腳手架了。

生成的腳手架模板目錄結構如下:

.
├── generators/
│   └── app/
│       ├── index.js
│       └── templates/
│           └── dummyfile.txt
├── .editorconfig
├── .eslintignore
├── .gitattributes
├── .gitignore
├── .travis.yml
├── .yo-rc.json
├── LICENSE
├── README.md
├── package.json
└── __tests__/
    └── app.js
複製程式碼

接下來我們就在generators/app/index.js裡寫腳手架的邏輯。

編寫自己的腳手架

腳手架所做的事情:

  • 接收使用者輸入
  • 根據使用者輸入生成模板檔案
  • 將模板檔案拷貝到目標目錄(通常是使用者執行腳手架的目錄)
  • 安裝依賴

yeoman 提供了一個基本生成器,你可以擴充套件它以實現自己的行為。這個基礎生成器將幫你減輕大部分工作量。在生成器的 index.js 檔案中,以下是擴充套件基本生成器的方法:

var Generator = require("yeoman-generator");

module.exports = class extends Generator {};
複製程式碼

yeoman 生命週期函式執行順序如下:

  • initializing - 初始化函式
  • prompting - 接收使用者輸入階段
  • configuring - 儲存配置資訊和檔案
  • default - 執行自定義函式
  • writing - 生成專案目錄結構階段
  • conflicts - 統一處理衝突,如要生成的檔案已經存在是否覆蓋等處理
  • install - 安裝依賴階段
  • end - 生成器結束階段

我們常用的就是 initializing、prompting、default、writing、install 這四種生命周期函式。看下例子:

"use strict";
const Generator = require("yeoman-generator");
const chalk = require("chalk"); // 讓console.log帶顏色輸出
const yosay = require("yosay");
const mkdirp = require("mkdirp"); // 建立目錄

module.exports = class extends Generator {
  initializing() {
    this.props = {};
  }
  
  // 接受使用者輸入
  prompting() {
    // Have Yeoman greet the user.
    this.log(
      yosay(
        `Welcome to the grand ${chalk.red(
          "generator-javascript-plugin"
        )} generator!`
      )
    );

    const prompts = [
      {
        type: "confirm",
        name: "someAnswer",
        message: "Would you like to enable this option?",
        default: true
      }
    ];

    return this.prompt(prompts).then(props => {
      // To access props later use this.props.someAnswer;
      this.props = props;
    });
  }

  // 建立專案目錄
  default() {
    if (path.basename(this.destinationPath()) !== this.props.name) {
      this.log(`\nYour generator must be inside a folder named
        ${this.props.name}\n
        I will automatically create this folder.\n`);

      mkdirp(this.props.name);
      this.destinationRoot(this.destinationPath(this.props.name));
    }
  }
  // 寫檔案
  writing() {
    // 將templates目錄的程式碼拷貝到目標目錄
    // templates目錄預設路徑是generators/app/templates
    this.fs.copy(
      this.templatePath("dummyfile.txt"),
      this.destinationPath("dummyfile.txt")
    );
    this._writingPackageJSON();
  }

  // 以下劃線_開頭的是私有方法
  _writingPackageJSON() {
    // this.fs.copyTpl(from, to, context)
    this.fs.copyTpl(
      this.templatePath("_package.json"),
      this.destinationPath("package.json"),
      {
        name: this.props.name,
        description: this.props.description,
        keywords: this.props.keywords.split(","),
        author: this.props.author,
        email: this.props.email,
        repository: this.props.repository,
        homepage: this.props.homepage,
        license: this.props.license
      }
    );
  }

  // 安裝依賴
  install() {
    this.installDependencies();
  }
};
複製程式碼

編寫模板程式碼

前面我們把一個腳手架的基本框架都寫好了,它可以接受使用者輸入的內容,可以寫檔案,可以安裝依賴,但接收使用者輸入的資料怎麼用?寫進什麼檔案?安裝什麼依賴呢?這些都是模板檔案做的事情。現在就開始最主要的一部分:編寫模板檔案。

模板檔案是你為使用者生成的一個專案 demo,讓使用者看著這些示例程式碼就可以開工了,使用者應該只需要專注於業務邏輯,而不用管打包構建這些事。

首先建好模板目錄:

├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .babelrc
├── jsdoc.json
├── README.md
├── package.json
├── build/
    └── rollup.js
├── src/
    └── index.js
├── test/
    └── index.js
複製程式碼

我們的模板package.json裡已經寫好這些命令:

"scripts": {
  "prebuild": "npm run lint && npm run test && npm run doc",
  "build": "node ./build/rollup.js",
  "lint": "eslint --ext .js, src",
  "test": "mocha --require babel-register --require babel-polyfill --bail",
  "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
  "doc": "jsdoc -c ./jsdoc.json"
}
複製程式碼
  • npm run lint 用 eslint 進行語法檢查,在編譯前就避免語法錯誤和統一程式碼風格。
  • npm test 執行單元測試
  • npm run doc 根據註釋生成文件
  • npm run changelog 根據git log生成專案日誌,改動記錄一目瞭然
  • npm run prebuild 編譯前的語法檢查、 執行測試、生成文件
  • npm run build 編譯打包

我們可以使用<%= name %>這樣的模板語法使用腳手架中的context上下文,無論是使用者輸入的資料,還是程式自己的變數:

{
  "name": "<%= name %>",
  "description": "<%= description %>",
  "version": "1.0.0",
  "private": false,
  "main": "dist/<%= name %>.umd.js",
  "module": "dist/<%= name %>.es.js"
}
複製程式碼

詳細程式碼請到github檢視。

執行測試用例

為了保證程式碼的健壯性,我們必須進行單元測試。其實我們用generator生成的腳手架程式碼中已經有測試程式碼示例了,改成自己的邏輯就可以測試我們的腳手架邏輯了。現在我們來測試下檔案是否生成:

'use strict';
const path = require('path');
const assert = require('yeoman-assert');
const helpers = require('yeoman-test');

describe('generator-javascript-plugin:app', () => {
  beforeAll(() => {
    return helpers
      .run(path.join(__dirname, '../generators/app'))
      .withPrompts({ someAnswer: true });
  });

  it('creates files', () => {
    assert.file(['build/rollup.js']);
    assert.file(['dist']);
    assert.file(['src']);
    assert.file(['test']);
    assert.file(['package.json']);
    assert.file(['.babelrc']);
    ...
  });
});
複製程式碼

執行命令

npm test
複製程式碼

用 yeoman 打造自己的專案腳手架

執行腳手架

到此,我們的腳手架開發完了,接下來實際執行下看看是否正確。

由於我們的腳手架還是本地開發,它尚未作為全域性 npm 模組提供。我們可以使用 npm 建立全域性模組並將其符號連結到本地模組。在專案根目錄執行:

npm link
複製程式碼

這樣就可以呼叫yo javascript-plugin執行腳手架了。你可以在終端看到執行過程。

用 yeoman 打造自己的專案腳手架

釋出

寫好的腳手架釋出出去才能讓更多的人使用,釋出腳手架和釋出其他 npm 包一樣。如何釋出?

github 地址

generator-javascript-plugin這個腳手架已經發布到npm上,可以下載或訪問原始碼。

原始碼地址:github.com/greenfavo/g…

掃一掃關注迅雷前端公眾號

用 yeoman 打造自己的專案腳手架

相關文章