Hello Webpack Plugin - AssetsCopier 插件開(kāi)發(fā)

Hello Webpack Plugin

Webpack Internal Plugin Relation
WebPack 插件開(kāi)發(fā)實(shí)例
Webpack API
Webpack Modules
File System
child_process
從Webpack源碼探究打包流程

Introduction

Webpack 插件開(kāi)發(fā)可以參考官方提供的 html-webpack-plugin集绰、 clean-webpack-pluginwebpack-manifest-plugin 這些插件作為參考,推薦使用 ES6 語(yǔ)法寫(xiě)插件,Webpack 2.0 就開(kāi)始支持 ES6聂示。

其次要了解 Webpack 內(nèi)部插件與鉤子關(guān)系就乓,參考[Webpack Internal Plugin Relation],了解 CompolationCompiler腹忽、Parser建芙、MainTemplate 基本對(duì)象結(jié)構(gòu)没隘。

Webpack 在執(zhí)行過(guò)程不同的階段執(zhí)行插件方法,開(kāi)始時(shí)執(zhí)行 apply(compiler) 方法會(huì)將當(dāng)前的編譯器實(shí)例傳入禁荸,編譯器包含了一組鉤子函數(shù) compiler.hooks 供插件使用右蒲。compiler.options 包含了 Webpack 的配置文件內(nèi)容,這些配置拼合了默認(rèn)的選項(xiàng)赶熟。

實(shí)列化 Webpack 時(shí)瑰妄,沒(méi)有設(shè)置回調(diào)函數(shù),Webpack 就會(huì)返回一個(gè) Compiler 實(shí)例:

const webpack = require('webpack');

const compiler = webpack({
  // Configuration Object
});

compiler.run((err, stats) => { // Stats Object
  // ...
});

const watching = compiler.watch({
  // Example watchOptions
  aggregateTimeout: 300,
  poll: undefined
}, (err, stats) => { // Stats Object
  // Print watch/build result here...
  console.log(stats);
});

基于 Node.js, Compiler 實(shí)例提供了基本的文件系統(tǒng) inputFileSystem映砖,outputFileSystem间坐,可以這樣來(lái)定制:

const MemoryFS = require('memory-fs');
const webpack = require('webpack');

const fs = new MemoryFS();
const compiler = webpack({ / options / });

compiler.outputFileSystem = fs;
compiler.run((err, stats) => {
  // Read the output later:
  const content = fs.readFileSync('...');
});

在使用 webpack-dev-server 開(kāi)發(fā)時(shí),就是使用 CachedInputFileSystemMemoryFileSystem邑退。Webpack 內(nèi)部還實(shí)現(xiàn)了幾個(gè)文件系統(tǒng) NodeWatchFileSystem竹宋、 NodeOutputFileSystemNodeJsInputFileSystem 用于不同的場(chǎng)景地技。對(duì)磁盤(pán)文件的讀寫(xiě)還是通過(guò) Node.js 提供的 [File System] 文件系統(tǒng)模塊實(shí)現(xiàn)的逝撬。

完成后要執(zhí)行的動(dòng)作通過(guò) compiler.hooks.done 鉤子來(lái)注冊(cè),Webpack 會(huì)將 Compilation 實(shí)例傳入乓土。compilation.hooks 也提供了一些鉤子函數(shù)宪潮。注冊(cè)鉤子的方法參考以下代碼溯警,不同鉤子回調(diào)函數(shù)的參數(shù)會(huì)有所差別:

compiler.hooks.someHook.tap('MyPlugin', (params) => {

});

compilation.hooks.buildModule.tap('MyPlugin',
  module => {
    module.useSourceMap = true;
  }
);

Webpack 的 Module 是模塊化編程的基本單元結(jié)構(gòu),支持的模塊類型有 CoffeeScript狡相、 TypeScript梯轻、 ESNext (Babel)、 Sass尽棕、 Less喳挑、 Stylus 等等,Webpack 系統(tǒng)的 Loader 也是模塊的一種滔悉。在 buildModule 鉤子中可以讀取 module.type伊诵、module.constructor 確定模塊類型。

compiler.hooks.done 鉤子在編譯完成時(shí)執(zhí)行回官,回調(diào)參數(shù)是一個(gè)統(tǒng)計(jì)信息對(duì)象曹宴,compilation 中有包含了 compiler 和編譯模塊 modules 信息:

{
    compilation: ...
    hash: '00354804c2f21f00c528',
    startTime: 1563516897225,
    endTime: 1563516905223
}

