Creating and Publishing a VS Code Extension: A Step-by-Step Guide with JavaScript
Published: 2023-02-24 01:44:24

I recently stumbled upon a unique and entertaining extension for VS Code called [vscode-pets](https://marketplace.visualstudio.com/items?itemName=tonybaloney.vscode-pets), created by [Anthony Shaw](https://twitter.com/anthonypjshaw). 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.

```javascript 
const [counter, setCounter] = useState(0);
```
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:

```javascript
npm install -g yo generator-code
```
`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:
```javascript
yo code
```
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.

```bash
     _-----_     ╭──────────────────────────╮
    |       |    │   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
```
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:

```javascript
code .
```

## 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.


```javascript
├── .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 
```

- 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.

```javascript
const vscode = require('vscode');
```
Imports the vscode extensibility API module.

```javascript
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);
}
```
The above is the `activate` function which get call when our extenison is activated. 

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

		vscode.window.showInformationMessage('Hello World from react-setstate!');
	});
```
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.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ozi8qkxv6fwh8k84dys.png)

This will launch our extension

![Extension launch successfull](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ana583kxeb9u4ehd2vja.png)

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.

```javascript

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
}

```


- 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.

```javascript
"commands": [
    {
      "command": "react-setstate.setState",
      "title": "React setState"
    }
  ]
```

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

```javascript
"activationEvents": [
    "onCommand:react-setstate.setState"
  ],
```

- 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.

```javascript
const inputText = await vscode.window.showInputBox({
    placeHolder: "Eg: show modal",
    prompt: "Enter the name of your state.",
  });
```
- 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.
```javascript
if (inputText === "") {
    vscode.window.showErrorMessage(
      "You need to type in something for this to work😊."
    );
    return;
  }
if (inputText == null) return;
```
- 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. 
```javascript
const words = inputText.split(" ");
```
- 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.
```javascript
function capitalize(word) {
	return word.charAt().toUpperCase() + word.slice(1);
}
```
- The conventional way of creating states in React involves defining a variable using the useState hook, which typically looks like this:
```javascript
const [showModal, setShowModal] = useState(false)
```
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. 

```javascript
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]));
  }
```


```javascript
const stateList = ['show', 'Modal']
const actionList = ['Show', "Modal']
```
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".

```javascript
  if (i !== 0) {
      stateList.push(capitalize(words[i]));
    } 
```

- 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. 

```javascript
const transformedState = stateList.join(""); // showModal
```
 - Here we are joining the elements of actionList array to form a string with the prefix set.

```javascript
const transformedAction = "set" + actionList.join("");
```
- Now we should have two separate strings.
```javascript
const transformedState = "showModal"
const transformedAction = "setShowModal"
```
All we have to do now it bring them together.

```javascript
const text = `const [${transformedState} , ${transformedAction}] = useState(null)`;
```
This is insert `transformedState` and `transformedAction` string into the string above.

```javascript
const text = `const [showModal , setShowModal] = useState(null)`;
```
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. 

```javascript
const editor = vscode.window.activeTextEditor;
```
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.
```javascript
  const selection = editor.selection;
```

Finally, we use the line below to replace the text on that line with our text.
```javascript
if (editor) {
    const selection = editor.selection;

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


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

![Extension Demo Gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebkqdsprpx724yatmfu3.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](https://azure.microsoft.com/en-us/products/devops) and a [publisher](https://marketplace.visualstudio.com/manage) account.

Once you've set up your organization in [Azure DevOps](https://azure.microsoft.com/en-us/products/devops), you can follow these steps to generate your personal access token:


## Personal Access Token
![Personal settings menu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4d6uftoluhbr0uk0e6t.png)

![Create personal access token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b6aa3x6heesugpska7vf.png)

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](https://marketplace.visualstudio.com/manage/publishers).
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.

```javascript
  "publisher": "your-publisher-name",
```


## Installing vsce
```javascript
npm install -g @vscode/vsce
```
`vsce` is a CLI tool for managing VS Code extensions. Use it to login with your publisher name and personal access token.

```javascript
vsce login <unique publisher name>
```

```javascript
vsce publish
```
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.

```bash
# react-setstate README

react-setstate is an extension that allows react developers to create state quickly.
```

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](https://marketplace.visualstudio.com/manage/publishers).



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](https://github.com/Tylerasa/react-setstate).
- Download the extension [here](https://marketplace.visualstudio.com/items?itemName=tylerasa.react-setstate&ssr=false#overview).

![Congrats](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ffmi0vqtuehm2ulq4s5a.gif)