From 5a440724c7be37f7fdebe297f2493fd7d9bc533a Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:06:54 +0200 Subject: [PATCH] fix: fallback to bundled Prettier when finding Prettier 2 (#960) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: override prettier version in yaml–language–server * chore: changeset * chore: lockfile * feat: more robust package loading --- .changeset/grumpy-planes-sip.md | 7 ++ packages/language-server/src/core/index.ts | 7 +- packages/language-server/src/importPackage.ts | 66 +++++++++++++++---- packages/language-server/src/utils.ts | 63 +++--------------- 4 files changed, 75 insertions(+), 68 deletions(-) create mode 100644 .changeset/grumpy-planes-sip.md diff --git a/.changeset/grumpy-planes-sip.md b/.changeset/grumpy-planes-sip.md new file mode 100644 index 00000000..53d5fb0a --- /dev/null +++ b/.changeset/grumpy-planes-sip.md @@ -0,0 +1,7 @@ +--- +"@astrojs/language-server": patch +"@astrojs/check": patch +"astro-vscode": patch +--- + +Fixes formatting not working by default in certain circumstances diff --git a/packages/language-server/src/core/index.ts b/packages/language-server/src/core/index.ts index c9cfcde4..27f407e1 100644 --- a/packages/language-server/src/core/index.ts +++ b/packages/language-server/src/core/index.ts @@ -10,7 +10,8 @@ import type { TypeScriptExtraServiceScript } from '@volar/typescript'; import type ts from 'typescript'; import type { HTMLDocument } from 'vscode-html-languageservice'; import type { URI } from 'vscode-uri'; -import { type AstroInstall, getLanguageServerTypesDir } from '../utils.js'; +import type { PackageInfo } from '../importPackage.js'; +import { getLanguageServerTypesDir } from '../utils.js'; import { astro2tsx } from './astro2tsx'; import type { AstroMetadata } from './parseAstro'; import { getAstroMetadata } from './parseAstro'; @@ -21,7 +22,7 @@ import { extractScriptTags } from './parseJS.js'; const decoratedHosts = new WeakSet(); export function addAstroTypes( - astroInstall: AstroInstall | undefined, + astroInstall: PackageInfo | undefined, ts: typeof import('typescript'), host: ts.LanguageServiceHost, ) { @@ -41,7 +42,7 @@ export function addAstroTypes( if (astroInstall) { addedFileNames.push( ...['./env.d.ts', './astro-jsx.d.ts'].map((filePath) => - ts.sys.resolvePath(path.resolve(astroInstall.path, filePath)), + ts.sys.resolvePath(path.resolve(astroInstall.directory, filePath)), ), ); diff --git a/packages/language-server/src/importPackage.ts b/packages/language-server/src/importPackage.ts index f09372a1..e2115482 100644 --- a/packages/language-server/src/importPackage.ts +++ b/packages/language-server/src/importPackage.ts @@ -5,36 +5,48 @@ import type * as svelte from '@astrojs/svelte/dist/editor.cjs'; import type * as vue from '@astrojs/vue/dist/editor.cjs'; import type * as prettier from 'prettier'; +type PackageVersion = { + full: string; + major: number; + minor: number; + patch: number; +}; + let isTrusted = true; export function setIsTrusted(_isTrusted: boolean) { isTrusted = _isTrusted; } +export type PackageInfo = { + entrypoint: string; + directory: string; + version: PackageVersion; +}; + /** * Get the path of a package's directory from the paths in `fromPath`, if `root` is set to false, it will return the path of the package's entry point */ -export function getPackagePath( - packageName: string, - fromPath: string[], - root = true, -): string | undefined { +export function getPackageInfo(packageName: string, fromPath: string[]): PackageInfo | undefined { const paths = []; if (isTrusted) { paths.unshift(...fromPath); } try { - return root - ? dirname(require.resolve(packageName + '/package.json', { paths })) - : require.resolve(packageName, { paths }); + const packageJSON = require.resolve(packageName + '/package.json', { paths }); + return { + directory: dirname(packageJSON), + entrypoint: require.resolve(packageName, { paths }), + version: parsePackageVersion(require(packageJSON).version), + }; } catch { return undefined; } } function importEditorIntegration(packageName: string, fromPath: string): T | undefined { - const pkgPath = getPackagePath(packageName, [fromPath]); + const pkgPath = getPackageInfo(packageName, [fromPath])?.directory; if (pkgPath) { try { @@ -66,17 +78,31 @@ export function importVueIntegration(fromPath: string): typeof vue | undefined { } export function importPrettier(fromPath: string): typeof prettier | undefined { - const prettierPkg = getPackagePath('prettier', [fromPath, __dirname]); + let prettierPkg = getPackageInfo('prettier', [fromPath, __dirname]); if (!prettierPkg) { return undefined; } - return require(prettierPkg); + if (prettierPkg.version.major < 3) { + console.error( + `Prettier version ${prettierPkg.version.full} from ${prettierPkg.directory} is not supported, please update to at least version 3.0.0. Falling back to bundled version to ensure formatting works correctly.`, + ); + + prettierPkg = getPackageInfo('prettier', [__dirname]); + if (!prettierPkg) { + return undefined; + } + } + + return require(prettierPkg.entrypoint); } export function getPrettierPluginPath(fromPath: string): string | undefined { - const prettierPluginPath = getPackagePath('prettier-plugin-astro', [fromPath, __dirname], false); + const prettierPluginPath = getPackageInfo('prettier-plugin-astro', [ + fromPath, + __dirname, + ])?.entrypoint; if (!prettierPluginPath) { return undefined; @@ -94,3 +120,19 @@ export function getWorkspacePnpPath(workspacePath: string): string | null { return null; } } + +export function parsePackageVersion(version: string): PackageVersion { + let [major, minor, patch] = version.split('.'); + + if (patch.includes('-')) { + const patchParts = patch.split('-'); + patch = patchParts[0]; + } + + return { + full: version, + major: Number(major), + minor: Number(minor), + patch: Number(patch), + }; +} diff --git a/packages/language-server/src/utils.ts b/packages/language-server/src/utils.ts index 1ff3a515..db8d8175 100644 --- a/packages/language-server/src/utils.ts +++ b/packages/language-server/src/utils.ts @@ -1,15 +1,6 @@ import * as path from 'node:path'; -import { getPackagePath } from './importPackage.js'; - -export interface AstroInstall { - path: string; - version: { - full: string; - major: number; - minor: number; - patch: number; - }; -} +import type { PackageInfo } from './importPackage.js'; +import { getPackageInfo } from './importPackage.js'; export function getLanguageServerTypesDir(ts: typeof import('typescript')) { return ts.sys.resolvePath(path.resolve(__dirname, '../types')); @@ -21,10 +12,7 @@ export function getAstroInstall( nearestPackageJson: string | undefined; readDirectory: typeof import('typescript').sys.readDirectory; }, -): AstroInstall | 'not-an-astro-project' | 'not-found' { - let astroPath; - let version; - +): PackageInfo | 'not-an-astro-project' | 'not-found' { if (checkForAstro && checkForAstro.nearestPackageJson) { basePaths.push(path.dirname(checkForAstro.nearestPackageJson)); @@ -53,52 +41,21 @@ export function getAstroInstall( } } - try { - astroPath = getPackagePath('astro', basePaths); - - if (!astroPath) { - throw Error; - } - - version = require(path.resolve(astroPath, 'package.json')).version; - } catch { - // If we couldn't find it inside the workspace's node_modules, it might means we're in the monorepo - try { - astroPath = getPackagePath('./packages/astro', basePaths); + let astroPackage = getPackageInfo('astro', basePaths); - if (!astroPath) { - throw Error; - } + if (!astroPackage) { + // If we couldn't find it inside the workspace's node_modules, it might means we're in the Astro development monorepo + astroPackage = getPackageInfo('./packages/astro', basePaths); - version = require(path.resolve(astroPath, 'package.json')).version; - } catch { - // If we still couldn't find it, it probably just doesn't exist + if (!astroPackage) { console.error( `${basePaths[0]} seems to be an Astro project, but we couldn't find Astro or Astro is not installed`, ); + // If we still couldn't find it, it probably just doesn't exist return 'not-found'; } } - if (!version) { - return 'not-found'; - } - - let [major, minor, patch] = version.split('.'); - - if (patch.includes('-')) { - const patchParts = patch.split('-'); - patch = patchParts[0]; - } - - return { - path: astroPath, - version: { - full: version, - major: Number(major), - minor: Number(minor), - patch: Number(patch), - }, - }; + return astroPackage; }