關(guān)鍵鉤子作用 鉤子類型 回調(diào)參數(shù) 解析
beforeRun AsyncSeriesHook Compiler 運(yùn)行前的準(zhǔn)備活動(dòng),主要啟用了文件讀取的功能歉提。
run AsyncSeriesHook Compiler 跑起來(lái)了笛坦,在編譯之前有緩存,則啟用緩存苔巨,這樣可以提高效率版扩。
beforeCompile AsyncSeriesHook params 開(kāi)始編譯前的準(zhǔn)備,創(chuàng)建的ModuleFactory侄泽,創(chuàng)建Compilation礁芦,并綁定ModuleFactory到Compilation上。
compile SyncHook params 編譯了
make AsyncParallelHook compilation 從Compilation的addEntry函數(shù)悼尾,開(kāi)始構(gòu)建模塊
afterCompile AsyncSeriesHook compilation 編譯結(jié)束了
shouldEmit SyncBailHook compilation 獲取compilation發(fā)來(lái)的電報(bào)柿扣,確定編譯時(shí)候成功,是否可以開(kāi)始輸出了诀豁。
emit AsyncSeriesHook compilation 輸出文件了
afterEmit AsyncSeriesHook compilation 輸出完畢
done AsyncSeriesHook Stats 無(wú)論成功與否窄刘,一切已塵埃落定窥妇。

Coding Now

現(xiàn)在就來(lái)寫(xiě)一個(gè)插件舷胜,用來(lái)將項(xiàng)目根目錄下的 index.html 模板拷貝到發(fā)布目錄下。利用 Shell 來(lái)拷貝 public 目錄的資源活翩。在 Node.js 中使用 child_process 執(zhí)行 shell 有三種方法烹骨,spawn,exec和execFile材泄。假定 Windows 系統(tǒng)沮焕,借用了 xcopy 命令來(lái)復(fù)制目錄結(jié)構(gòu)。同時(shí)使用了 iconv-lite 做默認(rèn)的 Windows 系統(tǒng) GB2312 編碼的默認(rèn)的 Node.js 編碼 UTF-8 的轉(zhuǎn)換拉宗。

const fs = require('fs');
const exec = require('child_process').execSync;
var iconv = require('iconv-lite');

class AssetsCopier{
  constructor(options){
    this.options = options || {};
    console.log('Plugin options', this.options);
  }

  apply(compiler){
    let path = compiler.options.output.path;
    let buf = exec(`xcopy ${process.cwd()}\\public ${path} /Y /S /I`, err => {
      if(err) console.log(err);
    });
    let str = iconv.decode(buf, "gb2312");
    console.log("? apply", str);
    // fs.copyFile(src, dest[, flags], callback)  
    fs.readFile(process.cwd()+'/index.html', 'utf8', function (err, data) {
      if (err) return console.log("HTML Template not found "+err.path);
      fs.writeFile(path+'/index.html', data, err => {
        console.log( err? "Fail to copy index.html":"Copy index.html to "+path)
      });
    });

    if (compiler.hooks) {
      compiler.hooks.done.tap('AssetsCopier', this.onDone);
      // compiler.hooks.compilation.tap('AssetsCopier', this.onCompilation);
      compiler.hooks.afterResolvers.tap('AssetsCopier', this.onAfterResolve);
    // } else { for old webpack
    //   compiler.plugin('done', this.onDone);
    //   compiler.plugin('compilation', this.onCompilation);
    //   compiler.plugin('normal-module-factory', (nmf) => {
    //     nmf.plugin('after-resolve', this.onAfterResolve);
    //   });
    }
  }

  onAfterResolve(compiler){
    console.log("? onAfterResolve");
  }
  onCompilation(compilation, compilationParams){
    console.log("? onCompilation");
    compilation.hooks.buildModule.tap('AssetsCopier',
      module => {
        console.log(`\n? buildModule ${module.type}\n`, module.constructor.toString().split("{")[0]);
      }
    );
  }
  onDone(stats){
    console.log("? onDone");
  }
}

module.exports = AssetsCopier;

現(xiàn)在峦树,把以上代碼保存到 assets-copier.js 并在 Webpack 配置文件中引入插件辣辫,然后就可以源打包,Webpack 就會(huì)執(zhí)行插件了魁巩。

const AssetsCopier  = require("./assets-copier");

module.exports = {
    // ...
    plugins: [
        new AssetsCopier (),
    ]
}
AssetsCopier

API

lib/Compiler.js

創(chuàng)建的hook(鉤子)

Compiler.hooks.additionalPass

Compiler.hooks.afterCompile

Compiler.hooks.afterEmit

Compiler.hooks.afterEnvironment

Compiler.hooks.afterPlugins

Compiler.hooks.afterResolvers

Compiler.hooks.beforeCompile

Compiler.hooks.beforeRun

Compiler.hooks.compilation

Compiler.hooks.compile

Compiler.hooks.contextModuleFactory

Compiler.hooks.done

Compiler.hooks.emit

Compiler.hooks.entryOption

Compiler.hooks.environment

Compiler.hooks.failed

Compiler.hooks.invalid

Compiler.hooks.make

Compiler.hooks.normalModuleFactory

Compiler.hooks.run

Compiler.hooks.shouldEmit

Compiler.hooks.thisCompilation

Compiler.hooks.watchClose

Compiler.hooks.watchRun

無(wú)注冊(cè)的hook(鉤子)

調(diào)用的hook(鉤子)

Compiler.call.additionalPass - callAsync

Compiler.call.afterCompile - callAsync

Compiler.call.afterEmit - callAsync

Compiler.call.beforeCompile - callAsync

Compiler.call.beforeRun - callAsync

Compiler.call.childCompiler - call

Compiler.call.compilation - call

Compiler.call.compile - call

Compiler.call.contextModuleFactory - call

Compiler.call.done - callAsync

Compiler.call.emit - callAsync

Compiler.call.make - callAsync

Compiler.call.needAdditionalPass - call

Compiler.call.normalModuleFactory - call

Compiler.call.run - callAsync

Compiler.call.shouldEmit - call

Compiler.call.thisCompilation - call

lib/Compilation.js

創(chuàng)建的hook(鉤子)

Compilatio.hooks.additionalAssets

Compilatio.hooks.additionalChunkAssets

Compilatio.hooks.advancedOptimizeModuleOrder

Compilatio.hooks.afterChunks

Compilatio.hooks.afterHash

Compilatio.hooks.afterOptimizeAssets

Compilatio.hooks.afterOptimizeChunkAssets

Compilatio.hooks.afterOptimizeChunkIds

Compilatio.hooks.afterOptimizeChunkModules

Compilatio.hooks.afterOptimizeChunks

Compilatio.hooks.afterOptimizeDependencies

Compilatio.hooks.afterOptimizeExtractedChunks

Compilatio.hooks.afterOptimizeModuleIds

Compilatio.hooks.afterOptimizeModules

Compilatio.hooks.afterOptimizeTree

Compilatio.hooks.afterSeal

Compilatio.hooks.assetPath

Compilatio.hooks.beforeChunkAssets

Compilatio.hooks.beforeChunkIds

Compilatio.hooks.beforeChunks

Compilatio.hooks.beforeHash

Compilatio.hooks.beforeModuleAssets

Compilatio.hooks.beforeModuleIds

Compilatio.hooks.buildModule

Compilatio.hooks.childCompiler

Compilatio.hooks.chunkAsset

Compilatio.hooks.chunkHash

Compilatio.hooks.contentHash

Compilatio.hooks.dependencyReference

Compilatio.hooks.failedModule

Compilatio.hooks.finishModules

Compilatio.hooks.finishRebuildingModule

Compilatio.hooks.moduleAsset

Compilatio.hooks.moduleIds

Compilatio.hooks.needAdditionalPass

Compilatio.hooks.needAdditionalSeal

Compilatio.hooks.normalModuleLoader

Compilatio.hooks.optimize

Compilatio.hooks.optimizeAssets

Compilatio.hooks.optimizeChunkAssets

Compilatio.hooks.optimizeChunkIds

Compilatio.hooks.optimizeChunkModules

Compilatio.hooks.optimizeChunkModulesAdvanced

Compilatio.hooks.optimizeChunkModulesBasic

Compilatio.hooks.optimizeChunkOrder

Compilatio.hooks.optimizeChunks

Compilatio.hooks.optimizeChunksAdvanced

Compilatio.hooks.optimizeChunksBasic

Compilatio.hooks.optimizeDependencies

Compilatio.hooks.optimizeDependenciesAdvanced

Compilatio.hooks.optimizeDependenciesBasic

Compilatio.hooks.optimizeExtractedChunks

Compilatio.hooks.optimizeExtractedChunksAdvanced

Compilatio.hooks.optimizeExtractedChunksBasic

Compilatio.hooks.optimizeModuleIds

Compilatio.hooks.optimizeModuleOrder

Compilatio.hooks.optimizeModules

Compilatio.hooks.optimizeModulesAdvanced

Compilatio.hooks.optimizeModulesBasic

Compilatio.hooks.optimizeTree

Compilatio.hooks.rebuildModule

Compilatio.hooks.record

Compilatio.hooks.recordChunks

Compilatio.hooks.recordHash

Compilatio.hooks.recordModules

Compilatio.hooks.reviveChunks

Compilatio.hooks.reviveModules

Compilatio.hooks.seal

Compilatio.hooks.shouldGenerateChunkAssets

Compilatio.hooks.shouldRecord

Compilatio.hooks.succeedModule

