Skip to content

build: migrate from CommonJS to ESM #687

Description

@tnaum-ms

Context

The extension is currently CommonJS. This blocks us from consuming a growing number of modern, ESM-only dependencies. Concrete recent example: p-limit@4+ is pure ESM, which is why #685 introduced an in-house ConcurrencyLimiter instead of pulling a battle-tested library off the shelf.

vscode-cosmosdb (sibling extension, same org) has already completed this migration on main, so there is a working reference for what the end state looks like.

What this issue tracks

The actual migration. The choice of build system, test runner, linter/formatter, and Node target is still to be made as part of this work. Options to evaluate:

  • Build: Vite (what vscode-cosmosdb chose) vs. continuing with webpack in ESM mode vs. esbuild vs. tsup.
  • Test runner: Vitest (what vscode-cosmosdb chose) vs. Jest with ESM support vs. node:test.
  • Linter/formatter: continue with ESLint + Prettier vs. switch to oxlint / oxfmt (faster, what vscode-cosmosdb chose).
  • Node engine: bump to >=22 (matches vscode-cosmosdb) or hold lower.
  • TypeScript: bump to latest (vscode-cosmosdb is on TS 6.x).

A short design note before the implementation PR is expected.

Concrete benefits

  • Unblocks ESM-only dependencies: p-limit, chalk, node-fetch, nanoid, execa, and a long and growing tail.
  • Smaller bundles and faster cold start (tree-shaking is much more effective on ESM).
  • Faster build/test feedback loops if we adopt Vite + Vitest (sub-second incremental builds reported by vscode-cosmosdb).
  • Aligns us with vscode-cosmosdb tooling, lowering the cognitive cost of moving between repos.
  • Removes the mocha-based VS Code test runner pain point. The legacy @vscode/test-electron + mocha pipeline does not love ESM; vscode-cosmosdb works around this with a pretest hook that drops a CJS package.json into out/. The upstream story is that this runner has either already been removed or is on track to be removed, so the workaround should be temporary or unnecessary depending on when we land.

Known costs / risks

  • Touches every file with a relative import (./foo -> ./foo.js in many ESM toolchains, depending on bundler/moduleResolution).
  • Webpack config rewrite or replacement.
  • Jest -> alternative test runner migration (touches every test file's imports and mocks; jest.mock does not have a 1:1 ESM equivalent).
  • __dirname / require removal from the codebase.
  • Potential VS Code engine version bump.
  • One-shot reviewer load: the diff will be large.

References

Acceptance criteria

  • Decision recorded for each open choice above.
  • All existing tests pass under the new test runner (or a documented, scoped exception list).
  • Extension activates, builds, packages, and ships exactly as before.
  • CI green.
  • No CJS-only fallback shims remaining (e.g. no pretest-style package.json rewrite needed once the upstream test runner removal lands).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    No fields configured for Task.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions