npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ss-style-plugin

v1.0.1-alpha.0

Published

style-lint plugin to change variable

Downloads

1

Readme

我们关心代码质量和用户体验。出于这个原因,我们使用各种工具来使开发更容易、更准确。其中,我们使用Stylelint整理我们的 SCSS 和 CSS 文件。stylelint 的强大功能之一是能够添加您自己的规则,这将满足您的项目需求。虽然 stylelint 提供了用于添加自定义规则的 API,但第一次这样做时可能会有些混乱。

下面我们将完成向您的项目添加新的自定义 stylelint 规则的步骤:

  1. 声明和创建插件
  2. 向插件添加规则
  3. 实现 linting 功能以及自动修复支持
  4. 将插件与项目的 Stylelint 配置集成

背景故事

在我们应用程序的代码库中,我们将常用的颜色存储在变量中。这使得颜色主题更改非常快速和容易。在开发可视化组件时,我们使用 Invision 的模型作为参考。Invision 不知道我们使用的颜色变量。因此,从 Invision 复制样式涉及搜索颜色的变量(例如替换#ff6363$color-red)。

这似乎很容易做到:使用“查找任何地方”并寻找这种颜色。然而,这个过程变得繁琐——尤其是当我们包含多种颜色时。此外,在某些遗留代码的情况下,$color-red未使用变量。因此,我们可以找到#ff6363分散在代码库中的相同颜色。在这种情况下,开发人员必须在所有搜索结果中找到正确的声明。

为了解决这个不便,我们编写了自定义的stylelint规则。它建议正确的变量名称,甚至自动修复错误。多么方便!

企业微信截图_20210622141958.png

我们的自定义规则在起作用

在以下段落中,我们将描述添加您自己的自定义规则的步骤。作为一个简短的例子,我们将编写一个规则,讲属性值转换成变量。

编写插件

让我们从创建一个包含规则本身的文件开始——在我们的例子中,它是declaration-property-value-to-variable.js.

const stylelint = require('stylelint');
const { ruleMessages, validateOptions } = stylelint.utils;
 
const ruleName = 'ss-style-plugin/declaration-property-value-to-variable';
const messages = ruleMessages(ruleName, {
   //… linting messages can be specified here
});

接下来,您需要定义规则。这样,当规则出现在配置中时,stylelint 将知道要运行什么: 您的文件将需要导出三项内容:

  1. 规则名称
  2. 显示的消息
  3. 规则本身
module.exports.ruleName = ruleName;
module.exports.messages = messages;
module.exports = stylelint.createPlugin(ruleName, function ruleFunction(primaryOption, secondaryOptionObject, context) {
   return function lint(postcssRoot, postcssResult) {
   // ... 
}});

如果它对你来说仍然没有多大意义,那没关系!在接下来的部分中,我们将解释每个部分。

创建插件

我们stylelint.createPlugin用来创建和注册一个 stylelint 规则。它接收两个参数:规则名称和 ruleFunction 本身。如果在lint过程中使用了这个规则,则 stylelint 调用 ruleFunction。函数接收配置的选项以及上下文对象。Lint 函数(ruleFunction 返回的函数)将在正在被连接的每个文件上运行。这是规则的实际核心,它将报告 linting 过程的输出。让我们深入研究一下规则函数和 lint

向插件添加规则

如前所述,stylelint 为每个单个 linting “进程”调用 ruleFunction。Stylelint 的规则支持选项。这些可用于从 stylelint 的配置中自定义规则的行为。例如使用您的规则时,ruleFunction将使用匹配的选项(主要和次要)调用。 此外,stylelint 还提供了一个方便的 validateOptions 函数。如果用户传递的选项无效,则使用它自动报告错误。Stylelint 将把错误添加到链接结果(在这里称为 postcssResult)。文档并不是很广泛,但是源代码有一些很好的注释。ruleFunction 的第三个参数是上下文对象。它主要用于支持自动修复(稍后将详细介绍)

以下代码段应该使事情更清楚:

module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) {
   return function lint(postcssRoot, postcssResult) {
       const validOptions = validateOptions(
           postcssResult,
           ruleName,
           {
               //Options schema goes here
           }
       );
 
       if (!validOptions) { //If the options are invalid, don't lint
           return;
       }
       const shouldDoExtraCoolStuff = secondaryOptionObject.doStuff; //Parse the options to customize behaviour
       const isAutoFixing = Boolean(context.fix); //context.fix will be "true" if auto-fix mode is active
       //...
    }
}

我们已经完成了规则的设置。现在,让我们编写lint保存规则核心逻辑的函数。

执行 Linting

创建规则后,stylelint 将在每个 linted 文件上运行 PostCSS。接下来,在 linting 阶段,stylelint 将调用自定义规则的lint函数。该lint功能是您规则的核心。它接收两个参数:

通常,该lint函数将:

  1. “遍历” AST(解析的 CSS 节点)。使用其中一种root.walk方法完成此任务。
  2. 检测无效节点。
  3. 报告这些节点(或修复它们,取决于“修复”标志)。

让我们来看看我们规则的完整例子。建议颜色值替换成正确的变量名称,并且支持自动引入变量文件



const stylelint = require('stylelint');

const { report, ruleMessages, validateOptions } = stylelint.utils;
const ruleName = 'ss-style-plugin/declaration-property-value-to-variable';
const messages = ruleMessages(ruleName, {
    expected: (unfixed, fixed) => `Expected "${unfixed}" to be "${fixed}"`,
});


function insertNode({postcssRoot, keyword, file}) {
    const hasProperty = postcssRoot.nodes.find(
        ({ type, name, params }) => type === "atrule" && name === "import" && params.includes(keyword)
    );
    // 有node的时候自动加上import
    if (!hasProperty && postcssRoot.first) {
        postcssRoot.insertBefore(postcssRoot.first, file)
    }
}

module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) {
    return function lint(postcssRoot, postcssResult) {
        const validOptions = validateOptions(
            postcssResult,
            ruleName,
            {
                actual: primaryOption, // 验证主选项
                possible: (option) => {
                    if (typeof option !== 'object') {
                        return false
                    }
                    const primaryOptionKeys = Object.keys(option)
                    return primaryOptionKeys.length && primaryOptionKeys.every(k => {
                        return typeof option[k] === 'object'
                    });
                }, // 默认通过认证
            },
            {
                actual: secondaryOptionObject, // 验证第二选项
                possible: (option) => {
                    if (typeof option !== 'object') {
                        return false
                    }
                    return option.import;
                }, 
            }
        );

        if (!validOptions) { //如果选项无效,不是校验
            return;
        }
        // --fix选项
        const isAutoFixing = Boolean(context.fix) && !secondaryOptionObject.disableFix;
        let insertNodeFlag = false
        const keys = Object.keys(primaryOption)

        // 不去格式化非sass的文件
        if (postcssRoot.source.lang !== 'scss') {
            return
        }


        postcssRoot.walkDecls(decl => { 
            const hasProps = keys.includes(decl.prop) && Object.keys(primaryOption[decl.prop]).includes(decl.value);
            if (!hasProps) {
                return; //找不到替换的node - continue
            }
            const targetVal = primaryOption[decl.prop][decl.value]
            if (isAutoFixing) { // 修复模式下
                const newValue = decl.value.replace(decl.value, targetVal);
                insertNodeFlag = true
                if (decl.raws.value) {
                    decl.raws.value.raw = newValue;
                } else {
                    decl.value = newValue;
                }
            } else { // 不修复去报告
                report({
                    ruleName,
                    result: postcssResult,
                    message: messages.expected(`${decl.prop}: ${decl.value}`, `${decl.prop}: ${targetVal}`), // 生成报告的消息
                    node: decl, // 指定报告的节点
                    word: decl.value, // 哪个词导致了错误?这将正确定位错误
                });
            }
        });

        // 自动修复的时候插入css
        if (insertNodeFlag && secondaryOptionObject.import && secondaryOptionObject.import.length) {
            secondaryOptionObject.import.forEach(({keyword, file}) => insertNode({postcssRoot, keyword, file}))
        }
    };
});

module.exports.ruleName = ruleName;
module.exports.messages = messages;

自定义规则集成

编辑项目的stylelint 配置(例如.stylelintrc.json)。在“plugins”配置值下,添加插件文件的路径。剩下的就是激活您的规则,因此 stylelint 将实际运行它。通过将您的规则添加到“规则”配置值来实现。 一个最小的.stylelintrc文件看起来像这样:

{
  "plugins": [
      "./plugins/declaration-property-value-to-variable.js"
  ],
  "rules": {
    "ss-style-plugin/declaration-property-value-to-variable": [{ 
      "color": {
        "green": "$abc",
        "#fff": "$white"
      }
    }, {
      "import": [
        {
          "keyword": "colors.scss",
          "file": "@import 'variable/_colors.scss';"
        }
      ]
    }]
  }
}

package.json中加入scripts

{
  "scripts": {
    "lint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" "
  }
}

企业微信截图_20210622150347.png 运行npm run lint之后,自动导入变量文件,修复变量值 企业微信截图_20210622150050.png Stylelint 是一个非常有用的 linting 工具。在协作处理常见的 SCSS 和 CSS 文件时,它具有许多优势。它带有各种各样的现有规则。然而,它们不能总是满足您的所有需求。在某些时候,您将需要一个尚不存在的 linting 规则。它可能是项目的结构、您团队的约定或其他任何东西。在这些情况下,自定义规则将非常有用。 希望在阅读这篇文章后,您将能够快速创建和集成自己的 stylelint 规则。这样,您将在尊重项目要求的同时保持更高水平的代码质量。 ​

如果你觉得文章不错,点个赞给个start,github地址

参考文档