Creating and Publishing a VS Code Extension: A Step-by-Step Guide with JavaScript
Sylvester Asare Sarpong
Posted on February 24, 2023
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);
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
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
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
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 .
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
- The
launch.json
and thetasks.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');
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);
}
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!');
});
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.
This will launch our extension
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
}
- 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"
}
]
Don't forget to update the "activationEvents" array as well.
"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.
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.
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.
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.
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:
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.
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 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".
if (i !== 0) {
stateList.push(capitalize(words[i]));
}
- We can concatenate the elements in our
stateList
array to form a string. SincestateList
already has all the capitalized words we need, we can join them together directly without any further processing.
const transformedState = stateList.join(""); // showModal
- Here we are joining the elements of actionList array to form a string with the prefix set.
const transformedAction = "set" + actionList.join("");
- Now we should have two separate strings.
const transformedState = "showModal"
const transformedAction = "setShowModal"
All we have to do now it bring them together.
const text = `const [${transformedState} , ${transformedAction}] = useState(null)`;
This is insert transformedState
and transformedAction
string into the string above.
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.
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.
const selection = editor.selection;
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);
});
}
To test if everything is working as expected, you can run the extension by pressing Ctrl + F5
.
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
- Click on the user icon next to the profile image.
- Click on Personal access token.
- This should take you to a page to generate new token.
- Click on New Token to open a modal.
- On the modal, provide the necessary info for your token.
- Click on Show more scopes and look for Marketplace and check the option for Manage.
- Click create to create your token.
- 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:
- Go to the publisher registration page.
- Click on Create Publisher to create a new publisher.
- 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",
Installing vsce
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.
vsce login <unique publisher name>
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.
# 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.
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.
Posted on February 24, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.