正式開始跑編譯,依次解析,首先是: 流程圖如下: 這裡是第一個compilation事件註入的地方,註入代碼如下: 這裡的requestShortener為FunctionModulePlugin的第二個參數,沒有傳所以是undefined。 options.output為傳入的output參數,但 ...
正式開始跑編譯,依次解析,首先是:
compiler.apply( new JsonpTemplatePlugin(options.output), // start new FunctionModulePlugin(options.output), new NodeSourcePlugin(options.node), new LoaderTargetPlugin(options.target) );
流程圖如下:
這裡是第一個compilation事件註入的地方,註入代碼如下:
compiler.plugin("compilation", (compilation) => { compilation.moduleTemplate.requestShortener = this.requestShortener || new RequestShortener(compiler.context); compilation.moduleTemplate.apply(new FunctionModuleTemplatePlugin()); });
這裡的requestShortener為FunctionModulePlugin的第二個參數,沒有傳所以是undefined。
options.output為傳入的output參數,但是這裡並沒有用到,而是傳入了compiler.context,如果沒有傳預設為命令執行路徑。
RequestShortener
首先看第一個,源碼簡化如下:
"use strict"; const path = require("path"); // 匹配反斜杠 => \ const NORMALIZE_SLASH_DIRECTION_REGEXP = /\\/g; // 匹配特殊字元 const PATH_CHARS_REGEXP = /[-[\]{}()*+?.,\\^$|#\s]/g; // 匹配正反斜杠 => /\ const SEPARATOR_REGEXP = /[/\\]$/; // 匹配以'!'開頭或結尾 const FRONT_OR_BACK_BANG_REGEXP = /^!|!$/g; // 匹配 /index.js const INDEX_JS_REGEXP = /\/index.js(!|\?|\(query\))/g; // 將反斜杠替換為正斜杠 const normalizeBackSlashDirection = (request) => { return request.replace(NORMALIZE_SLASH_DIRECTION_REGEXP, "/"); }; // 將路徑中特殊字元轉義 例如 - => \- // 返回一個正則 const createRegExpForPath = (path) => { const regexpTypePartial = path.replace(PATH_CHARS_REGEXP, "\\$&"); return new RegExp(`(^|!)${regexpTypePartial}`, "g"); }; class RequestShortener { constructor(directory) { /**/ } shorten(request) { /**/ } } module.exports = RequestShortener;
可以看到都是對路徑做處理,正則都比較簡單,接下來看一下構造函數,其中傳進來的directory為命令執行上下文。
class RequestShortener { constructor(directory) { // 斜杠轉換 directory = normalizeBackSlashDirection(directory); // 沒看懂啥用 if (SEPARATOR_REGEXP.test(directory)) directory = directory.substr(0, directory.length - 1); // 上下文路徑正則 // /(^|!)轉義後的路徑/g if (directory) { this.currentDirectoryRegExp = createRegExpForPath(directory); } // 返回目錄名 const dirname = path.dirname(directory); // 這裡也不懂幹啥用的 const endsWithSeperator = SEPARATOR_REGEXP.test(dirname); const parentDirectory = endsWithSeperator ? dirname.substr(0, dirname.length - 1) : dirname; // 目錄正則 if (parentDirectory && parentDirectory !== directory) { this.parentDirectoryRegExp = createRegExpForPath(parentDirectory); } // .....\node_modules\webpack\lib if (__dirname.length >= 2) { // webpack的目錄 const buildins = normalizeBackSlashDirection(path.join(__dirname, "..")); // 目錄檢測 const buildinsAsModule = this.currentDirectoryRegExp && this.currentDirectoryRegExp.test(buildins); // false this.buildinsAsModule = buildinsAsModule; // 生成webpack目錄路徑正則 this.buildinsRegExp = createRegExpForPath(buildins); } } shorten(request) { /**/ } }
主要是生成了3個目錄匹配正則,上下文、上下文目錄、webpack主目錄三個。
這裡上下文一般不會是webpack的目錄,所以這個buildingsAsModule理論上都是flase。
再簡單看一下原型方法shorten:
class RequestShortener { constructor(directory) { /**/ } shorten(request) { if (!request) return request; // 轉化路徑斜杠 request = normalizeBackSlashDirection(request); // false if (this.buildinsAsModule && this.buildinsRegExp) request = request.replace(this.buildinsRegExp, "!(webpack)"); // 將上下文轉換為!. if (this.currentDirectoryRegExp) request = request.replace(this.currentDirectoryRegExp, "!."); // 將上下文目錄轉換為!.. if (this.parentDirectoryRegExp) request = request.replace(this.parentDirectoryRegExp, "!.."); // false if (!this.buildinsAsModule && this.buildinsRegExp) request = request.replace(this.buildinsRegExp, "!(webpack)"); // 把路徑中的index.js去了 留下參數 // /index.js?a=1 => ?a=1 request = request.replace(INDEX_JS_REGEXP, "$1"); // 把頭尾的!去了 return request.replace(FRONT_OR_BACK_BANG_REGEXP, ""); } }
可以看出,這個方法將傳入的路徑根據上下文的目錄進行簡化,變成了相對路徑,然後去掉了index.js。
FunctionModuleTemplatePlugin
這個模塊沒有實質性內容,主要是對compilation.moduleTemplate註入事件流,源碼如下:
"use strict"; const ConcatSource = require("webpack-sources").ConcatSource; class FunctionModuleTemplatePlugin { apply(moduleTemplate) { moduleTemplate.plugin("render", function(moduleSource, module) { /**/ }); moduleTemplate.plugin("package", function(moduleSource, module) { /**/ }); moduleTemplate.plugin("hash", function(hash) { /**/ }); } } module.exports = FunctionModuleTemplatePlugin;
等觸發的時候再回頭看。
ConcatSource後面單獨講。
下麵是第二個插件,源碼整理如下:
class NodeSourcePlugin { constructor(options) { this.options = options; } apply(compiler) { const options = this.options; if (options === false) // allow single kill switch to turn off this plugin return; function getPathToModule(module, type) { /**/ } function addExpression(parser, name, module, type, suffix) { /**/ } compiler.plugin("compilation", function(compilation, params) { params.normalModuleFactory.plugin("parser", function(parser, parserOptions) { /**/ }); }); compiler.plugin("after-resolvers", (compiler) => { /**/ }); } };
可以看到,這裡只是簡單判斷了是否關閉了node插件,然後在之前的params參數中的normalModuleFactory屬性上註入了一個parser事件。
第三個插件就更簡單了,如下:
class LoaderTargetPlugin { constructor(target) { this.target = target; } apply(compiler) { compiler.plugin("compilation", (compilation) => { // 這個完全不懂幹啥的 compilation.plugin("normal-module-loader", (loaderContext) => loaderContext.target = this.target); }); } }
這個plugin目前根本看不出來有什麼用。
總之,前三個compilation比較水,沒有什麼內容。