This is the 7th day of my participation in Gwen Challenge

Note: the following is personal understanding, if there is wrong also hope to correct!

Preface, a food and love the food with chicken H ~ write a bug in a night, suddenly turn to webpack website, suddenly want to implement a plug-in to deepen impression, then start character directly after two weeks to act, suddenly want to write a plug-in to be good, too difficult to estimate had to spend a lot of time, too easy to dull (inflation), that mimic a hot Door plug-in CopyWebpackPlugin bar, almost a necessary plug-in to use simple, also famous, OK open dry!

Loader is different from plugin

  • Loader: a file loader that tells WebPack how to convert and process a certain type of file and import it into the file at the packaging place
  • Plugin: Custom packaging process, WebPack provides a lot of lifecycle hooks. We can do the logic we want to do in the hook function, such as I want to add a js section to the output file, or I want to delete some code in the specified file, it is ok

Learn webPack plugin syntax

plugins: [
    new CopyWebpackPlugin({
        from:'/src/public/'.to:'public'})]Copy the code

You can see that the CopyWebpackPlugin in the Plugin array uses the keyword new and has arguments passed in so it should be a constructor

  • The compiler object

The constructor defines an Apply method, and when the plug-in executes, WebPack will execute our plug-in’s Apply method and pass in a Compiler object that has hooks in it, hooks for each part of the lifecycle, and the one we’ll need right away is called ThisCompilation (called when the compilation is initialized, before the compilation event is triggered)

  • Compilation object

I call them file instance objects. Compiler objects do more lifecycle hook functions, such as configuration loading, configuration initialization, while Compilation objects mostly do file content processing

First Plugin (provided by WebPack)

const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
    apply(compiler) {
        compiler.hooks.run.tap(pluginName, (compilation) = > {
            console.log('Webpack build process begins! '); }); }}module.exports = ConsoleLogOnBuildWebpackPlugin;
Copy the code

In the example above, we defined a class with an apply method in it, then got an instance of the Compiler, registered the callback of the RUN hook function on the instance,

A plugin must have an Apply method. Webpack will execute the plugin's Apply method and pass in a Compiler. Almost everything we do is done by Apply

Hook types and effect examples

  • Types of hooks
Type the name describe
SyncHook Synchronous hooks that cannot handle asynchronous tasks
SyncBailHook Synchronous hook, when return is not null, prevents downward execution
SyncWaterfallHook Synchronous hook, which supports passing the return value transparently to the next hook
SyncLoopHook Synchronous hook, which allows the return value to be passed transparently to the next hook and the method to be executed repeatedly if it is not null
AsyncParallelHook Asynchronous parallel hook that returns non-null and prevents downward execution and directly executes the callback
AsyncSeriesHook Asynchronous serial hook
AsyncSeriesBailHook Asynchronous serial hook that returns non-null, prevents downward execution and directly executes the callback
AsyncSeriesLoopHook A hook that supports asynchronous serial && parallelism that returns non-null, repeated execution
AsyncSeriesWaterfallHook An asynchronous serial hook whose next step depends on the value returned in the previous step

1. For synchronization, it is recommended to use TAP directly for registration.

TabAsync implements callback by executing callback methods.

3. A Promise is returned, and tapPromise is recommended for method registration

Document: Query the type of hook

  • The sample a

SyncHook executes serially, regardless of the return value of the event handler, and executes all event handlers in the order in which they were registered after the event is triggered

compiler.hooks.environment.tap('test'.(context, entry) = > {
    console.log(11111111)
})
compiler.hooks.environment.tap('test'.(context, entry) = > {
    console.log(2222222)
})
compiler.hooks.environment.tap('test'.(context, entry) = > {
    console.log(3333333)})// Output 11111111 2222222 3333333
Copy the code
  • Example 2

