diff --git a/CHANGELOG.md b/CHANGELOG.md index a9663dc6b..fca722c26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to the "prettier-vscode" extension will be documented in thi +## [Unreleased] + +- Add support for global Prettier plugins via `prettier.plugins` setting + ## [11.0.0] - [BREAKING CHANGE] Prevent `.editorconfig` from satisfying the `requireConfig` setting (#2708) - Thanks to [@redoPop](https://github.com/redoPop) diff --git a/package.json b/package.json index 7942842ca..00beb4207 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ }, "dependencies": { "find-up": "5.0.0", - "prettier": "^2.8.8", + "prettier": "^3.6.2", "resolve": "^1.22.8", "semver": "^7.6.3", "vscode-nls": "^5.2.0" @@ -154,6 +154,11 @@ "type": "object", "title": "%ext.config.title%", "properties": { + "prettier.plugins": { + "type": "array", + "default": [], + "markdownDescription": "%ext.config.plugins%" + }, "prettier.disableLanguages": { "type": "array", "items": { diff --git a/package.nls.json b/package.nls.json index f29ea0ee8..8943f316b 100644 --- a/package.nls.json +++ b/package.nls.json @@ -1,6 +1,7 @@ { "ext.command.createConfigFile.title": "Prettier: Create Configuration File", "ext.command.forceFormatDocument.title": "Format Document (Forced)", + "ext.config.plugins": "A list of plugins to automatically install and load globally.", "ext.config.arrowParens": "Include parentheses around a sole arrow function parameter.", "ext.config.bracketSpacing": "Controls the printing of spaces inside object literals.", "ext.config.configPath": "Path to the prettier configuration file.", diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json index 136bc4156..2cf5444ce 100644 --- a/package.nls.zh-cn.json +++ b/package.nls.zh-cn.json @@ -1,6 +1,7 @@ { "ext.command.createConfigFile.title": "Prettier:选择配置文件", "ext.command.forceFormatDocument.title": "格式化文件(强制)", + "ext.config.plugins": "一组插件,用于自动全局安装和加载。", "ext.config.arrowParens": "箭头函数仅有一个参数时,参数是否添加括号。", "ext.config.bracketSpacing": "在对象字面量的花括号内侧使用空格作为间隔。", "ext.config.configPath": "指定 Prettier 配置文件的路径。", diff --git a/package.nls.zh-tw.json b/package.nls.zh-tw.json index 947aa198e..a90031f2f 100644 --- a/package.nls.zh-tw.json +++ b/package.nls.zh-tw.json @@ -1,6 +1,7 @@ { "ext.command.createConfigFile.title": "Prettier: 建立組態檔", "ext.command.forceFormatDocument.title": "排版文件(強制)", + "ext.config.plugins": "一組插件,用於自動全域安裝和加載。", "ext.config.arrowParens": "箭頭函式中只有一個參數也加上括號。", "ext.config.bracketSpacing": "控制物件字面值中兩側的留白。", "ext.config.configPath": "Prettier 組態檔的路徑。", diff --git a/src/BrowserModuleResolver.ts b/src/BrowserModuleResolver.ts index d2414235a..e2326f5d4 100644 --- a/src/BrowserModuleResolver.ts +++ b/src/BrowserModuleResolver.ts @@ -1,10 +1,10 @@ import { + ModuleResolverInterface, PrettierFileInfoOptions, PrettierFileInfoResult, - PrettierSupportLanguage, PrettierModule, PrettierOptions, - ModuleResolverInterface, + PrettierSupportLanguage, PrettierVSCodeConfig, } from "./types"; @@ -26,10 +26,10 @@ import * as yamlPlugin from "prettier/parser-yaml"; //import * as flowPlugin from "prettier/parser-flow"; //import * as postcssPlugin from "prettier/parser-postcss"; +import { Options, Plugin, ResolveConfigOptions } from "prettier"; import { TextDocument, Uri } from "vscode"; import { LoggingService } from "./LoggingService"; import { getWorkspaceRelativePath } from "./util"; -import { ResolveConfigOptions, Options } from "prettier"; const plugins = [ angularPlugin, @@ -41,21 +41,21 @@ const plugins = [ meriyahPlugin, typescriptPlugin, yamlPlugin, -]; +] as unknown as Plugin[]; export class ModuleResolver implements ModuleResolverInterface { constructor(private loggingService: LoggingService) {} public async getPrettierInstance( // eslint-disable-next-line @typescript-eslint/no-unused-vars - _fileName: string + _fileName: string, ): Promise { return this.getGlobalPrettierInstance(); } public async getResolvedIgnorePath( fileName: string, - ignorePath: string + ignorePath: string, ): Promise { return getWorkspaceRelativePath(fileName, ignorePath); } @@ -66,7 +66,9 @@ export class ModuleResolver implements ModuleResolverInterface { format: (source: string, options: PrettierOptions) => { return prettierStandalone.format(source, { ...options, plugins }); }, - getSupportInfo: (): { languages: PrettierSupportLanguage[] } => { + getSupportInfo: async (): Promise<{ + languages: PrettierSupportLanguage[]; + }> => { return { languages: [ { @@ -167,7 +169,7 @@ export class ModuleResolver implements ModuleResolverInterface { // eslint-disable-next-line @typescript-eslint/no-unused-vars filePath: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars - options?: PrettierFileInfoOptions + options?: PrettierFileInfoOptions, ): Promise => { // TODO: implement ignore file reading return { ignored: false, inferredParser: null }; @@ -181,7 +183,7 @@ export class ModuleResolver implements ModuleResolverInterface { resolveConfigFile(filePath?: string | undefined): Promise; resolveConfig( fileName: string, - options?: ResolveConfigOptions | undefined + options?: ResolveConfigOptions | undefined, ): Promise; }, // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -189,7 +191,7 @@ export class ModuleResolver implements ModuleResolverInterface { // eslint-disable-next-line @typescript-eslint/no-unused-vars fileName: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars - vscodeConfig: PrettierVSCodeConfig + vscodeConfig: PrettierVSCodeConfig, ): Promise { return null; } @@ -198,11 +200,18 @@ export class ModuleResolver implements ModuleResolverInterface { // eslint-disable-next-line @typescript-eslint/no-unused-vars _doc: TextDocument, // eslint-disable-next-line @typescript-eslint/no-unused-vars - _vscodeConfig: PrettierVSCodeConfig + _vscodeConfig: PrettierVSCodeConfig, ): Promise<"error" | "disabled" | PrettierOptions | null> { return null; } + resolvePluginsGlobally( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + plugins: string[], + ): string[] { + return []; + } + dispose() { // nothing to do } diff --git a/src/ModuleResolver.ts b/src/ModuleResolver.ts index 7b89bf528..c8bb4791e 100644 --- a/src/ModuleResolver.ts +++ b/src/ModuleResolver.ts @@ -16,6 +16,10 @@ import { UNTRUSTED_WORKSPACE_USING_BUNDLED_PRETTIER, USING_BUNDLED_PRETTIER, } from "./message"; +import { loadNodeModule, resolveConfigPlugins } from "./ModuleLoader"; +import { PrettierInstance } from "./PrettierInstance"; +import { PrettierMainThreadInstance } from "./PrettierMainThreadInstance"; +import { PrettierWorkerInstance } from "./PrettierWorkerInstance"; import { ModuleResolverInterface, PackageManagers, @@ -23,11 +27,12 @@ import { PrettierResolveConfigOptions, PrettierVSCodeConfig, } from "./types"; -import { getConfig, getWorkspaceRelativePath, isAboveV3 } from "./util"; -import { PrettierWorkerInstance } from "./PrettierWorkerInstance"; -import { PrettierInstance } from "./PrettierInstance"; -import { PrettierMainThreadInstance } from "./PrettierMainThreadInstance"; -import { loadNodeModule, resolveConfigPlugins } from "./ModuleLoader"; +import { + getConfig, + getPackageInfo, + getWorkspaceRelativePath, + isAboveV3, +} from "./util"; const minPrettierVersion = "1.13.0"; @@ -57,6 +62,8 @@ const fsStatSyncWorkaround = ( // @ts-expect-error Workaround for https://github.com/prettier/prettier-vscode/issues/3020 fs.statSync = fsStatSyncWorkaround; +declare const __non_webpack_require__: NodeRequire; + const globalPaths: { [key: string]: { cache: string | undefined; get(): string | undefined }; } = { @@ -95,6 +102,7 @@ function globalPathGet(packageManager: PackageManagers): string | undefined { export class ModuleResolver implements ModuleResolverInterface { private findPkgCache: Map; private ignorePathCache = new Map(); + private pluginsCache = new Map(); private path2Module = new Map(); @@ -121,7 +129,7 @@ export class ModuleResolver implements ModuleResolverInterface { return pkgFilePath; } }, - { cwd } + { cwd }, ); if (!packageJsonPath) { @@ -344,6 +352,52 @@ export class ModuleResolver implements ModuleResolverInterface { return fileName; } + public resolvePluginsGlobally(plugins: string[]): string[] { + if (plugins.length === 0) { + return []; + } + + const cacheKey = plugins.sort().join(","); + + if (this.pluginsCache.has(cacheKey)) { + return this.pluginsCache.get(cacheKey)!; + } + + const pluginsDirectory = path.join(__dirname, "..", "plugins"); + + if (!fs.existsSync(pluginsDirectory)) { + fs.mkdirSync(pluginsDirectory, { recursive: true }); + } + + try { + this.loggingService.logInfo( + `Installing ${plugins.length} plugins at ${pluginsDirectory}`, + ); + + if (!fs.existsSync(path.join(pluginsDirectory, "package.json"))) { + execSync("npm init -y", { cwd: pluginsDirectory }); + } + + execSync(`npm install ${plugins.join(" ")}`, { cwd: pluginsDirectory }); + + const resolvedPlugins = plugins.map((plugin) => + __non_webpack_require__.resolve(getPackageInfo(plugin).name, { + paths: [path.join(pluginsDirectory, "node_modules")], + }), + ); + + this.loggingService.logInfo(`Plugins installed successfully.`); + + this.pluginsCache.clear(); + this.pluginsCache.set(cacheKey, resolvedPlugins); + + return resolvedPlugins; + } catch (error) { + this.loggingService.logError(`Failed to install plugins.`, error); + return []; + } + } + public async resolveConfig( prettierInstance: { version: string | null; @@ -382,8 +436,8 @@ export class ModuleResolver implements ModuleResolverInterface { config: isVirtual ? undefined : vscodeConfig.configPath - ? getWorkspaceRelativePath(fileName, vscodeConfig.configPath) - : configPath, + ? getWorkspaceRelativePath(fileName, vscodeConfig.configPath) + : configPath, editorconfig: isVirtual ? undefined : vscodeConfig.useEditorConfig, }; diff --git a/src/PrettierEditService.ts b/src/PrettierEditService.ts index 4e0d437c7..31c6be6f7 100644 --- a/src/PrettierEditService.ts +++ b/src/PrettierEditService.ts @@ -73,7 +73,7 @@ export default class PrettierEditService implements Disposable { constructor( private moduleResolver: ModuleResolverInterface, private loggingService: LoggingService, - private statusBar: StatusBar + private statusBar: StatusBar, ) {} public registerDisposables(): Disposable[] { @@ -91,14 +91,14 @@ export default class PrettierEditService implements Disposable { }); const prettierConfigWatcher = workspace.createFileSystemWatcher( - `**/{${PRETTIER_CONFIG_FILES.join(",")}}` + `**/{${PRETTIER_CONFIG_FILES.join(",")}}`, ); prettierConfigWatcher.onDidChange(this.prettierConfigChanged); prettierConfigWatcher.onDidCreate(this.prettierConfigChanged); prettierConfigWatcher.onDidDelete(this.prettierConfigChanged); const textEditorChange = window.onDidChangeActiveTextEditor( - this.handleActiveTextEditorChangedSync + this.handleActiveTextEditorChangedSync, ); this.handleActiveTextEditorChangedSync(window.activeTextEditor); @@ -116,13 +116,13 @@ export default class PrettierEditService implements Disposable { const editor = window.activeTextEditor; if (!editor) { this.loggingService.logInfo( - "No active document. Nothing was formatted." + "No active document. Nothing was formatted.", ); return; } this.loggingService.logInfo( - "Forced formatting will not use ignore files." + "Forced formatting will not use ignore files.", ); const edits = await this.provideEdits(editor.document, { force: true }); @@ -152,7 +152,7 @@ export default class PrettierEditService implements Disposable { }; private handleActiveTextEditorChangedSync = ( - textEditor: TextEditor | undefined + textEditor: TextEditor | undefined, ) => { this.handleActiveTextEditorChanged(textEditor).catch((err) => { this.loggingService.logError("Error handling text editor change", err); @@ -160,7 +160,7 @@ export default class PrettierEditService implements Disposable { }; private handleActiveTextEditorChanged = async ( - textEditor: TextEditor | undefined + textEditor: TextEditor | undefined, ) => { if (!textEditor) { this.statusBar.hide(); @@ -183,11 +183,11 @@ export default class PrettierEditService implements Disposable { } const prettierInstance = await this.moduleResolver.getPrettierInstance( - workspaceFolder.uri.fsPath + workspaceFolder.uri.fsPath, ); const isRegistered = this.registeredWorkspaces.has( - workspaceFolder.uri.fsPath + workspaceFolder.uri.fsPath, ); // If there isn't an instance here, it is because the module @@ -200,7 +200,7 @@ export default class PrettierEditService implements Disposable { const selectors = await this.getSelectors( prettierInstance, document.uri, - workspaceFolder.uri + workspaceFolder.uri, ); this.statusBar.updateConfig({ @@ -212,7 +212,7 @@ export default class PrettierEditService implements Disposable { this.registeredWorkspaces.add(workspaceFolder.uri.fsPath); this.loggingService.logDebug( `Enabling Prettier for Workspace ${workspaceFolder.uri.fsPath}`, - selectors + selectors, ); } @@ -226,7 +226,7 @@ export default class PrettierEditService implements Disposable { public async registerGlobal() { const selectors = await this.getSelectors( - this.moduleResolver.getGlobalPrettierInstance() + this.moduleResolver.getGlobalPrettierInstance(), ); this.registerDocumentFormatEditorProviders(selectors); this.loggingService.logDebug("Enabling Prettier globally", selectors); @@ -249,11 +249,11 @@ export default class PrettierEditService implements Disposable { this.rangeFormatterHandler = languages.registerDocumentRangeFormattingEditProvider( rangeLanguageSelector, - editProvider + editProvider, ); this.formatterHandler = languages.registerDocumentFormattingEditProvider( languageSelector, - editProvider + editProvider, ); } @@ -263,9 +263,12 @@ export default class PrettierEditService implements Disposable { private getSelectors = async ( prettierInstance: PrettierModule | PrettierInstance, documentUri?: Uri, - workspaceFolderUri?: Uri + workspaceFolderUri?: Uri, ): Promise => { - const plugins: (string | PrettierPlugin)[] = []; + const vscodeConfig = getConfig(documentUri); + + const plugins: (string | URL | PrettierPlugin)[] = + this.moduleResolver.resolvePluginsGlobally(vscodeConfig.plugins); // Prettier v3 does not load plugins automatically // So need to resolve config to get plugins info. @@ -278,7 +281,7 @@ export default class PrettierEditService implements Disposable { prettierInstance, documentUri, documentUri.fsPath, - getConfig(documentUri) + getConfig(documentUri), ); if (resolvedConfig === "error") { this.statusBar.update(FormatterStatus.Error); @@ -355,7 +358,7 @@ export default class PrettierEditService implements Disposable { private provideEdits = async ( document: TextDocument, - options: ExtensionFormattingOptions + options: ExtensionFormattingOptions, ): Promise => { const startTime = new Date().getTime(); const result = await this.format(document.getText(), document, options); @@ -405,7 +408,7 @@ export default class PrettierEditService implements Disposable { private async format( text: string, doc: TextDocument, - options: ExtensionFormattingOptions + options: ExtensionFormattingOptions, ): Promise { const { fileName, uri, languageId } = doc; @@ -413,9 +416,12 @@ export default class PrettierEditService implements Disposable { const vscodeConfig = getConfig(doc); + const resolvedVscodeConfigPlugins = + this.moduleResolver.resolvePluginsGlobally(vscodeConfig.plugins); + const resolvedConfig = await this.moduleResolver.getResolvedConfig( doc, - vscodeConfig + vscodeConfig, ); if (resolvedConfig === "error") { this.statusBar.update(FormatterStatus.Error); @@ -426,14 +432,13 @@ export default class PrettierEditService implements Disposable { return; } - const prettierInstance = await this.moduleResolver.getPrettierInstance( - fileName - ); + const prettierInstance = + await this.moduleResolver.getPrettierInstance(fileName); this.loggingService.logInfo("PrettierInstance:", prettierInstance); if (!prettierInstance) { this.loggingService.logError( - "Prettier could not be loaded. See previous logs for more information." + "Prettier could not be loaded. See previous logs for more information.", ); this.statusBar.update(FormatterStatus.Error); return; @@ -443,11 +448,11 @@ export default class PrettierEditService implements Disposable { if (vscodeConfig.ignorePath) { resolvedIgnorePath = await this.moduleResolver.getResolvedIgnorePath( fileName, - vscodeConfig.ignorePath + vscodeConfig.ignorePath, ); if (resolvedIgnorePath) { this.loggingService.logInfo( - `Using ignore file (if present) at ${resolvedIgnorePath}` + `Using ignore file (if present) at ${resolvedIgnorePath}`, ); } } @@ -456,9 +461,12 @@ export default class PrettierEditService implements Disposable { if (fileName) { fileInfo = await prettierInstance.getFileInfo(fileName, { ignorePath: resolvedIgnorePath, - plugins: resolvedConfig?.plugins?.filter( - (item): item is string => typeof item === "string" - ), + plugins: + resolvedConfig != null + ? resolvedConfig.plugins?.filter( + (item): item is string => typeof item === "string", + ) + : resolvedVscodeConfigPlugins, resolveConfig: true, withNodeModules: vscodeConfig.withNodeModules, }); @@ -480,7 +488,7 @@ export default class PrettierEditService implements Disposable { // somebody has registered a custom file extension without properly // configuring the parser in their prettier config. this.loggingService.logWarning( - `Parser not inferred, trying VS Code language.` + `Parser not inferred, trying VS Code language.`, ); const { languages } = await prettierInstance.getSupportInfo({ plugins: [], @@ -490,7 +498,7 @@ export default class PrettierEditService implements Disposable { if (!parser) { this.loggingService.logError( - `Failed to resolve a parser, skipping file. If you registered a custom file extension, be sure to configure the parser.` + `Failed to resolve a parser, skipping file. If you registered a custom file extension, be sure to configure the parser.`, ); this.statusBar.update(FormatterStatus.Error); return; @@ -500,8 +508,9 @@ export default class PrettierEditService implements Disposable { fileName, parser as PrettierBuiltInParserName, vscodeConfig, + resolvedVscodeConfigPlugins, resolvedConfig, - options + options, ); this.loggingService.logInfo("Prettier Options:", prettierOptions); @@ -510,7 +519,7 @@ export default class PrettierEditService implements Disposable { // Since Prettier v3, `format` returns Promise. const formattedText = await prettierInstance.format( text, - prettierOptions + prettierOptions, ); this.statusBar.update(FormatterStatus.Success); @@ -527,8 +536,9 @@ export default class PrettierEditService implements Disposable { fileName: string, parser: PrettierBuiltInParserName, vsCodeConfig: PrettierOptions, + resolvedVscodeConfigPlugins: string[], configOptions: PrettierOptions | null, - extensionFormattingOptions: ExtensionFormattingOptions + extensionFormattingOptions: ExtensionFormattingOptions, ): Partial { const fallbackToVSCodeConfig = configOptions === null; @@ -556,12 +566,13 @@ export default class PrettierEditService implements Disposable { vsCodeConfig.embeddedLanguageFormatting; vsOpts.vueIndentScriptAndStyle = vsCodeConfig.vueIndentScriptAndStyle; vsOpts.experimentalTernaries = vsCodeConfig.experimentalTernaries; + vsOpts.plugins = resolvedVscodeConfigPlugins; } this.loggingService.logInfo( fallbackToVSCodeConfig ? "No local configuration (i.e. .prettierrc or .editorconfig) detected, falling back to VS Code configuration" - : "Detected local configuration (i.e. .prettierrc or .editorconfig), VS Code configuration will not be used" + : "Detected local configuration (i.e. .prettierrc or .editorconfig), VS Code configuration will not be used", ); let rangeFormattingOptions: RangeFormattingOptions | undefined; diff --git a/src/PrettierInstance.ts b/src/PrettierInstance.ts index 5cc3a68e2..87eca6105 100644 --- a/src/PrettierInstance.ts +++ b/src/PrettierInstance.ts @@ -13,12 +13,12 @@ export interface PrettierInstance { format(source: string, options?: PrettierOptions): Promise; getFileInfo( filePath: string, - fileInfoOptions?: PrettierFileInfoOptions + fileInfoOptions?: PrettierFileInfoOptions, ): Promise; getSupportInfo({ plugins, }: { - plugins: (string | PrettierPlugin)[]; + plugins: (string | URL | PrettierPlugin)[]; }): Promise<{ languages: PrettierSupportLanguage[]; }>; @@ -26,7 +26,7 @@ export interface PrettierInstance { resolveConfigFile(filePath?: string): Promise; resolveConfig( fileName: string, - options?: ResolveConfigOptions + options?: ResolveConfigOptions, ): Promise; } diff --git a/src/PrettierMainThreadInstance.ts b/src/PrettierMainThreadInstance.ts index 1e552d0e4..023be980e 100644 --- a/src/PrettierMainThreadInstance.ts +++ b/src/PrettierMainThreadInstance.ts @@ -1,4 +1,6 @@ import { FileInfoOptions, Options, ResolveConfigOptions } from "prettier"; +import { loadNodeModule } from "./ModuleLoader"; +import { PrettierNodeModule } from "./ModuleResolver"; import { PrettierInstance, PrettierInstanceConstructor, @@ -8,8 +10,6 @@ import { PrettierPlugin, PrettierSupportLanguage, } from "./types"; -import { PrettierNodeModule } from "./ModuleResolver"; -import { loadNodeModule } from "./ModuleLoader"; export const PrettierMainThreadInstance: PrettierInstanceConstructor = class PrettierMainThreadInstance implements PrettierInstance @@ -30,7 +30,7 @@ export const PrettierMainThreadInstance: PrettierInstanceConstructor = class Pre public async format( source: string, - options?: Options | undefined + options?: Options | undefined, ): Promise { if (!this.prettierModule) { await this.import(); @@ -40,7 +40,7 @@ export const PrettierMainThreadInstance: PrettierInstanceConstructor = class Pre public async getFileInfo( filePath: string, - fileInfoOptions?: FileInfoOptions | undefined + fileInfoOptions?: FileInfoOptions | undefined, ): Promise { if (!this.prettierModule) { await this.import(); @@ -58,7 +58,6 @@ export const PrettierMainThreadInstance: PrettierInstanceConstructor = class Pre if (!this.prettierModule) { await this.import(); } - // @ts-expect-error actually getSupportInfo can recieve option return this.prettierModule!.getSupportInfo({ plugins }); } @@ -70,7 +69,7 @@ export const PrettierMainThreadInstance: PrettierInstanceConstructor = class Pre } public async resolveConfigFile( - filePath?: string | undefined + filePath?: string | undefined, ): Promise { if (!this.prettierModule) { await this.import(); @@ -80,7 +79,7 @@ export const PrettierMainThreadInstance: PrettierInstanceConstructor = class Pre public async resolveConfig( fileName: string, - options?: ResolveConfigOptions | undefined + options?: ResolveConfigOptions | undefined, ): Promise { if (!this.prettierModule) { await this.import(); diff --git a/src/types.d.ts b/src/types.d.ts index 6fc041d6c..df92b7647 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -19,26 +19,26 @@ type PrettierFileInfoOptions = prettier.FileInfoOptions; type PrettierPlugin = prettier.Plugin; type PrettierModule = { - format(source: string, options?: prettier.Options): string; - getSupportInfo(): { languages: PrettierSupportLanguage[] }; + format(source: string, options?: prettier.Options): Promise; + getSupportInfo(): Promise<{ languages: PrettierSupportLanguage[] }>; getFileInfo( filePath: string, - options?: PrettierFileInfoOptions + options?: PrettierFileInfoOptions, ): Promise; }; type ModuleResolverInterface = { getPrettierInstance( - fileName: string + fileName: string, ): Promise; getResolvedIgnorePath( fileName: string, - ignorePath: string + ignorePath: string, ): Promise; getGlobalPrettierInstance(): PrettierModule; getResolvedConfig( doc: TextDocument, - vscodeConfig: PrettierVSCodeConfig + vscodeConfig: PrettierVSCodeConfig, ): Promise<"error" | "disabled" | PrettierOptions | null>; dispose(): void; resolveConfig( @@ -46,13 +46,14 @@ type ModuleResolverInterface = { resolveConfigFile(filePath?: string): Promise; resolveConfig( fileName: string, - options?: prettier.ResolveConfigOptions + options?: prettier.ResolveConfigOptions, ): Promise; }, uri: Uri, fileName: string, - vscodeConfig: PrettierVSCodeConfig + vscodeConfig: PrettierVSCodeConfig, ): Promise<"error" | "disabled" | PrettierOptions | null>; + resolvePluginsGlobally(plugins: string[]): string[]; }; type TrailingCommaOption = "none" | "es5" | "all"; @@ -103,6 +104,10 @@ interface IExtensionConfig { * If true, enabled debug logs */ enableDebugLogs: boolean; + /** + * A list of plugins to automatically install and load globally. + */ + plugins: string[]; } /** * Configuration for prettier-vscode diff --git a/src/util.ts b/src/util.ts index 56f6d5615..e0a6275ee 100644 --- a/src/util.ts +++ b/src/util.ts @@ -6,7 +6,7 @@ import { PrettierVSCodeConfig } from "./types"; export function getWorkspaceRelativePath( filePath: string, - pathToResolve: string + pathToResolve: string, ) { // In case the user wants to use ~/.prettierrc on Mac if ( @@ -31,7 +31,7 @@ export function getConfig(scope?: TextDocument | Uri): PrettierVSCodeConfig { // eslint-disable-next-line @typescript-eslint/no-explicit-any const config = workspace.getConfiguration( "prettier", - scope + scope, ) as unknown as PrettierVSCodeConfig; // Some settings are disabled for untrusted workspaces @@ -60,3 +60,19 @@ export function isAboveV3(version: string | null): boolean { } return parsedVersion.major >= 3; } + +export function getPackageInfo(packageSpecification: string): { + name: string; + version: string | undefined; +} { + const atIndex = packageSpecification.lastIndexOf("@"); + + if (atIndex > 0) { + const name = packageSpecification.slice(0, atIndex); + const version = packageSpecification.slice(atIndex + 1) || undefined; + + return { name, version }; + } + + return { name: packageSpecification, version: undefined }; +} diff --git a/yarn.lock b/yarn.lock index 9747b4e42..493ef1651 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3252,10 +3252,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^2.8.8: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" + integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.1"