Prettier 编程式 API 详解:从 format 到插件化的完整实践指南
【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier
Prettier 除了命令行之外,还暴露了一套完整的编程式 API,供编辑器插件、CI 工具、自定义格式引擎直接调用。本文基于仓库文档 docs/api.md 展开,完整覆盖format、check、formatWithCursor、resolveConfig等全部公开接口的用法与返回约定,并结合 src/index.js、src/main/core.js、src/config/resolve-config.js 等源码实现,解释每个 API 底层的执行路径,帮助你在自研工具链中正确、高效地集成 Prettier。
一、引入方式与 API 的整体形态
文档开头给出的标准引入方式:
import * as prettier from "prettier";所有公开 API 均为异步函数,返回Promise。这是 Prettier v3 的重要约定:如果必须使用同步版本,文档推荐借助第三方包装层(如@prettier/sync包)来桥接,而不是在核心 API 中提供同步入口。
从 package.json 的exports字段可以确认模块解析关系:
{ "exports": { ".": { "types": "./src/index.d.ts", "require": "./src/index.cjs", "default": "./src/index.js" }, "./standalone": "./src/standalone.js", "./plugins/*": "./src/plugins/*.js", "./*": "./*" }, "engines": { "node": ">=22" } }import "prettier"对应 src/index.js(ESM)或 src/index.cjs(CJS),完整功能入口;"prettier/standalone"对应 src/standalone.js,面向浏览器/独立运行的裁剪版;"prettier/plugins/*"对应 src/plugins/ 下按语言切分出的内置插件,供外部以插件形式显式加载;- 当前开发版本为
3.10.0-dev,运行时要求 Node.js>=22。
standalone 入口的 API 子集
对比 src/standalone.js 的导出列表:
export { debugApis as __debug, check, format, formatWithCursor, getSupportInfo, }; export * as doc from "./document/public.js"; export { default as version } from "./main/version.evaluate.js"; export * as util from "./utilities/public.js";可以看到 standalone 版只导出check、format、formatWithCursor、getSupportInfo(外加doc、util、version),并不包含resolveConfig、getFileInfo等依赖文件系统搜索的 API——这与其面向浏览器、无法自由搜索配置目录的定位一致。如果你的运行环境是 Node.js,则应使用主入口获取完整 API。
二、prettier.format(source, options)
format是最核心的接口,用于把一段文本格式化为 Prettier 风格。使用约定:
options.parser必须按照目标语言显式设置(可用解析器列表见 docs/options.md);- 或者改用
options.filepath,让 Prettier 根据文件扩展名推断解析器; - 其余 options 均可传入以覆盖默认值。
文档示例:
await prettier.format("foo ( );", { semi: false, parser: "babel" }); // -> 'foo()\n'源码实现
src/index.js 中format并不是独立实现,而是formatWithCursor的薄封装——内部强制把cursorOffset置为-1(表示不追踪光标),取出结果中的formatted字段返回:
const formatWithCursor = withPlugins(core.formatWithCursor); async function format(text, options) { const { formatted } = await formatWithCursor(text, { ...options, cursorOffset: -1, }); return formatted; }值得注意的是withPlugins包装器(src/index.js):它会在调用真正格式化逻辑之前,把options.plugins中的字符串/URL 路径通过loadBuiltinPlugins()和loadPlugins()加载为插件对象并注入选项。也就是说,plugins选项接受插件对象、文件路径或 URL,API 层会自动完成加载。
三、prettier.check(source, options)
check用于判断文件是否已经是 Prettier 格式化后的产物,返回Promise<boolean>。它与 CLI 的--check/--list-different参数语义一致,非常适合在 CI 中做格式校验:格式化失败即阻断流水线。
从源码看(src/index.js),它的实现极其简洁——直接执行一次format并与原文全等比较:
async function check(text, options) { return (await format(text, options)) === text; }这意味着check与format对配置的解读完全一致;若options中缺少parser,同样需要依赖filepath推断。
四、prettier.formatWithCursor(source, options):编辑器集成的关键
formatWithCursor在格式化代码的同时,把未格式化代码中的光标位置映射到格式化后的对应位置。这是编辑器集成的刚需:格式化后光标不能"跳走"。使用它时必须通过cursorOffset选项指明光标所在的字符偏移:
await prettier.formatWithCursor(" 1", { cursorOffset: 2, parser: "babel" }); // -> { formatted: '1;\n', cursorOffset: 1 }光标迁移算法
底层实现在 src/main/core.js 的formatWithCursor中,注释里完整描述了三步策略:
- 格式化前:基于 AST 找出包含光标的最小区域(叶子节点、两节点之间的空隙、或文档首尾);
- 格式化中:记录该区域被写到了新文本的什么位置;
- 格式化后:把光标当作一个特殊字符(
Symbol("cursor"))插入旧区域文本,对新旧区域做仅含插入/删除的 diff(src/main/core.js 使用diff库的diffArrays),从 diff 结果中反推出光标在新文本中的偏移:
const oldCursorNodeCharArray = oldCursorRegionText.split(""); oldCursorNodeCharArray.splice(cursorOffsetRelativeToOldCursorRegionStart, 0, CURSOR); const cursorNodeDiff = diffArrays(oldCursorNodeCharArray, newCursorNodeCharArray); let cursorOffset = newCursorRegionStart; for (const entry of cursorNodeDiff) { if (entry.removed) { if (entry.value.includes(CURSOR)) break; } else { cursorOffset += entry.count; } }此外还有几个值得了解的行为细节(见 src/main/core.js):
- BOM 处理:输入若以 BOM 开头会先剥离、格式化后再补回,光标偏移相应调整;
- 换行归一:
endOfLine: "auto"时按内容猜测换行符;CRLF输入会先归一为LF参与计算,输出时再转回; - Range 格式化:设置了
rangeStart/rangeEnd时走formatRange分支(src/main/core.js),只格式化片段并恢复原始缩进,光标在范围外时保持不动; - Pragmas:
requirePragma、insertPragma、checkIgnorePragma均在此层统一裁决,不满足条件时原样返回输入。
五、prettier.resolveConfig(fileUrlOrPath, options)
resolveConfig为某个源文件解析 Prettier 配置:从文件所在目录开始向上搜索配置文件;也可以直接把配置文件路径作为options.config传入以跳过搜索。返回 Promise:
- 找到配置时,resolve 为一个选项对象;
- 未找到时,resolve 为
null; - 配置文件解析出错时,Promise 被 reject。
文档给出的典型用法(读取文件 → 解析配置 → 格式化):
const text = await fs.readFile(filePath, "utf8"); const options = await prettier.resolveConfig(filePath); const formatted = await prettier.format(text, { ...options, filepath: filePath, });参数与底层行为
options.useCache:默认true,缓存目录结构以加速重复查询;设为false时完全绕过缓存(见 src/config/resolve-config.js 中options = { useCache: true, ...options })。options.editorconfig:设为true且项目存在.editorconfig时,Prettier 会解析它并转换为对应配置,但优先级低于.prettierrc等 Prettier 配置文件。目前支持的 EditorConfig 属性:end_of_lineindent_styleindent_size/tab_widthmax_line_length
实现上,resolveConfig会并行加载 Prettier 配置与 EditorConfig(src/config/resolve-config.js 中的Promise.all([loadPrettierConfig(...), loadEditorconfig(...)])),再合并:
const merged = { ...editorConfigured, // EditorConfig 垫底 ...mergeOverrides(result, filePath), // .prettierrc(含 overrides)覆盖 };其中mergeOverrides(src/config/resolve-config.js)会用micromatch匹配overrides[].files/excludeFiles(基于配置文件所在目录的相对路径),命中则用override.options覆盖基础选项。另外,配置里声明的plugins若为相对路径(以.开头),会被解析为相对于配置文件目录的绝对路径。
六、prettier.resolveConfigFile([fileUrlOrPath])
resolveConfigFile只回答一个问题:最终会使用哪个配置文件。返回 Promise:
- 找到时 resolve 为配置文件的路径字符串;
- 未找到时 resolve 为
null; - 解析出错时 reject。
搜索起点是process.cwd();若提供了fileUrlOrPath参数,则从该文件所在目录开始。对应实现见 src/config/resolve-config.js——它始终用shouldCache: false调用searchPrettierConfig,即本 API 自身不参与缓存复用。
const configFile = await prettier.resolveConfigFile(filePath); // you got the path of the configuration file它与resolveConfig是配套关系:resolveConfigFile给出路径,resolveConfig给出解析后的选项。
七、prettier.clearConfigCache()
当 Prettier 反复读取配置文件与插件时,会为性能缓存文件系统结构(resolveConfig系列默认启用缓存)。clearConfigCache用于主动清空该缓存——典型场景是编辑器集成方已知文件系统在两次格式化之间发生了变化(新增了.prettierrc、改动忽略规则等)。
从主入口的实现看(src/index.js),它实际清空的不仅是配置缓存:
async function clearCache() { clearConfigCache(); // 清 .prettierrc / .editorconfig 的缓存 clearPluginCache(); // 同时清插件加载缓存 }八、prettier.getFileInfo(fileUrlOrPath, options)
getFileInfo面向编辑器扩展:在真正格式化之前先判断某个文件"要不要格式化、用哪个解析器"。它返回一个 Promise,resolve 为:
{ ignored: boolean; inferredParser: string | null; }约束与选项:
- 第一个参数必须是
string或URL,否则 Promise 被 reject(见 src/common/get-file-info.js 的TypeError抛出); options.ignorePath(string | URL | (string | URL)[]):指定.prettierignore之类的忽略文件;options.withNodeModules(boolean):是否把node_modules也视为可忽略对象;二者共同影响ignored的取值;- 若文件被忽略,
inferredParser恒为null; options.plugins((string | URL | Plugin)[]):提供插件路径有助于为 Prettier 核心不直接支持的文件类型推断出inferredParser;options.resolveConfig(boolean,默认true):设为false时跳过配置文件搜索,适合"只关心是否被忽略"的低成本调用。
从源码看(src/common/get-file-info.js),推断解析器的优先级是:显式options.parser→ 配置文件中声明的parser→ 根据插件的语言描述与文件扩展名推断(inferParser)。另外源码注释特别提到:本 API 的plugins期望是路径数组,与format等接口的插件加载方式有意区分(涉及 VS Code 扩展的兼容历史)。
九、prettier.getSupportInfo()
getSupportInfo()返回一个 Promise,resolve 为描述 Prettier 当前支持范围的SupportInfo对象:
{ languages: Array<{ name: string; parsers: string[]; group?: string; tmScope?: string; aceMode?: string; codemirrorMode?: string; codemirrorMimeType?: string; aliases?: string[]; extensions?: string[]; filenames?: string[]; linguistLanguageId?: number; vscodeLanguageIds?: string[]; isSupported?(options: { filepath: string }): boolean; }>; options: SupportOption[]; }(options部分的结构定义见 src/index.d.ts 的SupportInfo。)它的典型用途是给编辑器补全 Prettier 选项、为语言动态推荐parser。实现位于 src/main/support.js:从所有插件聚合languages,合并插件options与核心选项定义,并可通过showDeprecated参数决定是否保留已废弃的选项与选项值。
一个来自文档的注意点:Prettier 无法保证filepath在磁盘上真实存在;若通过 API(如prettier.format())使用,连路径是否有效都无法保证——因此依赖isSupported(options: { filepath })这类回调时,调用方需自行兜底。
十、Custom Parser API(已移除)与插件迁移
文档明确标记:Custom Parser API 已在 v3.0.0 中移除,被 Plugin API 取代。在插件出现之前,parser选项可以直接传一个函数:
// ❌ Custom parser API (removed) import { format } from "prettier"; format("lodash ( )", { parser(text, { babel }) { const ast = babel(text); ast.program.body[0].expression.callee.name = "_"; return ast; }, }); // -> "_();\n"等价迁移到 Plugin API 后,需要定义一个含parsers的插件对象,并在选项里通过plugins显式传入:
// ✔️ Plugin API import { format } from "prettier"; import * as prettierPluginBabel from "prettier/plugins/babel"; const myCustomPlugin = { parsers: { "my-custom-parser": { async parse(text) { const ast = await prettierPluginBabel.parsers.babel.parse(text); ast.program.body[0].expression.callee.name = "_"; return ast; }, astFormat: "estree", }, }, }; await format("lodash ( )", { parser: "my-custom-parser", plugins: [myCustomPlugin], }); // -> "_();\n"迁移要点:
- 解析器变成具名函数,通过
parser: "my-custom-parser"引用;astFormat声明产物 AST 的格式(此处复用内置的estree打印器); parse可以是async,parse的入参是(text, options);- 插件对象可内联传入
plugins数组,也可通过文件路径加载(prettier/plugins/*导出见 package.json 的exports,各语言内置插件见 src/plugins/)。
文档同时警告:用这种方式做 codemod 并不推荐。Prettier 依赖 AST 节点上的位置信息(locStart/locEnd)来保留空行、挂载注释等;在解析后再修改 AST,位置信息很容易与新结构失配,导致不可预测的输出。文档建议:需要 codemod 请考虑专用工具(如 jscodeshift),而不是借道 Prettier 的自定义解析器。
此外,旧的--parser选项允许传入"导出parse函数的模块路径",现已统一改为用--pluginCLI 选项或 API 的plugins选项来加载插件,详见 docs/plugins.md。
十一、API 速查与选型建议
| API | 返回 | 典型场景 | 实现入口 |
|---|---|---|---|
format(source, options) | Promise<string> | 任何"给我格式化结果"的调用 | src/index.js |
check(source, options) | Promise<boolean> | CI 格式校验(等价--check) | src/index.js |
formatWithCursor(source, options) | Promise<{formatted, cursorOffset}> | 编辑器 LSP/扩展集成 | src/main/core.js |
resolveConfig(file, options) | Promise<Options \| null> | 按文件解析配置(含 overrides / EditorConfig) | src/config/resolve-config.js |
resolveConfigFile([file]) | Promise<string \| null> | 只查配置文件路径 | src/config/resolve-config.js |
clearConfigCache() | Promise<void> | 文件系统变化后清理缓存 | src/index.js |
getFileInfo(file, options) | Promise<{ignored, inferredParser}> | 编辑器决定"是否格式化" | src/common/get-file-info.js |
getSupportInfo([options]) | Promise<SupportInfo> | 选项/语言元数据、自动补全 | src/main/support.js |
选型建议(依据上文源码行为归纳):
- 纯格式化:用
format;需要保持光标则换成formatWithCursor并传cursorOffset; - CI 校验:用
check,失败即非零退出; - 需要尊重项目配置:先
resolveConfig,再与filepath一起传给format;配置文件会热更新时记得在合适时机clearConfigCache; - 编辑器"要不要管这个文件":先
getFileInfo(可设resolveConfig: false提速),ignored为false且inferredParser非空才进入格式化; - 浏览器/无文件系统环境:只能用
standalone入口暴露的format/check/formatWithCursor/getSupportInfo子集,配置需要由宿主自行解析后作为options传入。
配套的完整选项定义(RequiredOptions、Plugin、SupportInfo等类型)可参阅仓库自带的 src/index.d.ts,各选项的取值与默认值说明见 docs/options.md,配置文件的搜索与overrides规则见 docs/configuration.md。
【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考