DEV Community

Cover image for Creating and Publishing a VS Code Extension: A Step-by-Step Guide with JavaScript
Sylvester Asare Sarpong
Sylvester Asare Sarpong

Posted on

Creating and Publishing a VS Code Extension: A Step-by-Step Guide with JavaScript

I recently stumbled upon a unique and entertaining extension for VS Code called vscode-pets, created by Anthony Shaw. This extension adds a virtual pet, such as a dog๐Ÿถ, to your screen, providing a fun distraction from coding. However, there are many other pet options to choose from.

VS Code extensions are plugins that can enhance your coding experience with the Visual Studio Code editor. They offer a range of features, including syntax highlighting, code formatting, debugging, testing, linting, snippets, themes, icons, and more. VS Code is designed with extensibility in mind, making it easy to personalize and tailor to your needs. The VS Code marketplace currently offers over 30,000 extensions, spanning from language and theme extensions to integration extensions and even fun and whimsical options.

To begin creating your own VS Code extension, you will need to have Node.js installed on your system. Once you have Node.js set up, you can use a command-line tool called yo (short for Yeoman) to generate a project template. This template will provide you with a basic structure and files for your extension, including a manifest file that defines the extension's metadata and dependencies. You can then customize and build on this template to create your desired functionality.

The Problem

React is my favorite JavaScript framework because of its versatility and powerful features. However, sometimes creating component state in React can be a bit of a hassle and require a lot of typing. In today's post, we will be addressing this issue and exploring ways to make this process less tedious. While we won't be attempting to fix the entire framework, we will be creating an extension that can simplify the creation of state in React components.

const [counter, setCounter] = useState(0);
Enter fullscreen mode Exit fullscreen mode

Creating a simple counter state in React can involve a lot of typing, which can be tedious and time-consuming. We all know how exhausting it can be to type out the same code repeatedly, right? That's where our new extension, react-setstate, comes in. With this extension, we can create state variables in just a few keystrokes, saving us time and effort.

Setting Up the Development Environment

To get started with creating a Visual Studio Code extension, we will need to install two packages - yo and generator-code. These can be installed globally using npm with the following command:

npm install -g yo generator-code
Enter fullscreen mode Exit fullscreen mode

yo is a popular scaffolding tool that helps developers generate new projects by using customizable generators. generator-code is a generator specifically designed for creating Visual Studio Code extensions. By installing these two packages, we will be able to quickly generate a new extension project and get started with coding right away!

Now that we have installed the necessary packages, we can start creating our project. To do this, open up your terminal and run the following command:

yo code
Enter fullscreen mode Exit fullscreen mode

This will launch an interactive shell that will guide us through the process of creating our new extension. We will be prompted to enter various details about our extension, such as its name, description, and the type of extension we want to create. By following the prompts and answering the questions, we will be able to quickly generate the scaffolding for our new extension and start building our code.

     _-----_     โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
    |       |    โ”‚   Welcome to the Visual  โ”‚
    |--(o)--|    โ”‚   Studio Code Extension  โ”‚
   `---------ยด   โ”‚        generator!        โ”‚
    ( _ยดU`_ )    โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
    /___A___\   /
     |  ~  |
   __'.___.'__
 ยด   `  |ยฐ ยด Y `

? What type of extension do you want to create? New Extension (JavaScript)
? What's the name of your extension? react-setstate
? What's the identifier of your extension? react-setstate
? What's the description of your extension? set state in react
? Enable JavaScript type checking in 'jsconfig.json'? No
? Initialize a git repository? Yes
? Which package manager to use? npm
Enter fullscreen mode Exit fullscreen mode

Once we have answered all the prompts in the yo interactive shell, it will generate a new project for us based on our inputs. We can open this project in Visual Studio Code by navigating to its directory in the terminal and running the following command:

code .
Enter fullscreen mode Exit fullscreen mode

Anatomy of the project.

Our project structure should look something like this, you might have some additional files if you selected TypeScript. We will go over all the files and explain why they are present.

โ”œโ”€โ”€ .vscode // launch a new window for testing 
โ”‚   โ”œโ”€โ”€ launch.json    
โ”‚   โ””โ”€โ”€ tasks.json 
โ”œโ”€โ”€ node_modules  //node_modules
โ”œโ”€โ”€ test //tests
โ”‚   โ”œโ”€โ”€  suite
โ”‚   โ”œโ”€โ”€ โ”œโ”€โ”€ extension.test.js       
โ”‚   โ””โ”€โ”€ runTest.js     
โ”œโ”€โ”€ .eslintrc.json // ESLint config
โ”œโ”€โ”€ .gitignore //ignore node_modules and other specified files
โ”œโ”€โ”€ .vscodeignore   //ignore files we don't have published
โ”œโ”€โ”€ CHANGELOG.md //keep track of changes
โ”œโ”€โ”€ extension.js //source code
โ”œโ”€โ”€ jsconfig.json   //JS config  
โ”œโ”€โ”€ package-lock.json //generated by npm   
โ”œโ”€โ”€ package.json //manifest    
โ”œโ”€โ”€ README.md  //info about extension
โ”œโ”€โ”€ vsc-extension-quickstart.md //quick start guide 
Enter fullscreen mode Exit fullscreen mode
  • The launch.json and the tasks.json are used to launch a new window of VS code for testing the extension.
  • node_modules - node_modules for our project.
  • The test folder will contain all the tests for our extension.
  • .eslintrc.json will contain ESLint configuration for this project.
  • .gitignore will contain a list of names of files and/or folders that we don't to be included when you push our extension to version control platform like Github.
  • .vscodeignore will contain a list of names of files and/or folders that should be published when we finally publish our extension.
  • CHANGELOG.md will be used to keep track of changes we make to our extension.
  • extension.js this is where we will be writing the code/logic behind our extension(entry point).
  • jsconfig.json will contain our Javascript configuration
  • package.json is the manifest file of any Node.js project and contains the metadata of the project.
  • README.md will contain the info about our extension.
  • vsc-extension-quickstart.md is the default quick start guide that comes with our project.

Entry Point

Open extension.js file, which contains boilerplate code and comments. Let's briefly understand the code before we proceed to clean it up.

const vscode = require('vscode');
Enter fullscreen mode Exit fullscreen mode

Imports the vscode extensibility API module.

function activate(context) {

    console.log('Congratulations, your extension "react-setstate" is now active!');

    let disposable = vscode.commands.registerCommand('react-setstate.helloWorld', function () {

        vscode.window.showInformationMessage('Hello World from react-setstate!');
    });

    context.subscriptions.push(disposable);
}
Enter fullscreen mode Exit fullscreen mode

The above is the activate function which get call when our extenison is activated.

let disposable = vscode.commands.registerCommand('react-setstate.helloWorld', function () {

        vscode.window.showInformationMessage('Hello World from react-setstate!');
    });
Enter fullscreen mode Exit fullscreen mode

The react-setstate.helloWorld is a unique identifier for a command that performs a specific action when triggered. For instance, when the react-setstate.helloWorld command is invoked, it shows a message displaying "Hello World from react-setstate!". However, command IDs can be lengthy, and typing them out every time can be tedious. Therefore, we can assign simpler names to these command IDs for ease of use

package.json

In the package.json file, under the contributes field, there is an array called commands which includes the default command ID. By default, the title of the react-setstate.helloWorld command has been set to something memorable, "Hello World". Any new command IDs we register can be added to this array. To run our extension, we can press Ctrl + F5, which will open a new VS Code window with the name "[Extension Development Host]". To launch our extension, we can press Ctrl + Shift + P to open the command palette, type in the name we gave the extension (in this case, "Hello World"), and press Enter.

Hello World typed in the dialog open to launch the extension.

This will launch our extension

Extension launch successfull

We can stop the Extension Development Host by pressing the red square.

extension.js

This tutorial has become quite lengthy, so to save time, I'll paste the code and then explain what each part does.


const vscode = require('vscode');

/**
 * @param {vscode.ExtensionContext} context
 */
function activate(context) {
    let disposable = vscode.commands.registerCommand('react-setstate.setState', async function () {
        const inputText = await vscode.window.showInputBox({
            placeHolder: "Eg: show modal",
            prompt: "Enter the name of your state.",
          });

          if (inputText === "") {
            vscode.window.showErrorMessage(
              "You need to type in something for this to work๐Ÿ˜Š."
            );
            return;
          }

          if (inputText == null) return;

          const words = inputText.split(" ");
          const stateList = [];
          const actionList = [];

          for (let i = 0; i < words.length; i++) {
            if (i !== 0) {
              stateList.push(capitalize(words[i]));
            } else {
              stateList.push(words[i]);
            }
            actionList.push(capitalize(words[i]));
          }

          const transformedState = stateList.join("");
          const transformedAction = "set" + actionList.join("");
          const text = `const [${transformedState} , ${transformedAction}] = useState(null)`;

          const editor = vscode.window.activeTextEditor;

          if (editor) {
            const selection = editor.selection;

            editor.edit((editBuilder) => {
              editBuilder.replace(selection, text);
            });
          }
    });

    context.subscriptions.push(disposable);
}

// This method is called when your extension is deactivated
function deactivate() {}

function capitalize(word) {
    return word.charAt().toUpperCase() + word.slice(1);
}


module.exports = {
    activate,
    deactivate
}

Enter fullscreen mode Exit fullscreen mode
  • The activate function registers the react-setstate.setState command id, which we defined earlier in the extension.js file.

Next, we need to update the package.json file to reflect this change. We should rename our command to react-setstate.setState and give it a suitable title. For now, we will call it React setState, but you can choose any name you prefer.

"commands": [
    {
      "command": "react-setstate.setState",
      "title": "React setState"
    }
  ]
Enter fullscreen mode Exit fullscreen mode

Don't forget to update the "activationEvents" array as well.

"activationEvents": [
    "onCommand:react-setstate.setState"
  ],
Enter fullscreen mode Exit fullscreen mode
  • After the extension is activated, the user will be prompted to enter the name of the state they want to create, which will be stored in the inputText variable.
const inputText = await vscode.window.showInputBox({
    placeHolder: "Eg: show modal",
    prompt: "Enter the name of your state.",
  });
Enter fullscreen mode Exit fullscreen mode
  • If the user didn't type anything in and left the prompt empty, we will close the prompt and return a message indicating that no state was created.
if (inputText === "") {
    vscode.window.showErrorMessage(
      "You need to type in something for this to work๐Ÿ˜Š."
    );
    return;
  }
if (inputText == null) return;
Enter fullscreen mode Exit fullscreen mode
  • The reason for splitting the text based on space is that a state name could potentially have multiple words, such as "show modal", and we want to be able to capitalize each word individually.
const words = inputText.split(" ");
Enter fullscreen mode Exit fullscreen mode
  • The capitalize function is a helper function that takes a string and capitalizes the first letter of each word in the string. We will use this function to ensure that the state name entered by the user is properly capitalized.
function capitalize(word) {
    return word.charAt().toUpperCase() + word.slice(1);
}
Enter fullscreen mode Exit fullscreen mode
  • The conventional way of creating states in React involves defining a variable using the useState hook, which typically looks like this:
const [showModal, setShowModal] = useState(false)
Enter fullscreen mode Exit fullscreen mode

On the left-hand side, we have the state itself, and on the right-hand side, we have the action. That is why we have two arrays: stateList will store the capitalized versions of all the words the user typed, except for the first word.

for (let i = 0; i < words.length; i++) {
    if (i !== 0) {
      stateList.push(capitalize(words[i]));
    } else {
      stateList.push(words[i]);
    }
    actionList.push(capitalize(words[i]));
  }
