Skip to content

fix: update ESM packaging and extension host loading#2735

Closed
alisonlhart wants to merge 3 commits intomainfrom
fix-esm-packaging
Closed

fix: update ESM packaging and extension host loading#2735
alisonlhart wants to merge 3 commits intomainfrom
fix-esm-packaging

Conversation

@alisonlhart
Copy link
Copy Markdown
Contributor

@alisonlhart alisonlhart commented Apr 13, 2026

Fix language server loading in extension development host after ESM transition:

  • Add .js extension to vscode-languageserver/node import for ESM compatibility
  • Always bundle language server (fixes path alias issues in unbundled dev builds)
  • Use workspace server in debug mode (for Extension Development Host)

In debug mode, the extension checks if the workspace server exists at packages/ansible-language-server/dist/cli.cjs and uses it. This allows the Extension Development Host to work when developing from source. Falls back to node_modules version (cli.js) if workspace server doesn't exist.

This PR fixes language server loading in the Extension Development Host after the extension's transition to ESM. The changes add ESM compatibility, ensure the language server is always bundled to avoid path alias issues, and enable development-mode fallback to workspace builds.

  • Updated vscode-languageserver/node import to vscode-languageserver/node.js for ESM compatibility in server.ts
  • Changed tsup.config.ts to always bundle the language server, not just in production
  • Added workspace server detection in extension.ts to check for development builds at packages/ansible-language-server/dist/cli.cjs, with fallback to the published node_modules version

related: #2682, #2498

Fix language server loading in extension development host after ESM
transition:

- Add .js extension to vscode-languageserver/node import for ESM compatibility
- Always bundle language server (fixes path alias issues in unbundled dev builds)
- Use workspace server in debug mode (for Extension Development Host)

In debug mode, the extension checks if the workspace server exists at
packages/ansible-language-server/dist/cli.cjs and uses it. This allows
the Extension Development Host to work when developing from source.
Falls back to node_modules version (cli.js) if workspace server doesn't exist.
@github-actions github-actions Bot added the fix label Apr 13, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

Three files updated to modify import paths for the language server, enable unconditional code bundling in the build process, and add workspace-level debug server detection with filesystem checks for improved local development support.

Changes

Cohort / File(s) Summary
Language Server Build & Imports
packages/ansible-language-server/src/server.ts, packages/ansible-language-server/tsup.config.ts
Import path updated from vscode-languageserver/node to vscode-languageserver/node.js; build configuration modified to enable bundle: true unconditionally across all environments instead of only in production mode.
Extension Debug Configuration
src/extension.ts
Added filesystem existence checks via node:fs import; enhanced debug server selection logic to prioritize workspace-local server binary at packages/ansible-language-server/dist/cli.cjs, with fallback to published node_modules version.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰✨ Imports hop to .js, bundles always tight,
Workspace servers shine in the debug light,
Local paths checked with a filesystem's care,
Fallbacks stand ready—they're always there! 🚀

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: update ESM packaging and extension host loading' directly corresponds to the main changes: fixing ESM import paths and updating extension host loading logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-esm-packaging

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/extension.ts (1)

1151-1190: ⚠️ Potential issue | 🟠 Major

bundledServer and workspaceServer are the identical path — dedupe and add a run-mode fallback.

Lines 1153–1159 and 1162–1168 construct exactly the same path (packages/ansible-language-server/dist/cli.cjs). The fs.existsSync(workspaceServer) check at line 1169 only affects debug.module; run.module unconditionally uses bundledServer, so when the workspace build is absent (e.g., non-production tsup outputs to lib/, see packages/ansible-language-server/tsup.config.ts), the non-debug code path will still point at a missing file with no fallback to node_modules/@ansible/ansible-language-server/dist/cli.js.

Also worth noting: the comment on line 1151 describes the old precedence ("Prefer the server shipped in the vsix; otherwise use the workspace package"), which no longer matches the new workspace-first/node_modules-fallback behavior.

♻️ Suggested consolidation
-  // Prefer the server shipped in the vsix (packages/ansible-language-server/dist/); otherwise
-  // use the workspace package (e.g. when running from source).
-  const bundledServer = path.join(
-    context.extensionPath,
-    "packages",
-    "ansible-language-server",
-    "dist",
-    "cli.cjs",
-  );
-
-  // For debug mode: prefer workspace server (development) over node_modules (published package)
-  const workspaceServer = path.join(
-    context.extensionPath,
-    "packages",
-    "ansible-language-server",
-    "dist",
-    "cli.cjs",
-  );
-  const packageServer = fs.existsSync(workspaceServer)
-    ? workspaceServer
-    : path.join(
-        context.extensionPath,
-        "node_modules",
-        "@ansible",
-        "ansible-language-server",
-        "dist",
-        "cli.js",
-      );
+  // Prefer the workspace build (development from source) over the published
+  // package under node_modules (shipped in the VSIX via bundledDependencies).
+  const workspaceServer = path.join(
+    context.extensionPath,
+    "packages",
+    "ansible-language-server",
+    "dist",
+    "cli.cjs",
+  );
+  const nodeModulesServer = path.join(
+    context.extensionPath,
+    "node_modules",
+    "@ansible",
+    "ansible-language-server",
+    "dist",
+    "cli.js",
+  );
+  const resolvedServer = fs.existsSync(workspaceServer)
+    ? workspaceServer
+    : nodeModulesServer;

