Provide a pure ESM build · Issue #1661 · microsoft/vscode-languageserver-node · GitHub
Skip to content

Provide a pure ESM build #1661

Open
Open

Description

@alex-kinokon

Would it be possible to include a pure-ESM build for vscode-languageserver/browser to help with tree shaking?

Activity

  1. dbaeumer commented on Aug 4, 2025

    @dbaeumer
    Member

    Actually the new 10.0.0-next.13 version has export entry point which should help with tree shaking. Doesn't that work for you?

  2. alex-kinokon commented on Aug 4, 2025

    @alex-kinokon
    Author

    It’s still CommonJS which doesn’t seem to work with tree shaking.

  3. added
    feature-requestRequest for new features or functionality
    and removed
    info-neededIssue requires more information from poster
    on Aug 6, 2025
  4. changed the title [-]ESM build[/-] [+]Provide a pure ESM build[/+] on Aug 6, 2025
  5. kevinramharak commented on Nov 7, 2025

    @kevinramharak

    I did a little bit of prototyping by forking the repo and building the following packages with esbuild:

    • vscode-jsonrpc
    • vscode-languageserver-protocol
    • vscode-languageclient
    • vscode-languageserver

    The results for bundling my (small) extension with esbuild with optimisations and minification turned on:

    server (30% decrease in size)

    CJS:

    134 input files 1 output file
    793.8 kb 318.7 kb
    52% CJS 48% ESM

    ESM:

    134 input files 1 output file
    660.3 kb 242.6 kb
    4% CJS 95% ESM

    extension + client (40% decrease in size)

    CJS:

    139 input files 1 output file
    1000 kb 425.2 kb
    90% CJS 9% ESM

    ESM:

    139 input files 1 output file
    773.1 kb 301.7 kb
    4% CJS 95% ESM

    That adds up to a total decrease of ~37% for having proper tree-shakable modules as part of the distributed build.

    I hope this explains the advantages of providing an ESM build and helps to visualize the issue.

    esbuild analyze

    I used https://esbuild.github.io/analyze/ to analyze the results of my builds. It also provides useful visualisations:

    server

    CJS:
    Image

    ESM:
    Image

    extension + client

    CJS:
    Image

    ESM:
    Image

  6. dbaeumer commented on Nov 10, 2025

    @dbaeumer
    Member

    Kevin Ramharak (@kevinramharak) thanks for the additional information. The main reason why I didn't invest a lot into this is that VS Code extensions can't use ESM in their main. It still needs to be CommonJS.

    Would you be willing to share that prototype you did?

  7. kevinramharak commented on Nov 10, 2025

    @kevinramharak

    Dirk Bäumer (@dbaeumer)

    The branch is here: https://github.com/regex-radar/vscode-languageserver-esm/tree/feature/generate-esm-output
    The compare: main...regex-radar:vscode-languageserver-esm:feature/generate-esm-output

    The changes needed are:

    • set isolatedModules to true in the tsconfig.json files, this will require all type exports to be changed to explicit export type statements.
    • fix a few legacy import x = require('a') statements
      • import semverSatisfies = require('semver/functions/satisfies');
      • import semverSatisfies = require('semver/functions/satisfies');
    • figure out what the exact module setting should be
    • figure out what the exact moduleResolution setting should be
    • update the exports field in the package.json to enable ESM module resolution to work properly

    I used esbuild because I'm familiar with it, and I had a hard time figuring out how the compiling of vscode-languageserver-node was set up. I expect a similar result when tsc is being used with the tsconfig set up to target ESM.

    As for ESM support of VS Code extensions since version 1.100.x the NodeJS extension host supports ESM extensions:

    It is being tracked here:

    With an example extension here:

    I mainly used the example extension to try and get the client tests running, which did work, but there are a few more hurdles to get them to run properly and pass. I unfortunately did not commit those changes.


    I do think that even if the extension is bundled with target CJS, the bundler being used can still optimize based on the input files being ESM. I know that esbuild does not tree shake CJS by default, because dynamic require calls cannot be optimized a lot, compared to static import statements.

    I only figured out how the tsconfig-gen worked after I finished generating the output I needed for my own extension, which is unfortunate. I also haven't spend a lot of time getting the tests to work, as my extension seemed to run fine.

    If you are interested in a PR, I can try and invest some time to see if I can get it to work with tsconfig-gen and get the tests and testbed running, as I haven't tried it much as the codebase is still unfamiliar to me.

  8. dbaeumer commented on Nov 11, 2025

    @dbaeumer
    Member

    Thanks again for the detailed reply. What I actually meant is that ESM is not working in the Web worker extension host. This extension host can be used in the Desktop app and the browser (the underlying problem is that import maps are not supported in a web worker). I can have another look but the problem is a little that the LSP libraries are used in the web worker extension host in various scenarios which I would still need to support. One work around is to import vscode with a require statement in these scenarios and ensure to use a bundler. But before I make that switch I must make sure all these scenarios are still supported.

    But I will have a look at your changes and see if something has changed.

  9. kevinramharak commented on Nov 11, 2025

    @kevinramharak

    Understandable, I understand the need for supporting the other environments and wasn't expecting for a complete change from CJS to ESM. I was thinking more in line of the tsconfig.esm.publish.json approach the types package is already using. An additional build output that would be 'opt in' in the form of the exports defining import on top of require path resolution, was more what I was aiming at. As I do not expect the CJS builds to disappear for a long time (if ever) considering how slow and painful ESM adoption has been in the JS ecosystem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-requestRequest for new features or functionality

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions