Several necessary conditions for developing webpack plug-ins:
- Get compiler compiler Object, through which you can obtain information including config configuration, resource files, compilation information, hook functions, etc
- The life cycle function in the compilation stage, find the appropriate hook function to deal with the corresponding logic
- The returned results support both synchronous and asynchronous methods
Get compiler instance
Step 1: get the compiler instance object:
// helloPlugin.js module.exports = class RemoveLogs { constructor(options){ this.options = options } apply(compiler) { console.log(`Hello ${this.options.name}`) } };
By introducing this script and executing it on the console, you can see the compilation results:
// webpack.config.js var HelloWorldPlugin = require('./helloPlugin.js'); module.exports = { // Configure plug-ins plugins: [new HelloWorldPlugin({ name:"chenwl" })] };
Life cycle hook function
Through official documents compiler-hooks You can view the hook function provided by compiler or directly go to / node_modules/webpack/lib/Compiler.js view
Synchronous and asynchronous mode
Hook functions can be processed synchronously or asynchronously:
module.exports = class SyncPlugin { apply(compiler){ // tap synchronization compiler.hooks.emit.tap("tap", (compilation) => { console.log("***** tap *****") }) // The process will pause before the tapAsync parameter cb is called compiler.hooks.emit.tapAsync("tapAsync", (compilation,cb) => { start(0); function start(index){ console.log(index); if(index<=3){ setTimeout(() => { start(++index); }, 1000); }else{ cb() } } }) // tapPromise is called through promise compiler.hooks.emit.tapPromise("tapPromise", (compilation)=>{ return new Promise((resolve,reject)=>{ console.log("start tap-promise"); setTimeout(()=>{ resolve() },2000) }) }) } }
logRemoverPlugin
After compiling the file, remove the console:
// logRemoverPlugin.js const fs = require("fs"); module.exports = class RemoveLogs { apply(compiler) { compiler.hooks.done.tap("RemoveLogs", stats => { const { path, filename } = stats.compilation.options.output; try { // Here, you can match the filename before processing let filePath = path + "/" + filename; fs.readFile(filePath, "utf8", (err, data) => { const rgx = /console.log\(['|"](.*?)['|"]\)/; const newdata = data.replace(rgx, ""); if (err) console.log(err); fs.writeFile(filePath, newdata, function(err) { if (err) { return console.log(err) } console.log("Logs Removed"); }); }); } catch (error) { console.log(error) } }); } };
AnalyzePlugin
Analyze the packaged resource file information and generate the file:
file name | file size |
---|---|
index.html | 1266 |
Total number of files: 1
// AnalyzePlugin.js const { compilation } = require("webpack") module.exports = class Analyze { constructor(config){ // Get package file name this.filename = config.filename; } apply(compiler){ compiler.hooks.emit.tap("analyze-plugin",(compilation)=>{ const assets = compilation.assets; const entries = Object.entries(assets); const content = `| file name | file size | | ------------ | ------------ | ` entries.forEach(([filename,fileObj])=>{ content+=`|${filename}|${fileObj.size()}| ` }); content += ` > Total number of documents ${entries.length} individual` // console.log(this.filename) compilation.assets[this.filename] = { source(){ return content }, size(){ return content.length } } }) } }
inlinePlugin
Insert resource file into html
- Get head tag group and body tag group
- Convert the link tag into a style tag, get the style content of the link attribute link, and insert it into the style tag
- The script tag gets the script content linked by the src attribute and inserts it into the script tag
const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = class InlinePlugin { constructor(config) { this.match = config.match; // Match files to be converted this.compilation = null; // Save compilation } processTag(tag) { if (!this.compilation) return; // Get file link const url = tag.attributes.href || tag.attributes.src; // Get file content const source = this.compilation.assets[url].source() if (!this.match || !this.match.test(url)) return tag; if (tag.tagName === "link") { tag = { tagName: "style", innerHTML: source } } if (tag.tagName === "script") { tag = { tagName: "script", innerHTML: source } } delete this.compilation.assets[url]; return tag } processTags(data) { let headTags = data.headTags let bodyTags = data.bodyTags headTags = headTags.map((tag) => { return this.processTag(tag) }) bodyTags = bodyTags.map((tag) => { return this.processTag(tag) }); return { headTags, bodyTags, } } apply(compiler) { compiler.hooks.compilation.tap("MyPlugin", (compilation) => { HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync( "MyPlugin", (data, cb) => { // Save compilation this.compilation = compilation; cb(null, this.processTags(data)) } ) }) } }
Write a plugin to upload files to qiniu cloud
UploadQiNiuPlugin
First, install the dependency package of qiniu cloud
npm install qiniu
const path = require("path") const qiniu = require("qiniu") module.exports = class UploadQiNiuPlugin { constructor(options) { let { bucket = "", accessKey = "", secretKey = "" } = options let mac = new qiniu.auth.digest.Mac(accessKey, secretKey) let putPolicy = new qiniu.rs.PutPolicy({ scope: bucket }) this.outputPath = "" this.uploadToken = putPolicy.uploadToken(mac) let config = new qiniu.conf.Config() this.formUploader = new qiniu.form_up.FormUploader(config) this.putExtra = new qiniu.form_up.PutExtra() } upload(filename) { return new Promise((resolve, reject) => { let realPath = path.join(this.outputPath, filename) // Upload file this.formUploader.putFile( this.uploadToken, filename, realPath, this.putExtra, (err, body) => { err ? reject(err) : resolve(body) } ) }) } apply(compiler) { compiler.hooks.afterEmit.tapPromise("upload-plugin", (compilation) => { this.outputPath = compiler.outputPath let assets = compilation.assets let promises = [] Object.keys(assets).forEach((filename) => { promises.push(this.upload(filename)) }) return Promise.all(promises) }) } }
QiniuManager
Before uploading, you may have to delete the old resource files of qiniu cloud. Here is also a tool:
class QiniuManager { constructor({ bucket, accessKey, secretKey }) { let mac = new qiniu.auth.digest.Mac(accessKey, secretKey) let config = new qiniu.conf.Config() this.bucketManager = new qiniu.rs.BucketManager(mac, config) } deleteFiles(filenames) { let deleteFile = (filename) => { return new Promise((resolve, reject) => { this.bucketManager.delete(bucket, filename, (err) => err ? reject(err) : resolve(filename) ) }) } let deletePromises = filenames.map((f) => deleteFile(f)) return Promise.all(deletePromises) } }