Nodejs開闢簡樸的腳手架東西

腳手架,這個名詞關於作為前端的我們來講,或許並不生疏吧,像vue-cli,react-native-cli等,全局裝置后,只須要在敕令行中敲入一個簡樸的敕令,便可幫我們疾速的天生一個初始項目,如vue init webpack projectName,即可天生一個初始的vue項目。
本文主假如引見開闢一個簡樸的腳手架,相識開闢的基礎流程、終究經由歷程npm link鏈接到全局包。
<!– more –>

腳手架的作用

1、削減時刻,沒必要從零最先搭建初始項目,進步開闢效力。
2、便於多人合作。
3、項目更新同步輕易,只須要更新代碼庫中項目模板,即可下載最新的項目。

node相干基礎知識

在最先項如今,先簡樸引見下node相干的一些基礎知識,經由歷程npm init初始化一個node項目時,會天生一個package.json的設置文件,包含項目稱號、版本、作者、依靠等相干信息,主要說一下个中的bin字段。
許多包都有一個或多個可實行的文件,願望放在PATH中,(現實上,就是這個功用讓npm可實行的)。
當你要用這個功用時,須要給package.json中的bin字段增添一個敕令名,並指向須要實行的文件(即後文的進口文件)。初始化的時刻npm會將他鏈接到prefix/bin(全局初始化)或許./node_modules/.bin/(當地初始化)。
比方,npm有:

  { "bin" : { "npm" : "./npm-cli.js" } }

所以,當你初始化npm,它會建立一個標記鏈接到npm-cli.js劇本到/usr/local/bin/npm。

假如你只要一個可實行文件,而且名字和包名一樣。那末你能夠只用一個字符串,比方:

  { "name": "my-program"
  , "version": "1.2.5"
  , "bin": "./path/to/program" }

結果和這個一樣:

  { "name": "my-program"
  , "version": "1.2.5"
  , "bin" : { "my-program" : "./path/to/program" } }

想要相識package.json更多的細緻設置,請參考這篇文章

思緒

要開闢腳手架,起首要理清思緒,腳手架是怎樣事情的?我們能夠自創 vue-cli 的基礎思緒。vue-cli 是將項目模板放在 git 上,運轉的時刻再依據用戶交互下載差別的模板,經由模板引擎襯着出來,天生項目。如許將模板和腳手架星散,就能夠各自保護,縱然模板有變動,只須要上傳最新的模板即可,而不須要用戶去更新腳手架就能夠天生最新的項目。那末就能夠根據這個思緒來舉行開闢了。

初始化項目

新建一個文件夾,翻開敕令行東西,經由歷程npm init 舉行項目初始化,會在項目根目次下天生package.json文件。

  npm init

裝置依靠

  npm install commander download-git-repo inquirer handlebars ora chalk log-symbols shelljs -S

除此以外,還運用了nodejs的幾個內置模塊:fs、path、child_process
commander.js:能夠自動的剖析敕令和參數,用於處置懲罰用戶輸入的敕令。

download-git-repo:下載並提取 git 堆棧,用於下載項目模板。

Inquirer.js:通用的敕令行用戶界面鳩合,用於和用戶舉行交互。

handlebars.js:模板引擎,將用戶提交的信息動態填充到文件中。

ora:下載歷程久的話,能夠用於顯現下載中的動畫結果。

chalk:能夠給終端的字體加上色彩。

log-symbols:能夠在終端上顯現出 √ 或 × 等的圖標。

fs:node內置的文件處置懲罰模塊。

path:node內置的途徑處置懲罰、剖析模塊。

child_process:node中建立子歷程模塊。

設置進口文件

1、在package.json文件中增添bin字段,臨時先命名為okcli吧。

  "bin": {
    "okcli": "./index.js"
  }

2、在項目根目次新建index.js文件,在index.js文件頂部增添以下代碼:

  #!/usr/bin/env node

即可舉行開闢。
在這裏,簡樸引見下這行代碼寄義。
<font color=#f00>
!/usr/bin/node是通知操縱體系實行這個劇本的時刻,挪用/usr/bin下的node詮釋器;
!/usr/bin/env node這類用法是為了防備操縱體系用戶沒有將node裝在默許的/usr/bin途徑里。當體系看到這一行的時刻,起首會到env設置里查找node的裝置途徑,再挪用對應途徑下的詮釋器順序完成操縱。
!/usr/bin/node相當於寫死了node途徑;
!/usr/bin/env node會去環境設置尋覓node目次,引薦這類寫法
</font>

處置懲罰敕令行

  const program = require('commander');
  const inquirer = require('inquirer');
  const symbols = require('log-symbols');
  const download = require('download-git-repo');
  const handlebars = require('handlebars');
  const chalk = require('chalk');
  const ora = require('ora');
  const shell = require('shelljs');
  const child_process = require('child_process');
  const fs = require('fs');
  const path = require('path');
  program.version('1.0.0', '-v, --version')
  .command('init <name>')
  .action((name) => {
    console.log(name);
  });
  program.parse(process.argv);

挪用program.version(‘1.0.0’, ‘-v, –version’)會將-v和–version增添到敕令行中,挪用時可經由歷程帶上該參數獵取該腳手架的版本號(敕令 -v/–version),挪用comand(‘init <name>’)定義初始化敕令,name參數必傳,作為項目的文件夾名,如vue init webpack parojectName
action是實行command敕令時發作的回調,參數為敕令行中輸入的name,即init <name>中的name,項目天生歷程便發作在回調函數中。
如今能夠經由歷程挪用node index.js init test,能夠看到掌握台中已打印了輸入的項目名,也就是test。
个中:program.parse(process.argv)剖析敕令行中的參數,剖析出name,並傳入action回調。

下載模板

經由歷程download-git-repo或許直接運用shelljs或許child_process直接運轉敕令舉行下載模塊(個人挑選的是第三種)。
download-git-repo 支撐從 Github、Gitlab 和 Bitbucket 下載堆棧,各自的詳細用法能夠參考官方文檔。

  download('https://github.com/jefferyE/webpack-configuration-for-vue', name, {clone: true}, (err) => {
    if(err){
     // spinner.fail();
      console.log(symbols.error, chalk.red(err));
    }else{
     // spinner.fail();
    }
  })

或許

  if (shell.exec('git clone https://github.com/jefferyE/webpack-configuration-for-vue').code == 0) {
   // spinner.succeed();
  } else {
   // spinner.fail();
    console.log(symbols.error, chalk.red('模板下載失利'))
  }

或許

  child_process.exec('git clone https://github.com/jefferyE/webpack-configuration-for-vue', function(err, stdout, stderr) {
    if (err) {
    //  spinner.fail();
      console.log(symbols.error, chalk.red('模板下載失利'))
    } else {
    //  spinner.succeed();
    }
  })

个中:download() 第一個參數就是堆棧地點,但是有一點點不一樣。現實的堆棧地點是 https://github.com/jefferyE/w… ,能夠看到端口號背面的 ‘/‘ 在參數中要寫成 ‘:’,#master 代表的就是分支名,差別的模板能夠放在差別的分支中,變動分支便能夠完成下載差別的模板文件了。第二個參數是途徑,上面我們直接在當前途徑下建立一個 name 的文件夾寄存模板,也能夠運用二級目次比方 test/${name}

交互敕令行

敕令行交互功用能夠在用戶實行 init 敕令后,向用戶提出題目,吸收用戶的輸入並作出響應的處置懲罰。這裏運用 inquirer.js 來完成,也能夠本身運用node內置的readline模塊。
詳細能夠參考我的這篇文章

  inquirer.prompt([
      {
          type: 'input',
          name: 'author',
          message: '請輸入作者稱號'
      }
  ]).then((answers) => {
      console.log(answers.author);
  })

經由歷程這裏例子能夠看出,題目就放在 prompt() 中,題目的範例為 input 就是輸入範例,name 就是作為答案對象中的 key,message 就是題目了,用戶輸入的答案就在 answers 中,運用起來就是這麼簡樸。更多的參數設置能夠參考官方文檔。

襯着package模板

這裏用 handlebars 的語法對 HTML5/H5Template 堆棧的模板中的 package.json 文件做一些修正。並在下載模板完成以後將用戶輸入的答案襯着到 package.json 中。

  {
    "name": "{{name}}",
    "version": "1.0.0",
    "description": "{{description}}",
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "{{author}}",
    "license": "ISC"
  }

注重:因為我的項目沒有變動package模板,因而經由歷程在下載模板后,剖析時舉行動態修正的。
除此以外,經由歷程ora、chalk模塊也舉行了一些視覺美化。詳細請參考完全代碼。
完全代碼以下:

  #!/usr/bin/env node

  const program = require('commander');
  const chalk = require('chalk');
  const ora = require('ora');
  const fs = require('fs');
  const inquirer = require('inquirer');
  const shell = require('shelljs');
  const symbols = require('log-symbols');
  const download = require('download-git-repo');
  const child_process = require('child_process');
  const handlebars = require('handlebars');
  const path = require('path');

  program.version('1.0.0', '-v, --version').
  command('init <name>').
  action(name => {
    console.log(name);
    if (!fs.existsSync(name)) {
      console.log('正在建立項目...');
      inquirer.prompt([
        {
            name: 'description',
            message: '請輸入項目形貌'
        },
        {
            name: 'author',
            message: '請輸入作者稱號'
        }
      ]).then(answers => {
        console.log(answers)
        const spinner = ora('正在向下載模板...\n');
        spinner.start();
        child_process.exec('git clone https://github.com/jefferyE/webpack-configuration-for-vue', function(err, stdout, stderr) {
          if (err) {
            spinner.fail();
            console.log(symbols.error, chalk.red('模板下載失利'))
          } else {
            spinner.succeed();
            shell.mv(__dirname + '/webpack-configuration-for-vue', __dirname + '/' + name)
            const filename = `${name}/package.json`;
            const meta = {
              name,
              description: answers.description,
              author: answers.author
            }
            if (fs.existsSync(filename)) {
              const content = fs.readFileSync(filename).toString();
              let dt = JSON.parse(content);
              dt.name = '{{name}}';
              dt.description = '{{description}}'
              const result = handlebars.compile(JSON.stringify(dt, null, 2))(meta);
              fs.writeFileSync(filename, result);
              console.log(symbols.success, chalk.green('項目初始化完成'));
            } else {
              console.log(symbols.error, chalk.red('package不存在'))
            }
          }
        })
      })
    } else {
      console.log(symbols.error, chalk.red('項目已存在'));
    }
  })
  program.parse(process.argv);

个中:
1、在用戶輸入答案以後,最先下載模板,這時刻運用 ora 來提醒用戶正在下載中。
2、然後經由歷程 chalk 來為打印信息加上款式,比方勝利信息為綠色,失利信息為赤色,如許子會讓用戶越發輕易區分,同時也讓終端的顯現越發的悅目。
3、除了給打印信息加上色彩以外,還能夠運用 log-symbols 在信息前面加上 √ 或 × 等的圖標。

完成以後,就能夠把腳手架宣布到 npm 上面,經由歷程 -g 舉行全局裝置,就能夠在本身本機上實行 okcli init [name] 來初始化項目,如許便完成了一個簡樸的腳手架東西了。

鏈接全局敕令

在根目次運轉 npm link敕令,將包鏈接到全局環境。
npm link敕令能夠將一個恣意位置的npm包鏈接到全局實行環境,從而在恣意位置運用敕令行都能夠直接運轉該npm包。
詳細請參考這篇文章

謝謝:
參考文章  文章1
                文章2

    原文作者:Jeffery
    原文地址: https://segmentfault.com/a/1190000015271651
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