Skip to content

fix(language-server): preserve prettier config when resolveConfig returns null#16174

Open
spaciousejar wants to merge 1 commit into
withastro:mainfrom
spaciousejar:fix/trailing-comma-bug
Open

fix(language-server): preserve prettier config when resolveConfig returns null#16174
spaciousejar wants to merge 1 commit into
withastro:mainfrom
spaciousejar:fix/trailing-comma-bug

Conversation

@spaciousejar
Copy link
Copy Markdown

Summary

Fixes issue where trailing commas were incorrectly removed when `prettierInstance.resolveConfig()` returns null.

Problem

The Astro VSCode extension v2.8.1+ incorrectly removes trailing commas from multi-line function calls when saving `.astro` files, even when Prettier is configured to preserve them with `trailingComma: "all"`.

Root Cause

In `packages/language-tools/language-server/src/languageServerPlugin.ts`, when `prettierInstance.resolveConfig()` fails or returns `null`, the `configOptions` variable remains `null`. During the configuration cascade (`...configOptions`), this causes the user's Prettier configuration to be completely ignored.

Solution

  • Initialize `configOptions` to empty object (`{}`) instead of `null`
  • Only override `configOptions` when `resolveConfig` returns a valid configuration
  • Ensures user's Prettier configuration (including `trailingComma: "all"`) is properly respected

Changes

  • Updated error handling in `getFormattingOptions` function
  • Improved configuration resolution logic
  • Added proper null checking for `resolveConfig` result

Testing

  • Manual testing with `.prettierrc` containing `{ "trailingComma": "all" }`
  • Verified trailing commas are preserved after save in VSCode
  • Confirmed existing functionality remains unaffected

Fixes #14665

…urns null

Fixes issue where trailing commas were incorrectly removed when prettierInstance.resolveConfig() returns null. The fix ensures that user's Prettier configuration is properly respected by initializing configOptions to an empty object and only overriding it when resolveConfig returns a valid configuration.

Fixes withastro#14665
Copilot AI review requested due to automatic review settings March 31, 2026 23:39
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 31, 2026

⚠️ No Changeset found

Latest commit: 9d680aa

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Copy Markdown
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

Fixes Astro language server formatting so user Prettier settings (notably trailingComma: "all") are still respected when prettier.resolveConfig() returns null, preventing formatting from unintentionally dropping trailing commas on save.

Changes:

  • Initialize Prettier configOptions to {} and only replace it when resolveConfig() returns a non-null config.
  • Add a null-check around the resolveConfig() result to avoid clobbering downstream merged options.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +94 to +100
const resolvedConfig = await prettierInstance.resolveConfig(filePath, {
// This seems to be broken since Prettier 3, and it'll always use its cumbersome cache. Hopefully it works one day.
useCache: false,
editorconfig: true,
});
if (resolvedConfig) {
configOptions = resolvedConfig;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The identifier resolvedConfig is used for two different concepts (the result of prettierInstance.resolveConfig() inside the try, and then the merged config object later). Even though the scopes don’t overlap, this makes the config-resolution logic harder to follow; consider renaming the inner variable to something like fileConfig / resolvedFileConfig (or renaming the merged one) to avoid confusion during future edits.

Suggested change
const resolvedConfig = await prettierInstance.resolveConfig(filePath, {
// This seems to be broken since Prettier 3, and it'll always use its cumbersome cache. Hopefully it works one day.
useCache: false,
editorconfig: true,
});
if (resolvedConfig) {
configOptions = resolvedConfig;
const fileConfig = await prettierInstance.resolveConfig(filePath, {
// This seems to be broken since Prettier 3, and it'll always use its cumbersome cache. Hopefully it works one day.
useCache: false,
editorconfig: true,
});
if (fileConfig) {
configOptions = fileConfig;

Copilot uses AI. Check for mistakes.
Comment on lines 119 to +125
const resolvedConfig = {
filepath: filePath,
tabWidth: formatOptions.tabSize,
useTabs: !formatOptions.insertSpaces,
...editorOptions,
...configOptions,
};
} as any;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

resolvedConfig is force-cast to any, which removes type-checking for downstream uses (e.g. resolvedConfig.plugins) and can hide accidental option-shape regressions. Consider typing editorOptions/configOptions as Record<string, unknown> or Partial<prettier.Options> (via a type import) and giving the merged object an explicit options type so you can avoid as any here.

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +101
let configOptions = {};
try {
configOptions = await prettierInstance.resolveConfig(filePath, {
const resolvedConfig = await prettierInstance.resolveConfig(filePath, {
// This seems to be broken since Prettier 3, and it'll always use its cumbersome cache. Hopefully it works one day.
useCache: false,
editorconfig: true,
});
if (resolvedConfig) {
configOptions = resolvedConfig;
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

This change adjusts how Prettier config resolution behaves when resolveConfig() returns null, but there’s no regression test covering the reported trailing-comma scenario. Since the language server already has Prettier formatting tests (test/misc/prettier-format.test.ts), please add a test that formats an .astro document with a .prettierrc (e.g. trailingComma: "all") and asserts the formatted output preserves trailing commas, so this doesn’t regress again.

Copilot uses AI. Check for mistakes.
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.

🐛 BUG: version 2.8.1 removes trailing comma on save

2 participants