Command not found in VSCode extension

32,236

Solution 1

Since my comment helped someone, I would like to post it as an answer to give it more visibility:

I was able to fix this issue by manually compiling the Typescript source (by running tsc -p ./ into my root folder). This command should be ran automatically when debugging, however, I was still not able to find why it was not the case on my machine.

Solution 2

You need to add all registered commands to the activationEvents list in package.json so that they are available on invocation. Update your package.json as such:

{
    ...
    "activationEvents": [
        "onCommand:extension.openMyExtension",
        "onCommand:extension.useMyExtension"
    ]
    ...
}

You can find more details on activation events in the official VSCode Documentation.

Solution 3

I had the same issue. Everything was configured correctly but it turned out my package.json was missing one dependency. That caused the extension to fail loading and consequently the command not having been registered correctly at runtime.

To find out what is wrong with your code, open up the Help > Toggle Developer Tools and look for errors in the console.

Solution 4

you have not activated the extension in activationEvents:

"activationEvents": [
    "onCommand:extension.useMyExtension"
],

Solution 5

In case anyone made the same mistake as I did...

So if it compiles successfully, but complains command could not found, check:

  1. In the extension debug window, check the console logs from Help > Toggle Developer Tools (From @Bart Theeten). There's a high chance of JS error you've not noticed.

  2. Whether all the commands are added to package.json

"contributes": {
    "commands": [ ... ]
}
  1. Whether the command is registered with
context.subscriptions.push(
    vscode.commands.registerCommand('command_name', callbackFunc)
);
  1. Finally, check the production bundle setting <- this is the one that caused me headache

My extension was made with Typescript, and bundled by Webpack + ts-loader. I introduced an interesting conditionally load module feature, which will need to require module from a dynamic path like /module/<version>. That creates several interesting problems:

First is a wrong __dirname, __filename globals that only happens when using webpack to pack NodeJS apps(I got / value for __dirname). I have to set webpack.config.js with:

module.exports = {
  node: false
}

To disable the webpack nodeJS global mocks. (reference)

Secondly, the modules path restrictions that we easily forgot:

The absolute path import may not work, the relative path also could not exceed the scope you've set somewhere 🤷🏻‍♀️. Example:

// tsconfig.json
{
  "compilerOptions": {
    "outDir": "lib"
  },
  "include": ["src"]
}
// src/index.ts
require('../external/test')

The require path will be processed by ts-loader and webpack, it could not recognize this module path unless you use path aliases to tell them where these files are located.

And the dynamic import path cannot be dynamic after compile:

require(`./${dir}/module`) // may not work
import(`./${dir}/module`)  // may not work

Well, the reason is compiler loaders may do some processing with the path, if it is a simple static string, it could replace it with the right one but if it is a runtime value it couldn't. I am sorry but I easily forgot this since it's a function syntax!

And you know what's worse? The worse case is I get the path thing works for me but not working for others. Because the hack introduced the absolute path that only exists on my environment... Well for this, I have to say try avoid the absolute path.

Share:
32,236
Eldy
Author by

Eldy

Updated on July 11, 2022

Comments

  • Eldy
    Eldy almost 2 years

    I am trying to create a VSCode extension. This extension provides two commands, never mind their implementation:

    export function activate(context: ExtensionContext) {
    
        const provider = new ContentProvider();
        const providerRegistrations = Disposable.from(
            workspace.registerTextDocumentContentProvider(ContentProvider.scheme, provider)
        );
    
        // Open the dynamic document, and shows it in the next editor
        const openMyExtensionCommandRegistration = commands.registerTextEditorCommand('extension.openMyExtension', editor => {
            // Activate the extension and do something
        });
    
        const useMyExtensionCommandRegistration = commands.registerTextEditorCommand('extension.useMyExtension', editor => {
            // Do something
        });
    
        context.subscriptions.push(
            provider,
            openMyExtensionCommandRegistration,
            useMyExtensionCommandRegistration,
            providerRegistrations
        );
    }
    

    And this is a part of my package.json file:

    "activationEvents": [
            "onCommand:extension.openMyExtension"
        ],
        "main": "./out/extension",
        "contributes": {
            "commands": [
                {
                    "command": "extension.openMyExtension",
                    "title": "Open my extension",
                    "category": "MyExtension"
                },
                {
                    "command": "extension.useMyExtension",
                    "title": "Do something with my extension",
                    "category": "MyExtension"
                }
            ],
    

    The first command, which is supposed to activates my extension, works. It appears in the command palette, and actually does what it is supposed to do when invoked.

    The second command however, despite appearing in the command palette, raise the following error message when called:

    command 'extension.useMyExtension' not found

    I find it weird that my first command works fine but not the second since the code is quite similar. Any ideas why?

    Note that I obviously changed some variable names, I double checked for typos in the real code.