Prettier多HTML插件链式调用问题:如何让自定义类名拆分插件在tailwind插件后执行
Prettier多HTML插件链式调用问题:如何让自定义类名拆分插件在tailwind插件后执行
我完全理解你的处境——Prettier的插件链式调用尤其是HTML相关的,官方文档确实写得不够直观,特别是当你需要在特定插件之后执行自定义逻辑的时候。你已经实现了拆分长Tailwind类名的功能,但想把它从单独的后处理脚本整合到Prettier流程中,同时还要保证tailwindcss插件的排序结果不被破坏,又不想fork插件失去更新能力,这个需求非常合理。
核心思路
要实现自定义插件在prettier-plugin-tailwindcss之后执行,关键是利用Prettier的插件钩子执行顺序和AST处理时机:
- Tailwind插件会在HTML的AST处理阶段完成类名排序
- 我们的自定义插件需要在所有前置插件(包括Tailwind、organize-attributes等)处理完AST后,通过
postprocess钩子对最终AST进行修改,只拆分长类名,不破坏其他插件的处理结果
具体实现步骤
1. 调整自定义插件结构,实现postprocess钩子
修改你的自定义插件,让它针对HTML文件的最终AST进行处理,确保只在Tailwind排序后拆分类名:
// ./tools/prettier-split-classes-plugin.js module.exports = { name: 'prettier-plugin-split-tailwind-classes', // 只针对HTML文件生效 overrides: [ { files: '**/*.html', options: {}, }, ], // Postprocess钩子会在所有其他插件的AST处理完成后执行 postprocess: (ast, options) => { // 递归遍历HTML AST,找到所有class属性 const traverseNode = (node) => { // 处理元素节点的class属性 if (node.type === 'element' && Array.isArray(node.attributes)) { node.attributes.forEach(attr => { if (attr.name === 'class' && typeof attr.value === 'string' && attr.value.length > 0) { // 拆分已被Tailwind排序后的类名数组 const classList = attr.value.split(' ').filter(cls => cls.trim() !== ''); if (classList.length <= 1) return; // 短类名无需拆分 // 自定义拆分逻辑:按每行最大长度拆分(可根据需求调整) const maxLineLength = options.printWidth - 10; // 预留缩进空间 let currentLine = ''; const newClassLines = []; classList.forEach(cls => { const proposedLine = currentLine ? `${currentLine} ${cls}` : cls; if (proposedLine.length > maxLineLength && currentLine) { newClassLines.push(currentLine); currentLine = cls; } else { currentLine = proposedLine; } }); if (currentLine) newClassLines.push(currentLine); // 更新class属性的value为多行格式 attr.value = newClassLines.join('\n '); } }); } // 递归处理子节点 if (Array.isArray(node.children)) { node.children.forEach(traverseNode); } }; traverseNode(ast); return ast; }, };
2. 调整Prettier配置的插件顺序
在你的Prettier配置中,将自定义插件放在prettier-plugin-tailwindcss的后面——因为Prettier会按插件数组的顺序加载,但postprocess钩子会按加载顺序依次执行,最后加载的插件的钩子会在最末尾运行,正好拿到Tailwind处理后的结果:
{ "attributeGroups": [ "$ANGULAR_ELEMENT_REF", "$ANGULAR_STRUCTURAL_DIRECTIVE", "$ANGULAR_OUTPUT", "$ANGULAR_TWO_WAY_BINDING", "$ANGULAR_INPUT", "$DEFAULT" ], "attributeSort": "ASC", "experimentalOperatorPosition": "start", "overrides": [ { "files": "**/*.html", "options": { "htmlWhitespaceSensitivity": "ignore", "parser": "html" } } ], "plugins": [ "prettier-plugin-organize-attributes", "prettier-plugin-sort-json", "./tools/prettier-stylistic/dist/index.js", "prettier-plugin-tailwindcss", "./tools/prettier-split-classes-plugin.js" // 自定义插件放在最后 ], "printWidth": 120, "singleQuote": true, "trailingComma": "es5" }
3. 验证效果
运行Prettier命令(比如npx prettier --write src/**/*.html),检查结果:
- Tailwind类名的排序保持不变(由
prettier-plugin-tailwindcss处理) - 长类名被拆分为多行(由自定义插件处理)
- HTML属性的排序依然符合
prettier-plugin-organize-attributes的规则
关键注意事项
- AST结构兼容性:Prettier的HTML AST结构可能会随版本变化,如果你遇到遍历问题,可以临时用
console.log(ast)打印节点结构,调整遍历逻辑。 - 拆分逻辑自定义:你可以根据团队需求修改拆分规则(比如按Tailwind功能分组拆分,而不是按长度),只要在
traverseNode函数中调整classList的处理逻辑即可。 - 避免破坏其他插件结果:自定义插件只修改class属性的value,不要触碰节点的其他属性(比如顺序、其他属性值),确保其他插件的处理结果被完整保留。
内容来源于stack exchange
相关产品推荐
相关产品推荐