SyncBailHook executes sequentially. Multiple events are registered, and if one function returns a value, it does not proceed to the next event

    compiler.hooks.entryOption.tap('test'.(context, entry) = > {
        console.log(11111111)
    })
    compiler.hooks.entryOption.tap('test'.(context, entry) = > {
        console.log(222222)
        return 1
    })
    compiler.hooks.entryOption.tap('test'.(context, entry) = > {
        console.log(3333333)})// Output 11111111 2222222
Copy the code

After learning the basic part of the process of the whole plug-in we are familiar with some, let’s start our protagonist copyWebpackPlugin plug-in

Start copyWebpackPlugin

The directory structure

CopyWebpackPlugin/globby. Js (used for processing files, and set to ignore)

const fs = require('fs');
const path = require('path');
async function globby( absoluteFrom,options ){
    const { ignore = [] } = options;
    absoluteFrom = absoluteFrom.indexOf('/') = = =0 ? absoluteFrom.substring(1) : absoluteFrom;
    try{
        const result = await fs.readdirSync(absoluteFrom)
        .filter( filte= >! ignore.includes(filte) ) .map(file= > { return { file_path:path.join( absoluteFrom,file ),file_name:file } } );
        return result || []
    }catch(err){
        console.log(err)
    }
}
module.exports = globby;
Copy the code

Schema. json (parameter validation, dependent on schema-utils)

{
    "type":"object"."properties": {"from": {"type":"string"
        },
        "to": {"type":"string"
        },
        "ignore": {"type":"array"}},"additionalProperties":false
}
Copy the code

CopyWebpackPlugin/index. Js (plug-in code)

Map-utils -> yarn add schema-utils
const { validate } = require('schema-utils');
const globby = require('./globby');
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const schema = require('./schema.json');
const { RawSource } = webpack.sources;
class CopyWebpackPlugin{
    constructor(options = {}){
        // Verify that options comply with the specification
        validate(schema,options,{
            name:'CopyWebpackPlugin'
        })
        this.options = options;
    }
    apply( compiler ){
        // Initialize the compilation
        compiler.hooks.thisCompilation.tap('CopyWebpackPlugin'.( compilation ) = >{
            // Add resource hooks
            compilation.hooks.additionalAssets.tapAsync('CopyWebpackPlugin'.async ( cb ) =>{
                // Copy the resources from to to
                const { from,ignore } = this.options;
                / / is lacking
                const to = this.options.to ? this.options.to : '. ';
                // Get the directory to run the command
                const context = compiler.options.context;
                // Change the output path to an absolute path
                const absoluteFrom = path.isAbsolute( from)?from : path.join( context,from );
                //globby(the folder to process), the second argument is the file to ignore
                const paths = await globby( absoluteFrom ,{ ignore } );
                // Read the contents of the file
                const files = await Promise.all(
                    paths.map( async file_info =>{
                        const { file_path,file_name } = file_info;
                        const file_data = await fs.readFileSync( file_path );
                        let filename = path.join(to,file_name)
                        return { file_data, filename }
                    })
                )
                // Generate webpack resources
                const assets = files.map( file= >{
                    const { file_data,filename } = file;
                    const source = new RawSource(file_data);
                    return{ source, filename }
                })
                // Add to the compilation output
                assets.forEach( asset= >{
                    const { filename, source} = asset;
                    compilation.emitAsset(filename,source)
                })
                // The plug-in is complete
                cb()
            })
        })
    }
}

module.exports = CopyWebpackPlugin;
Copy the code

use

plugins: [
    // Copy the contents of SRC /pulic/ to public under the package file
    new CopyWebpackPlugin({
        from:'/src/public/'.to:'public'}),]Copy the code

conclusion

Webpack has a lot of hooks, it’s a little bit more cumbersome to learn, and before you learn it, take a look at Tapable, which is the core webPack framework, event-based, or publisk-subscribe, or observer mode, Tapable supports the entire life cycle of WebPack and its open custom plug-in system. After understanding this, we just need to look at the document and find the appropriate event segment hook to do logic processing.