Is it possible to make a vscode extension made of mjs files?
because I tried to make an extension with mjs files only, in order to have full es6 features without TypeScript. But it does not run:
If I make the extension with $ vsce package
it does not give any error but it makes an extension that does not work when installed: the contributions that I've put in the package.json are present but vscode shows an error popup that says
Activating extension 'my.ext' failed: Must use import to load ES Module: c:\vsext\extension.mjs.
and every command I try to run gives an error
command 'my.cmd' not found
If I run the extension on the debugger, and the breakpoint on uncaught exception option flagged, it breaks on /src/vs/workbench/api/node/extHostExtensionService.ts:88.
After further search, I noticed that this exception is generatend when the script tries to load the first mjs module.
there is something I can do in order to include my mjs library files?
I think that this behaviour could also impact the use of npm modules with mjs files.
Found (kind of) a way using esm:
The idea is to use esm to handle es6 imports and share the vscode object between imported modules
this could seem quite tricky but when I tried to just import * as vscode from "vscode"
im my mjs files, vscode complained that can't find module vscode.
so I've done the following
var vscode; // will store vscode obj
export function IMPORTVSCODE(vscodeInstance){
vscode = vscodeInstance
}
for example
// init.js
const vscode = require("vscode")
const esm = require("esm")(module/*, options*/)
const main = esm("./main.mjs")
main.IMPORTVSCODE(vscode)
module.exports = main
// main.mjs
import * as other from "./other.mjs"
var vscode; // will store vscode obj
export function IMPORTVSCODE(vscodeInstance){
vscode = vscodeInstance
}
function activate(){
other.IMPORTVSCODE(vscode)
other.methodThatNeedsVscode()
}
// other.mjs
var vscode; // will store vscode obj
export function IMPORTVSCODE(vscodeInstance){
vscode = vscodeInstance
}
export function methodThatNeedsVscode(){
vscode // ...use vscode
}
My extension runs fine now!
But I think that better ideas could be found, so if somebody has some finest solutions, please share them
Yes, you can use pure ESM (thank god), even though you have to start through with CJS. The trick is to use await import
:
/**
* @param {import('vscode').ExtensionContext} context - The context.
*/
async function activate(context) {
(await import('./register-hello-world.mjs')).activate(context);
(await import('./register-hello-tensor.mjs')).activate(context);
}
module.exports.activate = activate;
register-hello-world.mjs:
import {add} from '#customImport';
import {vscode} from '#vscode';
/**
* This method is called when your extension is activated, which
* happens the very first time the command is executed.
* @param {import('vscode').ExtensionContext} context - The context.
*/
export function activate(context) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('> register extension.helloWorld');
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
const disposable = vscode.commands.registerCommand('extension.helloWorld', () => {
vscode.window.showInformationMessage(`Hello World! add(10, 20) is ${add(10, 20)}`);
});
context.subscriptions.push(disposable);
}
Now you may wonder - what is that import {vscode} from '#vscode';
?
You can basically use your package.json
like an importmap:
{
"name": "vscode-extension-transformers",
"displayName": "vscode-extension-transformers",
"description": "Transformers.js example for VS Code",
"version": "1.0.0",
"publisher": "kungfooman",
"repository": "https://github.com/kungfooman/vscode-extension-transformers/",
"scripts": {
"watch": "echo Begone, watch build step! Embracing true ESM power!",
"vscode:prepublish": "echo Begone, prepublish build step! Embracing true ESM power!",
"lint": "eslint \"src/**/*.js\""
},
"engines": {
"vscode": "^1.74.0"
},
"categories": [
"Other"
],
"activationEvents": [],
"main": "./src/extension.js",
"contributes": {
"commands": [
{
"command": "extension.helloWorld",
"title": "Hello World"
},
{
"command": "extension.helloTensor",
"title": "Hello Tensor"
}
]
},
"devDependencies": {
"@types/node": "^16.18.34",
"@types/vscode": "^1.73.0",
"eslint": "^8.26.0"
},
"imports": {
"#customImport": "./src/customImport.mjs",
"#vscode": "./src/vscode.mjs"
},
"dependencies": {
"@xenova/transformers": "^2.6.1"
}
}
And ./src/vscode.mjs has to look like this:
import {createRequire} from 'node:module';
const require = createRequire(import.meta.url);
export const vscode = require('vscode');
Tada, problems solved - have fun with ESM. If you use TypeScript, drop it now: it just creates extra headaches through AST transformations etc.
Too much code? I made a complete test repo here: