In daily development, we usually upload static resources to the CDN to improve access speed. This article will take you through the development of a Webpack plug-in that packs resources into a ZIP and uploads them to COS for automatic decompression.

Demand analysis

Before developing a feature, let’s break down the requirements. After analysis, the logic that needs to be realized is as follows:

  • inWebpackBefore the build is complete, iterate over the static resources to generate the ZIP
  • Upload the ZIPCOS (CDN)
  • Uploaded to theCOSAutomatic post-decompression

Since we uploaded a.zip file, we need to unzip the file after uploading. Here a cloud function is triggered in a coss-triggered manner to handle the decompression of the ZIP file.

The basic structure of the Webpack plug-in

A Webpack plug-in is essentially a constructor that defines a apply method on the prototype, which is called when Webpack starts. So you can set the hook function in the Apply method to execute the specified logic at a specific time. Here is the basic structure of a Webpack plug-in:

  class UploadToCDN {
    apply (compiler) {
      compiler.hooks.somehook.tap('plugin-name'.(compilation) = > {
        // to do something}}})Copy the code

compilerandcompilation

As shown in the code above, compiler and compilation are two objects that must be used when developing Webpack plug-ins. Compiler contains all the configuration information for our build and also provides lifecycle hooks that allow us to execute logic at specific times. Compilation can be understood as the context in which a package is run at this time, and all the results that are generated during the package are put into this object. It contains the current module resources, compile-build resources, changing files, and state information that is being tracked. When Webpack is running in development mode, a new compilation is created each time a change is detected. Like compiler, the Compilation object also provides many event callbacks for plug-ins to extend. In UploadToCDN plug-in development, we mainly used the compiler’s two hook EMIT and afterEmit. The emit will be executed before the output asset is exported to the output directory, so we can pack and compress the generated static resource on this hook function. AfterEmit will be executed after exporting the asset to the Output directory, at which point the ZIP file has been generated and can be uploaded.

So our plugin looks something like this:

  class UploadToCDN {
    constructor(options) {
      this.options = options
    }
    apply(compiler) {
      // Since there will be asynchronous operations, we use tapAsync to call callback to notify Webpack when the logical execution is complete
      compiler.hooks.emit.tapAsync(PLUGIN_NAME, (compilation, callback) = > {
        // Compress resources before output to dist
      })
      compiler.hooks.afterEmit.tapAsync(PLUGIN_NAME, (compilation, callback) = > {
        // Upload dist to cos after the resource output}}})Copy the code

Plug-in core code implementation

  • Compressed resource files

Compilation. assets will have the resources we built this time on this object, so we can either iterate over the generated static resources or output them by assigning the compilation.assets[filename] value. Here we will use JSZip to compress the file.

  compiler.hooks.emit.tapAsync(PLUGIN_NAME, (compilation, callback) = > {
    const folder = zip.folder(this.options.filename)
    // Traverse resources and put them into zip packages
    for (let filename in compilation.assets) {
      const source = compilation.assets[filename].source()
      folder.file(filename, source)
    }
    zip.generateAsync({ type: 'nodebuffer' }).then((content) = > {
      // Generate a zip file and export the file to the specified directory
      this.outputPath = path.join(compilation.options.output.path, `The ${this.options.filename}.zip`)
      const outputRelativePath = path.relative(compilation.options.output.path, this.outputPath)
      compilation.assets[outputRelativePath] = new RawSource(content)
      callback() // Notify Webpack that the hook function has finished executing})})Copy the code
  • Uploading a ZIP File

Because I use the object storage of Tencent Cloud, I upload the compressed package to the specified Bucket directly through the SDK of Tencent Cloud.

  compiler.hooks.afterEmit.tapAsync('UploadToCDN'.(compilation, callback) = > {
    cos.putObject({
      Bucket: 'your bucket'.Region: 'region'.Key: `The ${this.options.filename}.zip`.StorageClass: 'STANDARD'.Body: fs.createReadStream(this.outputPath),
      onProgress: progressData= > {
        console.log(JSON.stringify(progressData))
      }
    }, (err, data) = > {
      if (err) {
        console.error('Upload to cdn fail....... ! ')
        console.err(err)
      }
      console.log('Upload to cdn success... ! ', data)
      callback()
    })
  })
Copy the code

Configuring cloud Functions

Above we uploaded a ZIP file, after the upload we need to decompress the ZIP package, so that each static resource is in the correct access path. The specific approach is:

  1. Create twoBucket(staticandupload).staticUsed to store static resources that we will actually access, anduploadWe store the ones we just uploadedzipFile.
  2. Select the template to create the cloud function, and select Nodejs to decompress the ZIP package.

3. Set cloud function toCOS the trigger The code for cloud functions will not be posted here, because they are generated based on templates and do not need to write their own code.Unzip the logic of the cloud functionWhen a ZIP file is uploadedupload Bucket“, the cloud function will be triggered and the cloud function will get the download address of the ZIP file. Then call the API of SDK to download zip, unzip after downloading, and then traverse the directory to upload the resource to anotherBucket staticOn. After all this processing, we’ll be able tostaticDirectory petitions asked about what we just packed and uploadedjs.html.css.imageAnd so on.

After a series of logic implementation above, upload CDN plug-in is finally completed. Specific source code please see: github.com/samzerf/upl…