Compilatio.hooks.unseal

調(diào)用的hook(鉤子)

Compilation.call.additionalAssets - callAsync

Compilation.call.additionalChunkAssets - call

Compilation.call.advancedOptimizeModuleOrder - call

Compilation.call.afterChunks - call

Compilation.call.afterHash - call

Compilation.call.afterOptimizeAssets - call

Compilation.call.afterOptimizeChunkAssets - call

Compilation.call.afterOptimizeChunkIds - call

Compilation.call.afterOptimizeChunkModules - call

Compilation.call.afterOptimizeChunks - call

Compilation.call.afterOptimizeDependencies - call

Compilation.call.afterOptimizeModuleIds - call

Compilation.call.afterOptimizeModules - call

Compilation.call.afterOptimizeTree - call

Compilation.call.afterSeal - callAsync

Compilation.call.beforeChunkAssets - call

Compilation.call.beforeChunkIds - call

Compilation.call.beforeChunks - call

Compilation.call.beforeHash - call

Compilation.call.beforeModuleAssets - call

Compilation.call.beforeModuleIds - call

Compilation.call.buildModule - call

Compilation.call.chunkAsset - call

Compilation.call.chunkHash - call

Compilation.call.contentHash - call

Compilation.call.dependencyReference - call

Compilation.call.failedModule - call

Compilation.call.finishModules - call

Compilation.call.finishRebuildingModule - call

Compilation.call.moduleAsset - call

Compilation.call.moduleIds - call

Compilation.call.needAdditionalSeal - call

Compilation.call.optimize - call

Compilation.call.optimizeAssets - callAsync

Compilation.call.optimizeChunkAssets - callAsync

Compilation.call.optimizeChunkIds - call

Compilation.call.optimizeChunkModules - call

Compilation.call.optimizeChunkModulesAdvanced - call

Compilation.call.optimizeChunkModulesBasic - call

Compilation.call.optimizeChunkOrder - call

Compilation.call.optimizeChunks - call

Compilation.call.optimizeChunksAdvanced - call

Compilation.call.optimizeChunksBasic - call

Compilation.call.optimizeDependencies - call

Compilation.call.optimizeDependenciesAdvanced - call

Compilation.call.optimizeDependenciesBasic - call

Compilation.call.optimizeModuleIds - call

Compilation.call.optimizeModuleOrder - call

Compilation.call.optimizeModules - call

Compilation.call.optimizeModulesAdvanced - call

Compilation.call.optimizeModulesBasic - call

Compilation.call.optimizeTree - callAsync

Compilation.call.rebuildModule - call

Compilation.call.record - call

Compilation.call.recordChunks - call

Compilation.call.recordHash - call

Compilation.call.recordModules - call

Compilation.call.reviveChunks - call

Compilation.call.reviveModules - call

Compilation.call.seal - call

Compilation.call.shouldGenerateChunkAssets - call

Compilation.call.shouldRecord - call

Compilation.call.succeedModule - call

Compilation.call.unseal - call

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末急灭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子谷遂,更是在濱河造成了極大的恐慌葬馋,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肾扰,死亡現(xiàn)場(chǎng)離奇詭異畴嘶,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)集晚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)窗悯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人甩恼,你說(shuō)我怎么就攤上這事蟀瞧。” “怎么了条摸?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵悦污,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我钉蒲,道長(zhǎng)切端,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任顷啼,我火速辦了婚禮踏枣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钙蒙。我一直安慰自己茵瀑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布躬厌。 她就那樣靜靜地躺著马昨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扛施。 梳的紋絲不亂的頭發(fā)上鸿捧,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音疙渣,去河邊找鬼匙奴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛妄荔,可吹牛的內(nèi)容都是我干的泼菌。 我是一名探鬼主播谍肤,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哗伯!你這毒婦竟也來(lái)了谣沸?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤笋颤,失蹤者是張志新(化名)和其女友劉穎乳附,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體伴澄,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赋除,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了非凌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片举农。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖敞嗡,靈堂內(nèi)的尸體忽然破棺而出颁糟,到底是詐尸還是另有隱情,我是刑警寧澤喉悴,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布棱貌,位于F島的核電站,受9級(jí)特大地震影響箕肃,放射性物質(zhì)發(fā)生泄漏婚脱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一勺像、第九天 我趴在偏房一處隱蔽的房頂上張望障贸。 院中可真熱鬧,春花似錦吟宦、人聲如沸篮洁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)袁波。三九已至,卻和暖如春辰狡,著一層夾襖步出監(jiān)牢的瞬間锋叨,已是汗流浹背垄分。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工宛篇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人薄湿。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓叫倍,卻偏偏與公主長(zhǎng)得像偷卧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吆倦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容