…and then use resolvedServer for both run.module and debug.module.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/extension.ts` around lines 1151 - 1190, The two identical path variables
bundledServer and workspaceServer should be consolidated and a single
resolvedServer chosen with the correct precedence and fallback so both
run.module and debug.module point at a valid file; create a single workspacePath
(e.g., packages/ansible-language-server/dist/cli.cjs) and set resolvedServer =
fs.existsSync(workspacePath) ? workspacePath : path.join(context.extensionPath,
"node_modules", "@ansible", "ansible-language-server", "dist", "cli.js"), then
use resolvedServer for serverOptions.run.module and serverOptions.debug.module
(keeping debugOptions/execArgv as-is), and update the surrounding comment to
accurately describe the chosen precedence and fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ansible-language-server/tsup.config.ts`:
- Around line 25-29: The outDir in tsup.config.ts switches to "lib" when
NODE_ENV !== "production" which breaks src/extension.ts and the run lookup that
expect packages/ansible-language-server/dist/cli.cjs; update the build config so
artifacts always go to "dist" (make outDir unconditionally "dist") or
alternatively update src/extension.ts and the run lookup to probe both
"dist/cli.cjs" and "lib/cli.cjs" (check functions/methods that build the path to
cli.cjs in src/extension.ts and the run command) so development builds placed in
lib/ are discovered.

---

Outside diff comments:
In `@src/extension.ts`:
- Around line 1151-1190: The two identical path variables bundledServer and
workspaceServer should be consolidated and a single resolvedServer chosen with
the correct precedence and fallback so both run.module and debug.module point at
a valid file; create a single workspacePath (e.g.,
packages/ansible-language-server/dist/cli.cjs) and set resolvedServer =
fs.existsSync(workspacePath) ? workspacePath : path.join(context.extensionPath,
"node_modules", "@ansible", "ansible-language-server", "dist", "cli.js"), then
use resolvedServer for serverOptions.run.module and serverOptions.debug.module
(keeping debugOptions/execArgv as-is), and update the surrounding comment to
accurately describe the chosen precedence and fallback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d48d4fe-c306-47f1-9124-9d6170d2ab8b

📥 Commits

Reviewing files that changed from the base of the PR and between 408bbae and 0d7737a.

📒 Files selected for processing (3)
  • packages/ansible-language-server/src/server.ts
  • packages/ansible-language-server/tsup.config.ts
  • src/extension.ts

Comment on lines 25 to 29
minify: env === "production",
bundle: env === "production",
bundle: true,
entry: ["src/**/*.ts"],
format: ["esm", "cjs"],
outDir: env === "production" ? "dist" : "lib",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

outDir mismatch with extension host lookup path.

bundle is now unconditional, but outDir still switches to lib when NODE_ENV !== "production". Meanwhile src/extension.ts only looks for the workspace server at packages/ansible-language-server/dist/cli.cjs (and run has no fallback at all). Running a plain build/watch without NODE_ENV=production will place artifacts in lib/ and the Extension Development Host will silently fail to find dist/cli.cjs, defeating the PR's stated goal of enabling development from source.

Consider making outDir unconditionally dist, or update the extension to probe both dist/ and lib/.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ansible-language-server/tsup.config.ts` around lines 25 - 29, The
outDir in tsup.config.ts switches to "lib" when NODE_ENV !== "production" which
breaks src/extension.ts and the run lookup that expect
packages/ansible-language-server/dist/cli.cjs; update the build config so
artifacts always go to "dist" (make outDir unconditionally "dist") or
alternatively update src/extension.ts and the run lookup to probe both
"dist/cli.cjs" and "lib/cli.cjs" (check functions/methods that build the path to
cli.cjs in src/extension.ts and the run command) so development builds placed in
lib/ are discovered.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

❌ Patch coverage is 43.75000% with 9 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/extension.ts 43.75% 9 Missing ⚠️

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@alisonlhart alisonlhart marked this pull request as draft April 21, 2026 18:47
@github-project-automation github-project-automation Bot moved this from In Progress to Done in 🧰 devtools project board Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant