Skip to content

Enable consuming of ES modules in extensions #130367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
igorskyflyer opened this issue Aug 8, 2021 · 127 comments
Open

Enable consuming of ES modules in extensions #130367

igorskyflyer opened this issue Aug 8, 2021 · 127 comments
Assignees
Labels
extension-host Extension host issues extensions Issues concerning extensions feature-request Request for new features or functionality on-testplan
Milestone

Comments

@igorskyflyer
Copy link

When developing extensions and using either JavaScript or TypeScript we are unable to consume ES modules, only somewhat legacy CommonJS modules, setting the type to module and rewriting the extension to use import instead of require breaks the extension, generating an exception that states that all modules should use import instead of require in internal VS Code JavaScript files, I conclude it's caused by the type: module that forces Node to treat all .js files as ES modules. Tried using TypeScript which transpiles its own syntax to CommonJS module - so that's a no, I have also tried using just .mjs extension, again the same issue.

What is the status of this issue and are there plans to enable using of ES modules in extension development? That (could) bring somewhat big performance gains when bundling extensions with, for example, esbuild because it would enable tree-shaking - dead code removal, thus loading only necessary code. But I think this is not an extension API only issue, right? This needs to be done for VS Code itself?

@vscodebot
Copy link

vscodebot bot commented Aug 8, 2021

(Experimental duplicate detection)
Thanks for submitting this issue. Please also check if it is already covered by an existing one, like:

@igorskyflyer
Copy link
Author

Closing after 15 days of no reply.

@alexdima
Copy link
Member

cc @jrieken

@alexdima alexdima added extension-host Extension host issues feature-request Request for new features or functionality web Issues related to running VSCode in the web and removed web Issues related to running VSCode in the web labels Aug 25, 2021
@Lemmingh

This comment has been minimized.

@andyleejordan
Copy link
Member

For what it's worth, I ran into this trying to update node-fetch from 2.x to 3.x, since the newer version is ESM only. Looks to be the direction things are going, so extension developers are only more likely in the future to run into issues due to lack of ESM support.

@andyleejordan
Copy link
Member

FYI @TylerLeonhardt one of the Code issues I'd love to see fixed 😃

LinqLover added a commit to LinqLover/downstream-repository-mining that referenced this issue Oct 2, 2021
@Lemmingh

This comment has been minimized.

@andyleejordan
Copy link
Member

Excuse me, @TylerLeonhardt, do you know why my comment above was marked as spam? I have to assume that was a mistake.

@alexdima
Copy link
Member

alexdima commented Nov 1, 2021

@andschwa I'm sorry, it might have been me that marked the comment as spam, but I don't remember doing it. I personally tend to hide comments that do not bring any value to the underlying discussion or distract from it.

@jrieken
Copy link
Member

jrieken commented Apr 1, 2025

@TomasHubelbauer I did think a little about service workers and my main concerns are they are meant to be opaque, e.g they are just an optimisation and things should work without, they are bit finicky to manage esp. in different deployments like desktop, vscode.dev, gh.dev, and last I don't if the interception knows the origin of a request. For our tricks to work it is crucial to know what file/module makes an import-call.

I'm a bit skeptical about ESM adoption going forward given the missing web support tho.

Yeah, it's not because I don't want to solve this but because I don't how to do it. That said, web worker extensions are already constraint to be a single file (otherwise require(vscode) doesn't work) which means you already have a bundle/transpile pipeline in place

@TomasHubelbauer
Copy link
Contributor

@jrieken Alternatively, vscode.dev could expose a /vscode endpoint serving a dynamic text/javascript response which would know about the user's identity, open project etc. and could generate the same response the service worker would have been crearting, but on the BE instead of the FE. Not sure if this is practical given the shift of the load to the service instead of the browser, but technically I believe it should be possible. Additionally, if there is a way of code-sharing this logic, the service worker would then become truly opaque - it could take care of serving ./vscode if it is installed, but if it wasn't installed or supported, the actual server endpoint would take over. If your vscode.dev setup is such that code-sharing would be possible between the service worker implementation and the server implementation, maintenance could be reasonable - but this is hard to get right from my experience. AFAIK the service worker can access the request object information so when it comes to knowing the origin of the request to know what to serve exactly, this information could be also included in a cookie or a request header so that the service worker could use it to nail down exactly what it is serving.

@tats-u
Copy link
Contributor

tats-u commented Apr 9, 2025

Electron 35 (Node.js 22.14, where require(ESM) is available):

@justarandomgeek
Copy link

justarandomgeek commented Apr 25, 2025

I've been experimenting with migrating my extension (https://github.com/justarandomgeek/vscode-factoriomod-debug/tree/next) to ESM, and so far it works great with one notable exception: a few of my deps are CJS still, and when they get rolled into my ESM bundle (by esbuild) they still try to require("node:events") (and various other node internal modules), which fails. For now i'm shimming this with some esbuild external imports of separate .cjs bundles of those specific deps to then load by the main bundle. (You can see the list of what i had to shim in my build.ts, and the shims themselves in the /src/cjs dir.)

@justarandomgeek
Copy link

after doing a bit more research i've removed the cjs shims mentioned in my previous comment and instead just added

	banner: {
		// https://github.com/evanw/esbuild/issues/1232#issuecomment-830677608
		// shim `require` for cjs deps to load properly in esm-land
		js: `
		import {createRequire} from 'module'
		const require = createRequire(import.meta.url)
		`,
	},

in my esbuild bundling config, and now i've got pure-esm extension (and cli) bundle output, and everything seems to be working! Thanks for the great work everybody! :) 🎉

@lishaduck
Copy link

@justarandomgeek, you could try switching to rolldown (or tsdown), which handles that automatically.

jrieken added a commit that referenced this issue May 7, 2025
This PR makes it possible to have an extension that uses ESM for the NodeJS EH and CJS for the web worker EH.

A sample is the GH issue notebooks extension: microsoft/vscode-github-issue-notebooks#185

re #130367
jrieken added a commit that referenced this issue May 7, 2025
This PR makes it possible to have an extension that uses ESM for the NodeJS EH and CJS for the web worker EH.

A sample is the GH issue notebooks extension: microsoft/vscode-github-issue-notebooks#185

re #130367
@felipecrs
Copy link
Contributor

felipecrs commented May 9, 2025

I got this error when launching my extension in VS Code 1.100:

Dynamic require of "child_process" is not supported.

I use execa. Is this a limitation of ESM support?

@justarandomgeek
Copy link

@felipecrs that's the same issue i ran into with bundled CJS deps, see my last comment above for the shim you need to fix it - you need to pull in a require for that bundled CJS dep to load node internal modules!

@felipecrs
Copy link
Contributor

That's weird because execa is ESM-only. Maybe some other dependency is injecting that issue then. Thank you, I'll dig deeper.

@justarandomgeek
Copy link

Yeah most of mine came up via transitive deps, rather than direct deps. In any case, getting a require from createRequire should sort it right out!

I also experimented some with using esbuild alias entries to force things that load dual-mode packages via require to get the esm flavor instead of the cjs flavor from them (especially for a few where my bundles ended up with both flavors >.> ), but that made more mess than it helped!

@felipecrs
Copy link
Contributor

@jrieken maybe you can amend your sample repository with bundling instructions?

@felipecrs
Copy link
Contributor

felipecrs commented May 10, 2025

@justarandomgeek thank you. The esbuild banner trick worked like a charm.


Now I'm stuck with the import from "vscode". It works great when debugging the extension, but tests fail with:

> vscode-test

✔ Validated version: 1.100.0
✔ Found at https://update.code.visualstudio.com/1.100.0/linux-x64/stable?released=true
✔ Downloaded VS Code into /home/felipecrs/repos/vscode-shellcheck/.vscode-test/vscode-linux-x64-1.100.0
[main 2025-05-10T20:13:20.294Z] update#setState disabled
[main 2025-05-10T20:13:20.296Z] update#ctor - updates are disabled by the environment
Started local extension host with pid 87078.
Started initializing default profile extensions in extensions installation folder. file:///home/felipecrs/repos/vscode-shellcheck/.vscode-test/extensions
ComputeTargetPlatform: linux-x64
Completed initializing default profile extensions in extensions installation folder. file:///home/felipecrs/repos/vscode-shellcheck/.vscode-test/extensions
Loading development extension at /home/felipecrs/repos/vscode-shellcheck
Error: Cannot find package 'vscode' imported from /home/felipecrs/repos/vscode-shellcheck/out/src/utils/tool-check.js
        at packageResolve (node:internal/modules/esm/resolve:880:9)
        at moduleResolve (node:internal/modules/esm/resolve:953:18)
        at defaultResolve (node:internal/modules/esm/resolve:1195:11)
        at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:642:12)
        at #cachedDefaultResolve (node:internal/modules/esm/loader:591:25)
        at ModuleLoader.getModuleJobForRequire (node:internal/modules/esm/loader:347:53)
        at new ModuleJobSync (node:internal/modules/esm/module_job:333:34)
        at ModuleLoader.getModuleJobForRequire (node:internal/modules/esm/loader:407:11)
        at new ModuleJobSync (node:internal/modules/esm/module_job:333:34)
        at ModuleLoader.importSyncForRequire (node:internal/modules/esm/loader:320:11)
        at loadESMFromCJS (node:internal/modules/cjs/loader:1378:24)
        at Module._compile (node:internal/modules/cjs/loader:1518:5)
        at Module._extensions..js (node:internal/modules/cjs/loader:1588:16)
        at Module.load (node:internal/modules/cjs/loader:1282:32)
        at Module._load (node:internal/modules/cjs/loader:1103:12)
        at c._load (node:electron/js2c/node_init:2:16955)
        at e._load (file:///home/felipecrs/repos/vscode-shellcheck/.vscode-test/vscode-linux-x64-1.100.0/resources/app/out/vs/workbench/api/node/extensionHostProcess.js:225:1509)
        at t._load (file:///home/felipecrs/repos/vscode-shellcheck/.vscode-test/vscode-linux-x64-1.100.0/resources/app/out/vs/workbench/api/node/extensionHostProcess.js:180:22628)
        at r._load (file:///home/felipecrs/repos/vscode-shellcheck/.vscode-test/vscode-linux-x64-1.100.0/resources/app/out/vs/workbench/api/node/extensionHostProcess.js:172:22062)
        at Module.require (node:internal/modules/cjs/loader:1305:19)
        at require (node:internal/modules/helpers:182:18)
        at /home/felipecrs/repos/vscode-shellcheck/node_modules/mocha/lib/mocha.js:416:36
        at Array.forEach (<anonymous>)
        at Mocha.loadFiles (/home/felipecrs/repos/vscode-shellcheck/node_modules/mocha/lib/mocha.js:413:14)
        at Mocha.run (/home/felipecrs/repos/vscode-shellcheck/node_modules/mocha/lib/mocha.js:988:10)
        at mochaGlobalTeardown.mochaGlobalTeardown (/home/felipecrs/repos/vscode-shellcheck/node_modules/@vscode/test-cli/out/runner.cjs:33:50)
        at new Promise (<anonymous>)
        at Object.run (/home/felipecrs/repos/vscode-shellcheck/node_modules/@vscode/test-cli/out/runner.cjs:33:11)
        at file:///home/felipecrs/repos/vscode-shellcheck/.vscode-test/vscode-linux-x64-1.100.0/resources/app/out/vs/workbench/api/node/extensionHostProcess.js:123:17846
        at new Promise (<anonymous>)
        at PJ.sb (file:///home/felipecrs/repos/vscode-shellcheck/.vscode-test/vscode-linux-x64-1.100.0/resources/app/out/vs/workbench/api/node/extensionHostProcess.js:123:17567)
        at async PJ.$extensionTestsExecute (file:///home/felipecrs/repos/vscode-shellcheck/.vscode-test/vscode-linux-x64-1.100.0/resources/app/out/vs/workbench/api/node/extensionHostProcess.js:123:17155)
[main 2025-05-10T20:13:22.099Z] Extension host with pid 87078 exited with code: 0, signal: unknown.
Exit code:   1

Has someone run into this issue too? It can be reproduced here.

@s-h-a-d-o-w
Copy link

@felipecrs Based on the sample, we have to create and run this wrapper instead of vscode-test now: https://github.com/jrieken/vscode-esm-sample-extension/blob/main/test/runTest.mjs

@felipecrs
Copy link
Contributor

felipecrs commented May 17, 2025

@felipecrs Based on the sample, we have to create and run this wrapper instead of vscode-test now: jrieken/vscode-esm-sample-extension@main/test/runTest.mjs

I think that wrapper was based on when vscode-test cli was not a thing yet. I remember having similar wrappers back then. I hope vscode-test cli gets support for ESM at some point.

And thanks @s-h-a-d-o-w, I'll look into using this wrapper for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extension-host Extension host issues extensions Issues concerning extensions feature-request Request for new features or functionality on-testplan
Projects
None yet
Development

Successfully merging a pull request may close this issue.