I used to convert pictures into WebP format using intelligent map, but I found that it did not support online compression conversion. I had to download the client, and the computer exploded. I really did not want to continue adding software. Recently, a company friend set up a project, using node plug-in to convert pictures to WebP format, each compression has to copy the picture to this project, and then run, is also very troublesome… Thought to develop a vscode plug-in, convenient and fast!

I. Project construction

About project construction, in a previous article has been introduced in detail, not friends can first go to learn vscode plug-in development of the translation plug-in.

Second, preparation

The imagemin-Webp plugin is used to convert images to WebP format.

Install the NPM package first:

npm install --save imagemin
npm install --save imagemin-webp
Copy the code

The usage is as follows:

const imagemin = require('imagemin'); const imageminWebp = require('imagemin-webp'); (async () => { await imagemin(['images/*.{jpg,png}'], 'build/images', { use: [ imageminWebp({quality: 50}) ] }); console.log('Images optimized'); }) ();Copy the code

This plugin uses tinypng’s API for image compression. We tried several other Node plug-ins, but none of them worked as well as this one.

First of all, we need to go to the official website of Tinypng to get our API key. Compression of images using this API is only free for the first 500 images per month, and you need to pay for more, but each friend should not need to use so many images per month.

After obtaining the API key, open vscode’s config file and add your API key:

"tinypng.apiKey":"********",
Copy the code

First we need to install its NPM package:

npm install --save tinify
Copy the code

Once the NPM package is installed, use it as follows:

const source = tinify.fromFile("unoptimized.webp");
source.toFile("optimized.webp");
Copy the code

There are other methods of use, please refer to the official website.

Iii. Project development

The main development files are the manifest file package.json and the entry file extension.ts.

package.json

First of all, we need to configure our plug-in activation events. This plug-in has five activation events, which are compression of a single image, compression of a picture folder, conversion of a single image to WebP format, conversion of images under the whole folder to WebP format, and obtaining the number of compression times already used, as follows:

"activationEvents": [
    "onCommand:extension.compressImage",
    "onCommand:extension.compressImageFolder",
    "onCommand:extension.convertToWebp",
    "onCommand:extension.convertFolderToWebp",
    "onCommand:extension.getCompressUsedCount"
],
Copy the code

To do so, set the event name and category for our configured event, commands:

"contributes": {
    "commands": [
        {
            "command": "extension.compressImage",
            "title": "compress image",
            "category": "compress"
        },
        {
            "command": "extension.compressImageFolder",
            "title": "compress images",
            "category": "compress"
        },
        {
            "command": "extension.convertToWebp",
            "title": "convert image to webp",
            "category": "compress"
        },
        {
            "command": "extension.convertFolderToWebp",
            "title": "convert images to webp",
            "category": "compress"
        },
        {
            "command": "extension.getCompressUsedCount",
            "title": "get compress used count",
            "category": "compress"
        }
    ]
    ....
}
Copy the code

After setting up the event name and classification, let’s configure our menu, Menus:

"contributes": { "commands": [ ... ] , "menus": { "commandPalette": [ { "command": "extension.getCompressUsedCount" }, { "command": "extension.compressImage", "when": "False" }, { "command": "extension.convertToWebp", "when": "False" }, { "command": "extension.compressImageFolder", "when": "False" }, { "command": "extension.convertFolderToWebp", "when": "False" } ], "editor/title/context": [ { "when": "resourceLangId == image_file", "command": "extension.compressImage", "group": "1_modification" }, { "when": "resourceLangId == image_file", "command": "extension.convertToWebp", "group": "1_modification" } ], "explorer/context": [ { "when": "resourceLangId == image_file", "command": "extension.compressImage", "group": "1_modification" }, { "when": "explorerResourceIsFolder", "command": "extension.compressImageFolder", "group": "1_modification" }, { "when": "resourceLangId == image_file", "command": "extension.convertToWebp", "group": "1_modification" }, { "when": "explorerResourceIsFolder", "command": "extension.convertFolderToWebp", "group": "1_modification" } ] } }Copy the code

CommandPalette: the global commandPalette, where we make the commands other than the fetch times command invisible because there is no target image when entering the command directly from the command bar.

Editor /title/ Context: Editor title context menu. This menu is triggered by placing the mouse over the title of the editor image when we open the image. Here we need to control the display of the compressed image command only if the current file is an image.

  • when: controls when commands are visible,resourceLangIdIs used to get the currentlanguagestheid. inlanguagesWe have one in the configuration itemsidforimage_fileThe configuration limits the file name extensionpng,jpg,jpeg. So the configuration here means that the current file name suffix ispng,jpg,jpegThe command is displayed.whenThe detailed description of the fields is visiblewebsite.
  • commandCommand:
  • group: Group sort,1_modificationIs the second item in the default group.groupThe detailed description of the fields is visiblewebsite.

Explorer/Context: The explorer context menu, which is triggered when we right-click a file in the right file directory. When the file is a picture, show the command to compress the picture. Display compressed image folder command when the file is a folder. Where “when”: “explorerResourceIsFolder” means that the current resource is a folder.

We just mentioned the languages configuration item, which defines a languageId that can be reused in other parts of the VS Code API, associating file extensions, filename patterns, files that start with a particular line, minetypes with this languageId. The plug-in defines a file with the ID image_file and the file extensions PNG, JPG, and JPEG.

"contributes": { "commands": [ ... ] , "menus": [ ... ] , "languages": [ { "id": "image_file", "extensions": [ ".png", ".jpg", ".jpeg" ] } ] }Copy the code

Finally, to set the user optional configuration item, configuration.

"contributes": { "commands": [ ... ] , "menus": [ ... ] , "languages": [ ... ] , "configuration": { "title": "TinyPNG Configuration", "properties": { "tinypng.apiKey": { "type": "string", "description": "Your TinyPNG API Key" }, "tinypng.forceOverwrite": { "type": "boolean", "default": false, "description": "Judge whether to overwrite the uncompressed picture or to create a new one" }, "compress.webpQuality": { "type": "number", "default": 50, "description": "Set picture quality factor between 0 and 100" } } } }Copy the code

Before using this plug-in, you need to set some necessary information for the plug-in in the vscode configuration file. The plug-in has three configuration items:

  • tinypng.apiKey: that’s what we originally got from the official websiteAPI key. This configuration item is mandatory. Otherwise, image compression cannot be performed. The first 500 tickets per person per month are free, and the more you buy, go to the official website.
  • tinypng.forceOverwrite: Selects whether to place the compressed image in a new directory or replace the image directly.true: direct replacement;false: To the new directory. If no value is configured, it is used by defaultfalse.
  • compress.webpQuality: convertwebpThe quality configuration, the range is 0-100, the higher the configuration, the clearer the picture, the larger the volume. If no value is configured, the default value is 50.

extension.ts

Once the manifest is configured, we need to register the commands we set up in our entry file and perform the corresponding actions. It is divided into the following three steps:

  • Obtain configuration item information
  • authenticate
  • Registers commands that declare functions to be executed when the command is run

Start by importing the third-party libraries you need

import * as vscode from 'vscode';
const tinify = require("tinify");
const path = require("path");
const fs = require("fs");
const imagemin = require("imagemin");
const imageminWebp = require("imagemin-webp");
Copy the code

Next, implement the authentication function:

const validate = ( onSuccess = () => { }, onFailure = (e: any) => { } ) => tinify.validate(function (err: any) { if (err) { onFailure(err); } else { onSuccess(); }});Copy the code

Functions for compressing images:

const compressImage = (image: any, folderPath? : String) => {// Get configuration information, Const shouldOverwrite = vscode.workspace. GetConfiguration ('tinypng').get('forceOverwrite'); DestinationImagePath = image.fspath; // Let destinationImagePath = image.fspath; const parsedPath = path.parse(image.fsPath); if (! shouldOverwrite) { const dirname = parsedPath.dir; DestinationImagePath = path.join(folderPath, 'image-min'); destinationImagePath = path.join(folderPath, 'image-min'); DestinationImagePath = path.join(dirname, 'image-min'); destinationImagePath = path.join(dirname, 'image-min'); destinationImagePath = path.join(dirname, 'image-min'); } // image-min folder does not exist, create a new if (! fs.existsSync(destinationImagePath)) { fs.mkdir(destinationImagePath, (err: any) => { if (err) { console.log('Failed to create a folder'); } else { console.log('successed to create a folder'); }}); } destinationImagePath = path.join(destinationImagePath, parsedPath.base); } / / editor Left corner shows compressed the tips const statusBarItem = vscode. Window. CreateStatusBarItem vscode. StatusBarAlignment. (Left); statusBarItem.text = `Compressing file ${image.fsPath}... `; statusBarItem.show(); Return tinify.fromfile (image.fspath).tofile (destinationImagePath, (err: any) => {statusBarItem.hide(); If (err) {// Verify your API key and account limit. If (err instanceof tinify.AccountError) { console.log("The error message is: " + err.message); vscode.window.showErrorMessage( 'Authentication failed. Have you set the API Key? '); } else if (err instanceof tinify.ClientError) { // Check your source image and request options. console.log("The error message is: " + err.message); vscode.window.showErrorMessage( 'Ooops, there is an error. Please check your source image and settings.' ); } else if (err instanceof tinify.ServerError) { // Temporary issue with the Tinify API. console.log("The error message is: " + err.message); vscode.window.showErrorMessage( 'TinyPNG API is currently not available.' ); } else if (err instanceof tinify.ConnectionError) { // A network connection error occurred. console.log("The error message is: " + err.message); vscode.window.showErrorMessage( 'Network issue occurred. Please check your internet connectivity.' ); } else { // Something else went wrong, unrelated to the Tinify API. console.error(err.message); vscode.window.showErrorMessage(err.message); } } else { // compress successfully vscode.window.showInformationMessage( `Successfully compressed ${image.fsPath} to ${destinationImagePath}! `); }}); };Copy the code

Functions converted to WebP format:

const convertToWebp = async (image: any, folderPath? : string) => { try { const dirname = path.dirname(image.fsPath); // Let destinationImagePath; DestinationImagePath = path.join(folderPath, 'webp'); destinationImagePath = path.join(folderPath, 'webp'); } else {// destinationImagePath = path.join(dirname, 'webp');} destinationImagePath = path.join(dirname, 'webp'); } / / editor Left corner shows compressed the tips const statusBarItem = vscode. Window. CreateStatusBarItem vscode. StatusBarAlignment. (Left); statusBarItem.text = `Compressing file ${image.fsPath}... `; statusBarItem.show(); // Convert await imagemin([image.fspath], {glob: false, destination: destinationImagePath, plugins: [imageminWebp({ quality: imageQuantity })] }); statusBarItem.hide(); const parsedPath = path.parse(image.fsPath); vscode.window.showInformationMessage( `Successfully convert ${image.fsPath} to ${destinationImagePath + '\\' + parsedPath.name + '.webp'}! `); } catch (e) { console.log('convert fail', e); vscode.window.showErrorMessage('Ooops, there is an error. Please check your source image and settings.'); }};Copy the code

Finally, sign up for our command

// the quality of the webp image let imageQuantity: unknown; export function activate(context: vscode.ExtensionContext) { tinify.key = vscode.workspace.getConfiguration("tinypng").get("apiKey") || ""; imageQuantity = vscode.workspace.getConfiguration("compress").get("webpQuality") || 50; Validate (() => console.log('Validation successful! '), (e) => { console.error(e.message, "validate user fail"); vscode.window.showInformationMessage( 'TinyPNG: API validation failed. Be sure that you filled out tinypng.apiKey setting already.' ); }); / / compressed images const compressImageDisposable = vscode.com mands. RegisterCommand (' extension.com pressImage ', (image) => { compressImage(image); }); context.subscriptions.push(compressImageDisposable); / / all the images in the zip folder const compressImageFolderDisposable = vscode.com mands. RegisterCommand (' extension.com pressImageFolder ', (folder) => { vscode.workspace. findFiles(new vscode.RelativePattern( folder.fsPath, `**/*.{png,jpg,jpeg}` )) .then((files) => files.forEach((file) => { compressImage(file, folder.fsPath); })); }); context.subscriptions.push(compressImageFolderDisposable); / / convert images into the webp const convertToWebpDisposable = vscode.com mands. RegisterCommand (' extension. ConvertToWebp ', (image) => { convertToWebp(image); }); context.subscriptions.push(convertToWebpDisposable); / / in the folder of all of the pictures into the webp const convertFolderToWebpDisposable = vscode.commands.registerCommand('extension.convertFolderToWebp', (folder) => { vscode.workspace. findFiles(new vscode.RelativePattern( folder.fsPath, `**/*.{png,jpg,jpeg}` )) .then((files) => files.forEach((file) => { convertToWebp(file, folder.fsPath); })); }); context.subscriptions.push(convertFolderToWebpDisposable); / / get the number of compressed image is now using the const getCompressUsedCountDisposable = vscode.commands.registerCommand('extension.getCompressUsedCount', () = > {/ / tinypng API specified has been used for number must be in the callback function of authenticate or command execution in the validate function callback (() = > {vscode. Window. ShowInformationMessage ( `TinyPNG: You already used ${tinify.compressionCount} compression(s) this month.` ); }); }); context.subscriptions.push(getCompressUsedCountDisposable); }Copy the code

Here is the end of the development, press F5 run to see it ~

4. Refer to the article

2. Tinypng API