前言
在前端开发中,代码质量和一致性是至关重要的。Stylelint 作为一个强大的 CSS 代码检查工具,能够帮助开发者发现代码中的问题,并保持代码风格的一致性。然而,内置的规则和插件有时无法完全满足特定项目的需求。在这种情况下,开发自定义插件可以提供更灵活、更专业的解决方案。本文将详细介绍如何编写一个 Stylelint 自定义插件,帮助你扩展 Stylelint 的功能,以满足特定的项目需求。
什么是 Stylelint 插件?
Stylelint 插件是一段 JavaScript 代码,它定义了新的规则,用于检查 CSS 样式表中的特定问题。你可以使用插件来创建自定义的代码风格规则,或者检测特定的代码模式。
创建插件
我们从最简单的例子开始,创建一个插件来检查 CSS 中是否存在特定的属性(例如 font-size)。首先,在项目根目录下创建一个文件夹来存放我们的插件代码:
mkdir lib
然后在 lib 文件夹中创建一个名为 index.js 的文件:
// lib/index.jsmodule.exports = {// 插件名称ruleName: 'custom/font-size-check',// 定义插件规则rule: (primaryOption, secondaryOption, context) => {return (root, result) => {// 定义规则名称和报告const validOptions = context.utils.validateOptions(result, 'custom/font-size-check', {actual: primaryOption,});if (!validOptions) {return;}// 遍历 CSS ASTroot.walkDecls(decl => {if (decl.prop === 'font-size' && decl.value === primaryOption) {context.utils.report({message: `Unexpected font-size value: ${decl.value}`,node: decl,result,ruleName: 'custom/font-size-check',});}});};},
};
在这个例子中,我们定义了一个名为 custom/font-size-check 的规则,检查 font-size 属性的值是否符合预期。
配置 Stylelint 使用插件
接下来,我们需要配置 Stylelint 使用我们自定义的插件。在项目根目录下创建一个 .stylelintrc.js 文件:
// .stylelintrc.jsmodule.exports = {plugins: ['./lib'],rules: {'custom/font-size-check': ['16px'],},
};
在这个配置文件中,我们告诉 Stylelint 使用 ./lib 目录下的插件,并启用了 custom/font-size-check 规则,期望 font-size 的值为 16px。
运行 Stylelint
最后,我们创建一个 CSS 文件来测试我们的插件:
/* test.css */body {font-size: 14px; /* 这行代码会触发自定义规则 */
}
我们可以运行 Stylelint 来检查这个文件:
npx stylelint test.css
如果一切正常,你应该能看到类似下面的输出:
test.css
1:3 ✖ Unexpected font-size value: 14px custom/font-size-check
进阶:复杂规则和配置
现在你已经学会了如何创建一个简单的 Stylelint 插件,接下来我们可以探索一些更复杂的例子。我们将创建一个插件来检查 CSS 中是否存在未使用的变量。这类规则对大型项目尤为有用,因为它们往往会有大量的样式和变量,容易出现未使用的代码。
创建复杂插件
首先,我们在 lib 文件夹中创建一个新的文件 unused-vars.js:
// lib/unused-vars.jsconst hasOwn = Object.prototype.hasOwnProperty;module.exports = {ruleName: 'custom/unused-vars-check',rule: (primaryOption, secondaryOption, context) => {return (root, result) => {const validOptions = context.utils.validateOptions(result, 'custom/unused-vars-check', {actual: primaryOption,});if (!validOptions) {return;}const variables = {};const usedVariables = new Set();// 收集所有定义的变量root.walkDecls(decl => {if (decl.prop.startsWith('--')) {variables[decl.prop] = decl;}});// 查找所有使用的变量root.walkDecls(decl => {if (decl.value.includes('var(')) {const match = decl.value.match(/var\((--[a-zA-Z0-9-]+)\)/);if (match && match[1]) {usedVariables.add(match[1]);}}});// 报告未使用的变量Object.keys(variables).forEach(varName => {if (!usedVariables.has(varName)) {context.utils.report({message: `Variable "${varName}" is defined but never used`,node: variables[varName],result,ruleName: 'custom/unused-vars-check',});}});};},
};
在这个复杂的插件中,我们首先收集所有定义的 CSS 变量,然后遍历所有声明以查找使用的变量,最后报告那些未使用的变量。
配置 Stylelint 使用复杂插件
将新插件添加到 lib 文件夹,并更新 .stylelintrc.js 文件:
// .stylelintrc.jsmodule.exports = {plugins: ['./lib'],rules: {'custom/font-size-check': ['16px'],'custom/unused-vars-check': true,},
};
测试复杂插件
我们创建一个新的 CSS 文件来测试这个新的插件:
/* test-vars.css */:root {--main-color: #ff0000; /* 未使用的变量 */--secondary-color: #00ff00;
}body {color: var(--secondary-color);
}
运行 Stylelint:
npx stylelint test-vars.css
如果一切正常,你应该能看到类似下面的输出:
test-vars.css2:3 ✖ Variable "--main-color" is defined but never used custom/unused-vars-check
处理插件的参数
有时候,你可能希望插件的行为可以通过配置进行定制。例如,在未使用变量的检查中,我们可能希望忽略某些特定的变量。我们可以通过 secondaryOptions 来实现这一点。
更新 unused-vars.js 文件,使其支持忽略特定变量的功能:
// lib/unused-vars.jsmodule.exports = {ruleName: 'custom/unused-vars-check',rule: (primaryOption, secondaryOption, context) => {return (root, result) => {const validOptions = context.utils.validateOptions(result, 'custom/unused-vars-check', {actual: primaryOption,possible: [true, false],}, {actual: secondaryOption,possible: {ignore: [_.isString],},optional: true,});if (!validOptions) {return;}const ignoreVariables = secondaryOption && secondaryOption.ignore ? new Set(secondaryOption.ignore) : new Set();const variables = {};const usedVariables = new Set();// 收集所有定义的变量root.walkDecls(decl => {if (decl.prop.startsWith('--')) {variables[decl.prop] = decl;}});// 查找所有使用的变量root.walkDecls(decl => {if (decl.value.includes('var(')) {const match = decl.value.match(/var\((--[a-zA-Z0-9-]+)\)/);if (match && match[1]) {usedVariables.add(match[1]);}}});// 报告未使用的变量Object.keys(variables).forEach(varName => {if (!usedVariables.has(varName) && !ignoreVariables.has(varName)) {context.utils.report({message: `Variable "${varName}" is defined but never used`,node: variables[varName],result,ruleName: 'custom/unused-vars-check',});}});};},
};
然后更新 .stylelintrc.js 文件:
// .stylelintrc.jsmodule.exports = {plugins: ['./lib'],rules: {'custom/font-size-check': ['16px'],'custom/unused-vars-check': [true, { ignore: ['--main-color'] }],},
};
重新运行检查,这次 --main-color 应该不会报告为未使用的变量。
总结
通过本文的学习,我们不仅掌握了创建简单 Stylelint 插件的基本方法,还探索了实现更复杂规则和配置的技巧。自定义 Stylelint 插件能够极大地提升代码的质量和一致性,尤其在处理复杂和大型项目时尤为重要。希望本文能够帮助你深入理解 Stylelint 插件的开发过程,并激发你根据实际需求创建更强大和实用的自定义规则。