Enter fullscreen mode Exit fullscreen mode
const stateList = ['show', 'Modal']
const actionList = ['Show', "Modal']
Enter fullscreen mode Exit fullscreen mode

After the code above, the stateList array should contain all the capitalized words from the user input except for the first one. The actionList array should contain the capitalized version of the first word followed by the word "Modal".

  if (i !== 0) {
      stateList.push(capitalize(words[i]));
    } 
Enter fullscreen mode Exit fullscreen mode
  • We can concatenate the elements in our stateList array to form a string. Since stateList already has all the capitalized words we need, we can join them together directly without any further processing.
const transformedState = stateList.join(""); // showModal
Enter fullscreen mode Exit fullscreen mode
  • Here we are joining the elements of actionList array to form a string with the prefix set.
const transformedAction = "set" + actionList.join("");
Enter fullscreen mode Exit fullscreen mode
  • Now we should have two separate strings.
const transformedState = "showModal"
const transformedAction = "setShowModal"
Enter fullscreen mode Exit fullscreen mode

All we have to do now it bring them together.

const text = `const [${transformedState} , ${transformedAction}] = useState(null)`;
Enter fullscreen mode Exit fullscreen mode

This is insert transformedState and transformedAction string into the string above.

const text = `const [showModal , setShowModal] = useState(null)`;
Enter fullscreen mode Exit fullscreen mode

Our string should now look like the above.

Now that we have our state, we need a way to paste that text in our editor/VS Code.

const editor = vscode.window.activeTextEditor;
Enter fullscreen mode Exit fullscreen mode

To get the current active text editor, we use vscode.window.activeTextEditor. Then we can use selection.active to get the position of the cursor in the text editor.

  const selection = editor.selection;
Enter fullscreen mode Exit fullscreen mode

Finally, we use the line below to replace the text on that line with our text.

if (editor) {
    const selection = editor.selection;

    editor.edit((editBuilder) => {
      editBuilder.replace(selection, text);
    });
  }
Enter fullscreen mode Exit fullscreen mode

To test if everything is working as expected, you can run the extension by pressing Ctrl + F5.

Extension Demo Gif

Publishing our extension

With everything working as expected, it's time to publish our extension and make it available for others to use. To do this, we'll need a personal access token from Azure DevOps and a publisher account.

Once you've set up your organization in Azure DevOps, you can follow these steps to generate your personal access token:

Personal Access Token

Personal settings menu

Create personal access token

  1. Click on the user icon next to the profile image.
  2. Click on Personal access token.
  3. This should take you to a page to generate new token.
  4. Click on New Token to open a modal.
  5. On the modal, provide the necessary info for your token.
  6. Click on Show more scopes and look for Marketplace and check the option for Manage.
  7. Click create to create your token.
  8. Make sure to copy the token as we will need it later.

Publisher

We need to create a publisher to publish our extension under. Follow these steps:

  1. Go to the publisher registration page.
  2. Click on Create Publisher to create a new publisher.
  3. Remember to copy and paste the unique publisher name somewhere safe, as we will need it later for logging in.

With the publisher name copied, we need to update/create the publisher field in the package.json file with our publisher name.

  "publisher": "your-publisher-name",
Enter fullscreen mode Exit fullscreen mode

Installing vsce

npm install -g @vscode/vsce
Enter fullscreen mode Exit fullscreen mode

vsce is a CLI tool for managing VS Code extensions. Use it to login with your publisher name and personal access token.

vsce login <unique publisher name>
Enter fullscreen mode Exit fullscreen mode
vsce publish
Enter fullscreen mode Exit fullscreen mode

If you encounter an error message instructing you to update the README.md file, you can either use the following template or create your own README.md text.

# react-setstate README

react-setstate is an extension that allows react developers to create state quickly.
Enter fullscreen mode Exit fullscreen mode

Once the extension has been published, it will undergo verification on the VS Code Marketplace, which may take several minutes. You can check the status of the verification process here.

Congratulations! Your extension has been successfully created and published on the VS Code [marketplace].
(https://marketplace.visualstudio.com/vscode) for anyone to download and use.

  • Checkout source code here.
  • Download the extension here.

Congrats

Top comments (0)