Skip to content

Conversation

@ntotten
Copy link
Member

@ntotten ntotten commented Dec 1, 2025

  • Convert code to ESM
  • Desktop extension runs as pure esm
  • Web extension is bundled as cjs
  • Also cleans up tests to make them run faster and improve reliability
  • Everything is async, no more blocking the loop

Copilot AI review requested due to automatic review settings December 1, 2025 12:32
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR converts the Prettier VS Code extension from CommonJS to ECMAScript Modules (ESM), enabling modern JavaScript module support and improving compatibility with Prettier v3+. The conversion involves significant architectural changes while maintaining backward compatibility with Prettier v2.

Key Changes

  • Module System: Converted to pure ESM with "type": "module" in package.json, requiring .js extensions on all imports
  • Dynamic Loading: Replaced worker thread-based Prettier loading with native ESM import() for both Prettier and plugins
  • Build Configuration: Desktop extension outputs as ESM (.js), browser extension as CJS (.cjs), and tests as bundled CJS (.cjs)

Reviewed changes

Copilot reviewed 47 out of 49 changed files in this pull request and generated no comments.

Show a summary per file
File Description
package.json Added "type": "module", updated entry points, removed find-up and resolve dependencies
package-lock.json Contains outdated dependencies that need regeneration
tsconfig.json / tsconfig.test.json Updated to ESM with module: "ESNext" and moduleResolution: "bundler"
esbuild.mjs Configured ESM output for desktop, CJS for browser/tests, added Node.js built-in externals
src/ModuleResolver.ts Complete refactor: removed find-up/resolve deps, added async module resolution with findUp utility
src/PrettierDynamicInstance.ts New unified loader using dynamic import() for both ESM and CJS Prettier modules
src/PrettierEditService.ts New loadPlugins() function to load plugins via dynamic import with pathToFileURL
src/utils/find-up.ts New async utility replacing find-up package
src/utils/exec.ts New async utility for executing shell commands with proper timeout handling
src/utils/resolve-module-entry.ts New utility to resolve package.json entry points for ESM compatibility
src/utils/global-node-paths.ts Converted to async, added pnpm support, uses new execAsync utility
src/utils/resolvers.ts Removed loadNodeModule(), updated resolveNodeModule() to use createRequire()
src/test/**/*.test.ts Updated imports to use .js extensions and assert module (not node:assert)
test-fixtures/config/**/*.js Converted Prettier config files from module.exports to export default
.vscode/launch.json Updated test configuration to use new test setup
.vscode-test.mjs Updated to look for .cjs test files
Comments suppressed due to low confidence (1)

src/utils/resolvers.ts:44

  • The resolveConfigPlugins function is exported but is no longer used anywhere in the codebase. Plugin resolution is now handled directly in PrettierEditService.loadPlugins() via dynamic imports. Consider removing this function, or if it's intended for future use or external API, add a comment explaining why it's kept.

@ntotten ntotten force-pushed the esm_support branch 10 times, most recently from 7d800f8 to 7b94270 Compare December 1, 2025 19:58
This PR converts the extension from CommonJS to ESM modules and improves
how Prettier is loaded and resolved.

Key changes:

**ESM Conversion**
- Convert all source files to use ESM imports/exports
- Update esbuild configuration for ESM output
- Add .js extensions to all relative imports
- Update tsconfig for ESM module resolution

**Prettier Module Loading**
- Lazy-load bundled Prettier using dynamic import() for faster activation
- Remove worker thread implementation (PrettierWorkerInstance) - now use
  PrettierDynamicInstance which loads Prettier dynamically
- Improve plugin loading to resolve and import plugins as ES modules
- Add utility functions for finding modules (find-up, resolve-module-entry)

**Extension Activation**
- Make activate() async and await formatter registration
- Ensures formatters are ready when extension.isActive becomes true
- Fixes race condition where tests could run before formatters registered

**Test Infrastructure**
- Add ensureExtensionActivated() helper for reliable test setup
- Extract common format test utilities to formatTestUtils.ts
- Update test imports for ESM compatibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
ntotten and others added 2 commits December 1, 2025 15:28
Tests loading plugins via ESM import in prettier.config.mts files,
which is a new feature in Prettier 3.5.0+.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@ntotten ntotten requested a review from fisker December 1, 2025 20:40

if (pluginPath) {
try {
// Resolve to actual entry file since ESM doesn't support directory imports
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we try both import and require? require still avaiable in ESM.

Just to be clear, directory import is for v2, right? Since Prettier 3 already dropped support for it.

Copy link
Member

@fisker fisker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know much about vscode extensions, but the code looks good to me.

ntotten and others added 6 commits December 1, 2025 16:05
Replace manual package.json parsing with createRequire().resolve()
which handles all the complexity of exports, main fields, and fallbacks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Rename ModuleResolver.ts → ModuleResolverNode.ts (desktop)
- Rename BrowserModuleResolver.ts → ModuleResolverWeb.ts (browser)
- Keep class export name as ModuleResolver for simplicity
- Update esbuild browser alias plugin for new file names
- Update documentation to reflect new file naming convention

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Create the require function from within the target module's directory
so Node's resolution works correctly for local Prettier installations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Fix scoped package resolution in resolveModuleEntry (e.g., @prettier/plugin-xml)
- Simplify plugin loading to just resolve paths and let Prettier import them
- Update browser languages script to dynamically get plugin list from prettier exports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Handle URLs, absolute paths, relative paths, and package names
- Use createRequire to resolve packages from file's directory
- Remove unused findUp import

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Use require.resolve with createRequire from target directory
- Only convert plugin paths to file:// URLs for Prettier v3+
- For Prettier v2, pass absolute paths that require() can handle

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@ntotten ntotten merged commit 74b56c7 into main Dec 1, 2025
13 checks passed
@ntotten ntotten deleted the esm_support branch December 1, 2025 21:57
@ntotten ntotten mentioned this pull request Dec 1, 2025
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants