Giter VIP home page Giter VIP logo

stylelint's Introduction

stylelint

NPM version Build Status Build status NPM Downloads Backers on Open Collective Sponsors on Open Collective

一个强大的,现代的代码检查工具,可以帮助您避免错误并在您的样式中强制执行约定。

特性

它很强大,因为它:

  • 有超过160条内置规则来捕捉错误,采取限制和执行风格约定
  • 懂得最新的CSS语法,包括自定义属性和4级选择器
  • 从 HTML、markdown、CSS-in-JS 的对象/模板字符串中提取内嵌样式
  • 解析类CSS语法,如SCSS、Sass、Less 和 SugarSS
  • 支持插件,这样您就可以创建自己的规则或使用社区编写的插件
  • 自动修复一些违规(实验性功能
  • 经10000多个单元测试充分测试
  • 支持可共享的配置,您可以扩展或创建自己的配置
  • 非倾向性的,所以您可以根据您的确切需要定制代码检查工具
  • 有一个不断发展的社区,被用于 Facebook, GitHubWordPress

示例输出

Example

入门

它很容易上手。

首先,决定如何使用 stylelint:

然后创建您的配置对象。您可以扩展共享配置或制作自己的配置。

扩展共享配置

这是最快捷的入门方式。我们建议您扩展:

建议(recommended)配置仅打开可能错误的规则。标准(standard)配置通过打开60个风格规则来扩展它。我们建议您扩展:

  • 建议(recommended)配置,如果您使用 prettier 之类的美化排版工具
  • 标准(standard)配置,如果您希望 stylelint 强制执行风格约定

您可能希望在配置中添加限制语言功能的规则,作为您的团队/项目特殊规则。

如果您使用语言扩展, 例如 @if@extends, 您可以使用像 stylelint-config-recommended-scss 这样的社区配置代替。

制作您自己的配置

或者,您可以了解规则,然后:

  • 从小处开始,只添加您要打开的规则
  • 复制,粘贴和调整示例配置中列出的所有规则及其主要选项

指南

您可以在我们的指南中找到有关自定义 stylelint 的详细信息:

需要帮助?

首先阅读我们的常问问题

如果没有您问题的答案,那么请将问题发布到 stackoverflow

如果出现以下情况,请创建新问题

  • 您觉得发现了一个 bug
  • 您有功能请求

如果您正在升级,请阅读我们的更改日志以了解最新版本中的更改。

协助我们

要协助我们,您可以:

我们在蓝图文档的指导下工作。

语义版本控制策略

我们有一个语义版本控制策略。任何次要更新都可能报告比以前版本更多的错误。因此,我们建议在 package.json 中使用波浪号 (~),例如 "stylelint": "~7.2.0" 以保证构建的结果。

许可证

MIT许可证.

贡献者

这个项目归功于所有这些人。贡献

支持者

谢谢所有支持者!成为支持者

赞助商

成为赞助商支持这个项目。您的商标将显示在此处,并带有指向您网站的链接。成为赞助商

stylelint's People

Contributors

jeddy3 avatar davidtheclark avatar greenkeeper[bot] avatar alexander-akait avatar ntwb avatar hudochenkov avatar gucong3000 avatar evilebottnawi avatar ota-meshi avatar m-allanson avatar moox avatar caydenberg avatar makotot avatar greenkeeperio-bot avatar onigoetz avatar sendilkumarn avatar kangax avatar dryoma avatar modosc avatar gaidarenko avatar bilie avatar borodean avatar ivanzusko avatar jwilsson avatar ybiquitous avatar manovotny avatar philippbaschke avatar xiaohanxu-nick avatar dan-gamble avatar jacobmischka avatar

Stargazers

 avatar Danika avatar 志江 avatar cph avatar TAKO avatar SandoGeek avatar  avatar  avatar

Watchers

James Cloos avatar  avatar heyli avatar Hapi Lin avatar Jay.M.Hu avatar  avatar  avatar

stylelint's Issues

翻译指南

需要翻译的文件:项目内所有 markdown 文档,即 **/*.md
主分支 cn,所有PR请指向此分支

欢迎补充

脚本堆放场

规则文档构建脚本:

"use strict";

const fs = require("fs");
const path = require("path");
const util = require("util");
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);

const rules = "docs/user-guide/rules.md";
let count = 0;
let cn = 0;

const translatorDate = {
  "keyword": "关键字",
  "scheme": "协议",
  "lowercase": "小写",
  "uppercase": "大写",
  "allowed": "允许的",
  "disallowed": "禁用的",
  "blacklist": "黑名单",
  "whitelist": "白名单",
  "custom": "自定义",
  "pattern": "模式",
  "complain": "指正",
  "number": "数量",
  "depth": "深度",
  "length": "长度",
  "unit": "单位",
  "specificity": "特异性",
  "decimal places": "小数位",
  "attribute": "属性",
  "classes": "类",
  "class": "类",
  "compound": "复合",
  "pseudo-element": "伪元素",
  "pseudo-classes": "伪类",
  "pseudo-class": "伪类",
  "type": "类型",
  "universal": "通用",
  "must always": "必须",
  "must never": "不能",
  "overrides": "覆盖",
  "vendor": "浏览器引擎",
  "prefixes": "前缀",
  "prefix": "前缀",
  "qualifying": "限定",
  "duplicated": "重复",
  "duplicate": "重复",
  "redundant": "冗余",
  "shorthand": "简写",
  "feature": "功能",
  "keyframe": "关键帧",
  "url": "URL",
  "hex": "16进制",
  "color": "颜色",
  "short or long notation": "缩写或扩写",
  "a single space": "一个空格",
  "whitespace": "空白符",
  "white": "空白符",
  "space": "空白符",
  "newline": "换行符",
  "adjacent": "相邻",
  "empty": "空",
  "line": "行",
  "a": "一个",
  "an": "一个",
  "bang": "叹号",
  "colon": "冒号",
  "comma": "逗号",
  "semicolon": "分号",
  "notation": "符号",
  "quoted": "使用引号",
  "quote": "引号",
  "combinator": "组合符",
  "operator": "运算符",
  "range": "范围",
  "after": "之后",
  "before": "之前",
  "inside": "内侧",
  "multi": "多",
  "single": "单",
  "multi-line": "多行",
  "single-line": "单行",
  "block": "块",
  "declaration": "声明",
  "named": "命名",
  "name": "名",
  "at-rule": "@规则",
  "rule": "规则",
  "media": "媒体",
  "query": "查询",
  "list": "列表",
  "parameter": "参数",
  "function": "函数",
  "selector": "选择器",
  "combinators": "组合选择器",
  "descendant": "后代",
  "nested": "嵌套",
  "longhand": "简写",
  "properties": "属性",
  "property": "属性",
  "value": "值",
  "comment": "注释",
  "marker": "标记",
  "opening": "开",
  "closing": "闭",
  "parentheses": "括号",
  "parenthes": "括号",
  "bracket": "中括号",
  "brace": "大括号",
  "these": "这些",
  "this": "这个",
  "unknown": "未知的",
  "invalid": "无效的",
  "string": "字符串",
  "leading": "前导",
  "trailing": "尾随",
  "zero": "零",
  "one": "一个",
  "two": "两个",
  "this": "这个",
  "is": "是",
  "pairs": "对",
  "and": "和",
  "or": "或",
  "the": "",
}

function translator(words) {
  if (translatorDate.hasOwnProperty(words)) {
    words = translatorDate[words];
  } else if (/ allowed$/.test(words)) {
    words = "允许的" + translator(RegExp.leftContext);
  } else if (/^this /i.test(words)) {
    words = "这个" + translator(RegExp.rightContext);
  } else if (/^These /i.test(words)) {
    words = "这些" + translator(RegExp.rightContext);
  } else if (/(^|\s+)(of|(?:with)?in|on|with|for)(\s+|$)/.test(words)) {
    const {
      leftContext,
      rightContext,
    } = RegExp;
    words = `${translator(rightContext || "")}${translator(leftContext || "")}`;
  } else if (/^[\w-]+s$/.test(words) && !/^(?:th)is\b/i.test(words)) {
    words = translator(words.slice(0, -1));
  } else if (/\s/.test(words)) {
    words = words.split(/\s+/).map(translator).join("");
  }
  return words;
}

readFile(rules, "utf8").then(async (md) => {
  md = md.replace(/^(-\s+\[.+?\]\((.+?)\)[::]\s*).*$/gm, (s, prefix, file) => {
    count++;
    file = path.resolve("docs/user-guide", file)
    let rule = fs.readFileSync(file, "utf8");

    let desc = rule.match(/(?:^|\n)# .*\n+([^\n]+(?:\n\S[^\n]+)*)/)[1]
      .replace(/\[(.+?)\]\(.+?\)/g, "$1")
      .replace(/\n+/g, " ");

    const fixedRule = rule.replace(
      /(\w`?) *(?=[\u4e00-\u9fa5])/g,
      "$1 "
    ).replace(
      /([\u4e00-\u9fa5]) *(`?\w)/g,
      "$1 $2"
    ).replace(
      /([,。、;])[  ]*/g,
      "$1"
    ).replace(
      /notice the (\W+?), with no space after the closing parenthesis/igm,
      "注意 $1,在闭括号后没有空格"
    ).replace(
      /`(.+?)` is inside `?(.+?)`?,? (and|so it) is evaluated separately/igm,
      "`$1` 在 `$2` 里面,所以它是单独计算的"
    ).replace(
      /^This rule resolves nested selectors before (.*?)\..*$/igm,
      (s, sth) => {
        return `此规则在计算${translator(sth.replace(/^\w+\s+the\s+/, ""))}之前先解析选择器嵌套。[选择器列表](https://www.w3.org/TR/selectors4/#selector-list)中的每个选择器都将单独计算。`;
      }
    ).replace(
      /each selector in a selector list is evaluated separately/igm,
      "选择器列表中的每个选择器都将单独计算"
    ).replace(
      /^The( content of the)? `:not\(\)` pseudo\-class is also evaluated separately\. The rule processes the argument as if it were an independent selector, and the result does not count toward the total for the entire selector\.?/igm,
      "`:not()` 伪类的内容也是单独计算的。此规则将其参数视为一个独立的选择器,结果不计入整个选择器的总数。"
    ).replace(
      /There \*(.*?)\* be (.*?) (after|before|inside) (.*?)\.?$/igm,
      (s, how, sth, pos, where) => {
        return `在${translator(where)}${translator(pos)}*${translator(how)}*有${translator(sth)}。`
          .replace(/(\*不能\*有)(?:一个|的)/, "$1");
      }
    ).replace(
      /There \*(.*?)\* be (.*?)\.?$/igm,
      (s, how, sth) => {
        return `这里*${translator(how)}*有${translator(sth)}。`
          .replace(/(\*不能\*有)(?:一个|的)/, "$1");
      }
    ).replace(
      /(.*?) \*(.*?)\* be (.*?)\.?$/igm,
      (s, sth, how, what) => {
        return `${translator(sth.toLowerCase())}*${translator(how)}*${translator(what)}。`
          .replace(/(\*不能\*有)(?:一个|的)/, "$1");
      }
    ).replace(
      /((?:^|\/)\s*\*+\s*)(.*?) (after|before|inside) (.*?)(?=\s+\*\/)/igm,
      (s, prefix, sth, pos, where) => {
        return prefix + `${translator(where)}${translator(pos)}${translator(sth.toLowerCase())}`
        .trim();
      }
    ).replace(
      /^Limit the number of (.*) (?:with)?in (.*?)\.?$/igm,
      (s, sth, where) => {
        return `限制${translator(where)}${translator(sth)}的数量`;
      }
    ).replace(
      /^Require or disallow (.+?)\.?$/igm,
      (s, sth) => {
        return `要求或禁止${translator(sth)}。`;
      }
    ).replace(
      /^Disallow (.+?)\.?$/igm,
      (s, sth) => {
        return `禁止${translator(sth)}。`;
      }
    ).replace(
      /^Specify(?: an?)? (.+?) of (.+?) within (.+?)\.?$/igm,
      (s, how, sth, where) => {
        return `指定${translator(where)}${translator(sth)}${translator(how)}。`;
      }
    ).replace(
      /^Specify(?: an?)? (.+?)\.?$/igm,
      (s, sth) => {
        return `指定${translator(sth)}。`;
      }
    ).replace(
      /`int`: Maximum .* empty lines\b.*$/igm,
      "`int`:允许的最大相邻空行数量。"
    ).replace(
      /`int`: Maximum (.+?) allowed.*$/igm,
      (s, sth) => {
        return "`int`:允许的最大" + translator(sth) + "数量。";
      }
    ).replace(
      /(\*[↑←\s\n]+\*\s+)(Th(?:is|ese) .+?)(\s+\*)/ig,
      (s, before, sth, after) => {
        if (before.match(//g).length < 2) {
          sth = sth.replace(/^These\b/, "this");
        }
        sth = sth.replace(/^This\b/, "this");
        return before + translator(sth).replace(//, "").replace(/^这些(.+?[和或])这些/, "这些$1") + after;
      }
    ).replace(
      /^This rule considers (.+?) defined in the CSS Specifications, up to and including Editor's Drafts, to be known\.$/igm,
      (s, sth) => {
        return `此规则考虑了 CSS 规范中定义的${translator(sth).replace(//, "")},包括已知的编辑草案。`
      }
    ).replace(
      /^This rule ignores variables \(`\$sass`, `@less`, `\-\-custom\-property`\)\.?$/igm,
      "此规则忽略变量(`$sass`、`@less`、`--custom-property`)。"
    ).replace(
      /^This rule ignores `\$sass`, `@less`, and `var\(\-\-custom\-property\)` variable syntaxes\.$/igm,
      "此规则忽略 `$sass`、`@less` 和 `var(--custom-property)` 变量语法"
    ).replace(
      /^This rule ignores semicolons that are preceded by Less mixins.*$/igm,
      "此规则忽略 Less mixins 的分号。"
    ).replace(
      /^(-\s*)semicolons that are preceded by Less mixins.*$/igm,
      "$1Less mixins 的分号"
    ).replace(
      /^(-\s*)the last semicolon of declaration blocks$/igm,
      "$1声明块的最后一个分号"
    ).replace(
      /^Given:$/igm,
      "给定:"
    ).replace(
      /^This rule ignores:$/igm,
      "此规则忽略:"
    ).replace(
      /^## Options$/igm,
      "## 选项"
    ).replace(
      /^## Optional secondary options$/igm,
      "## 可选的辅助选项"
    ).replace(
      /^The following .* violation.*$/igm,
      (s) => {
        return `以下模式${/\bnot\b/.test(s) ? "*不*" : ""}被视为违规:`;
      }
    ).replace(
      /这些([一二三四五六七八九十两俩])/g,
      "这$1"
    ).replace(
      /(组合选择器)选择器/g,
      "$1"
    ).replace(
      /小数位的数量/g,
      "小数位数"
    ).replace(
      /小写或大写/g,
      "大小写"
    ).replace(
      /字体系列/g,
      "字体族"
    ).replace(
      /这个是/g,
      "这是"
    ).replace(
      //g,
      "您"
    );

    if (rule !== fixedRule) {
      rule = fixedRule
      writeFile(file, rule);
      console.log("已修正翻译", file);
    }

    if (!/[\u4e00-\u9fa5]/.test(desc)) {
      return s;
    }

    if (!/^(禁止|限制|要求|指定)/.test(desc)) {
      console.error("错误的单行描述前缀", desc);
      process.exitCode = 1;
    }

    cn++;

    if(/\(Autofixable\)|(可自动修复)/i.test(s)) {
      desc = desc.replace(/([.。]*)$/, "(可自动修复)$1");
    }

    if (
      rule.replace(/\n```([\s\S]+?)```(?=\n)/g, (s)=> {
        s = s.match(/\/\*[\s\S]*?\*\//g);
        if (s) {
          s = s.map(s => s.replace(/^\/[*\s\n]*|[*\s\n]*\/$/g, "")).filter(Boolean);
        }
        return s && s.length ? "\n" + s.join("\n") : "";
      }).split(/[\r\n]+/g).some(line => {
        if (!line || /^#\s+/.test(line) || /^[#-]*\s*\W*[`"]/.test(line) || /[\u4e00-\u9fa5↑←]/.test(line) || /\d|TODO:|foo|words|blergh|badword|stylelint|\w+\s*:\s*\w+/.test(line)) {
          return;
        }
        console.error(line);
        return true;
      })
    ) {
      console.error("不完整的翻译", file);
      process.exitCode = 1;
    }

    return prefix + desc;
  });
  console.log(`规则翻译进度:${cn}/${count}`);
  return writeFile(rules, md);
});

功能:

  • 提取规则文档中的单行描述写入docs/user-guide/rules.md
  • 粗略处理规则文档中的空格
  • 错略的翻译功能
  • 检查规则的单行描述是否使用了正确前缀
  • 查找规则文档中未翻译的行
  • 统计翻译进度

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.