Webpack 進階之源碼分析(一)

新年新氣象,2020 開篇之做html

前置知識點

process.exitCode

Node API 文檔中明確寫到,process.exit() 方法以退出狀態 code 指示 Node.js 同步地終止進程。 若是省略 code,則使用成功代碼 0process.exitCode 的值(若是已設置)退出。node

require.resolve

用於從模塊名取到絕對路徑。webpack

readline

readline 模塊提供了一個接口,用於一次一行地讀取可讀流(例如 process.stdin)中的數據。git

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question('你如何看待 Node.js 中文網?', (answer) => {
  // TODO:將答案記錄在數據庫中。
  console.log(`感謝您的寶貴意見:${answer}`);

  rl.close();
});
複製代碼

import-local

打開 import-localindex.js 文件,代碼量十分的少。github

// 當前文件所在項目的跟目錄,根據 package.json 來判斷項目的根目錄所在位置
const globalDir = pkgDir.sync(path.dirname(filename));
// 返回 filename 相對於 globalDir 的路徑
const relativePath = path.relative(globalDir, filename);
const pkg = require(path.join(globalDir, 'package.json'));
// 找到本地模塊路徑
const localFile = resolveCwd.silent(path.join(pkg.name, relativePath));
// 若是本地模塊路徑與 filename 不一致,則使用本地模塊
return localFile && path.relative(localFile, filename) !== '' ? require(localFile) : null;
複製代碼

process.argv

process.argv 屬性返回一個數組,其中包含當啓動 Node.js 進程時傳入的命令行參數。第一個參數爲 process.execPath 啓動 Node.js 進行的可執行文件的絕對路徑名,第二個參數爲正在執行的 JavaScript 文件的路徑,剩餘元素是任何其餘命令行參數。web

process.cwd

process.cwd() 方法返回 Node.js 進程的當前工做目錄。數據庫

yargs

yargs 解決如何處理命令行參數,其提供 argv 對象,用來讀取命令行參數。npm

#!/usr/bin/env node
var argv = require('yargs').argv;

console.log('hello ', argv.name);
複製代碼

使用時,下面兩種用法均可以。json

$ hello --name=tom
hello tom

$ hello --name tom
hello tom
複製代碼

涉及 API

  • usage 設置命令的提示的使用方法。api

  • help 設置幫助信息,添加 --help,可是沒有 -h,須要手動添加。

  • alias 設置別名,好比指定 namen 的別名。

    #!/usr/bin/env node
    var argv = require('yargs')
      .alias('h', 'help')
      .argv;
    複製代碼
  • version 添加版本顯示參數 --version,不過不添加縮寫參數。

  • options 批量設置參數。

    require('yargs')
      .options({
        'run': {
          alias: 'r', // 別名
          describe: 'run your program', // 描述
          demandOption: true // 是否必填
        },
        'path': {
          alias: 'p',
          describe: 'provide a path to file',
          demandOption: true
        }
      })
      .help()
      .argv
    複製代碼

    設置完成後,當你打印幫助信息時。

path.relative(from, to)

path.relative() 方法根據當前工做目錄返回 fromto 的相對路徑。 若是 fromto 各自解析到相同的路徑(分別調用 path.resolve() 以後),則返回零長度的字符串。

path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');
// 返回: '../../impl/bbb'
複製代碼

入口文件

你們可能並不陌生,當咱們在執行 webpack XX,會去本地項目文件的 node_modeles/bin 文件下查找 webpack 命令。因此,首先打開 node_modeles/bin 下的 webpack 文件。 入口文件由 4 個部分組成,分別爲 process.exitCoderunCommandCLI 數組判斷某 CLI 是否安裝

process.exitCode

process.exitCode 爲 0,表明程序正常運行;非 0,則表明程序報錯。

runCommand

此函數會啓動一個子進程,並執行輸入命令。

CLI 數組

定義了一個 CLI 數組,數組共有 2 項,分別爲 webpack-cliwebpack-command

  • webpack-cli

webpack-cli 包含了全部的 webpack 特性,通常推薦使用這個包

  • webpack-command

webpack-command 只是一個輕量的 webpack 包。

判斷某 CLI 是否安裝

  • 若是兩個 CLI 均未安裝,則會提示必須安裝一個 CLI。當你輸入 yes 並回車後,會經過上文定義的 runCommand 函數進行安裝。固然,webpack 會判斷當前應該使用 npm,仍是使用 yarn。安裝完成後,並 reuqire 進來。

  • 若是隻安裝一個,程序正常執行。這裏以 webpack-cli 舉例。

    首先經過 require.resolve 取到 webpack-cli 存放的絕對路徑,好比取到的路徑爲 /Users/XXX/Works/Demo/node_modules/webpack-cli/package.json,並把包 require 進來。而後根據以前取到的 webpack-cli 路徑執行到 webpack-cli/bin/cli.js 文件,文件內具體邏輯,此處不做講解。

    const path = require("path");
    const pkgPath = require.resolve(`${installedClis[0].package}/package.json`);
    const pkg = require(pkgPath);
    require(path.resolve(
    	path.dirname(pkgPath),
    	pkg.bin[installedClis[0].binName]
    ));
    複製代碼
  • 若是兩個都安裝,webpack 會提示須要刪除其中一個,才能夠正常運行。

總結

Webpack-cli

打開 node_modules/webpack-cli/bin/cli.js 文件,咱們首先看到下面這個代碼片斷。webpack 並不會編譯執行全部的命令,它會針對不須要編譯的 cmd,執行 ./utils/prompt-command 文件方法。

const NON_COMPILATION_CMD = process.argv.find(arg => {
     if (arg === 'serve') {
        global.process.argv = global.process.argv.filter(a => a !== 'serve');
        process.argv = global.process.argv;
    }
    return NON_COMPILATION_ARGS.find(a => a === arg);
});
if (NON_COMPILATION_CMD) {
    return require('./utils/prompt-command')(NON_COMPILATION_CMD, ...process.argv);
}
複製代碼

不須要編譯的 CMD 列表位於文件 ./utils/constants` 下 `NON_COMPILATION_ARGS 數組中。

./utils/prompt-command 文件往外暴露一個方法 promptForInstallation。方法內部首先會去查詢當前項目是否安裝 @webpack-cli/ + 外界傳入的 NON_COMPILATION_CMD,若是未安裝,便提示安裝,安裝後執行 runWhenInstalled;已安裝,則執行 runWhenInstalled

回到 cli.js 文件中,咱們能夠繼續往下看。代碼中引入 yargs,並定義了幫助信息,並把大部分幫助信息放在 ./config/config-yargs 文件中。進入 ./config/config-yargs 文件,不知你們是否留意到,每一項都設置了 group 分組,group 的值都來自 ../utils/constants 文件。webpack 共有 8 個分組的幫助信息,加上默認的分組,總共有 9 個分組。

繼續往下看,咱們調用yargs.parse 方法,處理命令行參數。第一個參數傳入已去除 process.argv 數組前兩項,也就是傳入全部命令行參數,第二個參數爲一個回調函數。

yargs.parse(process.argv.slice(2), (err, argv, output) => {})
複製代碼

定義變量 options,此變量存儲了把全部命令轉換成 webpack 配置參數的值。把通過處理的 options,經過調用 webpack(options) 生成 compiler。若是命令行參數傳入的是 watch,則調用 compiler.watch,不然調用 compiler.run

總結

參考連接

相關文章
相關標籤/搜索