added all files to project

This commit is contained in:
2022-03-10 10:36:59 +01:00
parent 09dd957b33
commit 46a936d7de
25351 changed files with 3883356 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
# Writing formatters
A formatter is a function with the following signature:
```js
/**
* @type {import('stylelint').Formatter}
*/
function formatter(results, returnValue) {
return "a string of formatted results";
}
```
Where the first argument (`results`) is an array of stylelint result objects (type `Array<StylelintResult>`) in the form:
```js
// A stylelint result object
{
"source": "path/to/file.css", // The filepath or PostCSS identifier like <input css 1>
"errored": true, // This is `true` if at least one rule with an "error"-level severity triggered a warning
"warnings": [
// Array of rule violation warning objects, each like the following ...
{
"line": 3,
"column": 12,
"rule": "block-no-empty",
"severity": "error",
"text": "You should not have an empty block (block-no-empty)"
}
],
"deprecations": [
// Array of deprecation warning objects, each like the following ...
{
"text": "Feature X has been deprecated and will be removed in the next major version.",
"reference": "https://stylelint.io/docs/feature-x.md"
}
],
"invalidOptionWarnings": [
// Array of invalid option warning objects, each like the following ...
{
"text": "Invalid option X for rule Y"
}
],
"ignored": false // This is `true` if the file's path matches a provided ignore pattern
}
```
And the second argument (`returnValue`) is an object (type `StylelintStandaloneReturnValue`) with one or more of the following keys:
```js
{
"errored": false, // `true` if there were any warnings with "error" severity
"maxWarningsExceeded": {
// Present if stylelint was configured with a `maxWarnings` count
"maxWarnings": 10,
"foundWarnings": 15
}
}
```
## Passing arguments
You can use environmental variables in your formatter. For example, pass `SKIP_WARNINGS`:
```console
SKIP_WARNINGS=true stylelint "*.css" --custom-formatter ./my-formatter.js
```
Alternatively, you can create a separate formatting program and pipe the output from the built-in JSON formatter into it:
```console
stylelint -f json "*.css" | my-program-that-reads-JSON --option
```
## `stylelint.formatters`
stylelint's internal formatters are exposed publicly in `stylelint.formatters`.
+324
View File
@@ -0,0 +1,324 @@
# Writing plugins
Plugins are rules and sets of rules built by the community.
We recommend your plugin adheres to [stylelint's conventions](rules.md) for:
- names
- options
- messages
- tests
- docs
## The anatomy of a plugin
```js
// Abbreviated example
const stylelint = require("stylelint");
const ruleName = "plugin/foo-bar";
const messages = stylelint.utils.ruleMessages(ruleName, {
expected: "Expected ..."
});
module.exports = stylelint.createPlugin(
ruleName,
function (primaryOption, secondaryOptionObject) {
return function (postcssRoot, postcssResult) {
const validOptions = stylelint.utils.validateOptions(
postcssResult,
ruleName,
{
/* .. */
}
);
if (!validOptions) {
return;
}
// ... some logic ...
stylelint.utils.report({
/* .. */
});
};
}
);
module.exports.ruleName = ruleName;
module.exports.messages = messages;
```
Your plugin's rule name must be namespaced, e.g. `your-namespace/your-rule-name`, to ensure it never clashes with the built-in rules. If your plugin provides only a single rule or you can't think of a good namespace, you can use `plugin/my-rule`. _You should document your plugin's rule name (and namespace) because users need to use them in their config._
Use `stylelint.createPlugin(ruleName, ruleFunction)` to ensure that your plugin is set up properly alongside other rules.
For your plugin rule to work with the [standard configuration format](../user-guide/configure.md#rules), `ruleFunction` should accept 2 arguments:
- the primary option
- optionally, a secondary options object
If your plugin rule supports [autofixing](rules.md#add-autofix), then `ruleFunction` should also accept a third argument: `context`. You should try to support the `disableFix` option in your secondary options object. Within the rule, don't perform autofixing if the user has passed a `disableFix` option for your rule.
`ruleFunction` should return a function that is essentially a little [PostCSS plugin](https://github.com/postcss/postcss/blob/master/docs/writing-a-plugin.md). It takes 2 arguments:
- the PostCSS Root (the parsed AST)
- the PostCSS LazyResult
You'll have to [learn about the PostCSS API](https://api.postcss.org/).
### Asynchronous rules
You can return a `Promise` instance from your plugin function to create an asynchronous rule.
```js
// Abbreviated asynchronous example
const stylelint = require("stylelint");
const ruleName = "plugin/foo-bar-async";
const messages = stylelint.utils.ruleMessages(ruleName, {
expected: "Expected ..."
});
module.exports = stylelint.createPlugin(
ruleName,
function (primaryOption, secondaryOptionObject) {
return function (postcssRoot, postcssResult) {
const validOptions = stylelint.utils.validateOptions(
postcssResult,
ruleName,
{
/* .. */
}
);
if (!validOptions) {
return;
}
return new Promise(function (resolve) {
// some async operation
setTimeout(function () {
// ... some logic ...
stylelint.utils.report({
/* .. */
});
resolve();
}, 1);
});
};
}
);
module.exports.ruleName = ruleName;
module.exports.messages = messages;
```
## Testing
You should use [`jest-preset-stylelint`](https://github.com/stylelint/jest-preset-stylelint) to test your plugin. The preset exposes a global `testRule` function that you can use to efficiently test your plugin using a schema.
For example:
```js
// index.test.js
const { messages, ruleName } = require(".");
testRule({
plugins: ["./index.js"],
ruleName,
config: true,
fix: true,
accept: [
{
code: ".class {}"
},
{
code: ".my-class {}"
}
],
reject: [
{
code: ".myClass {}",
fixed: ".my-class {}",
message: messages.expected(),
line: 1,
column: 1
}
]
});
```
However, if your plugin involves more than just checking syntax you can use stylelint directly.
For example:
```js
// index.test.js
const { lint } = require("stylelint");
const config = {
plugins: ["./index.js"],
rules: {
"plugin/at-import-no-unresolveable": [true]
}
};
it("warns for unresolveable import", async () => {
const {
results: [{ warnings, parseErrors }]
} = await lint({
files: "fixtures/contains-unresolveable-import.css",
config
});
expect(parseErrors).toHaveLength(0);
expect(warnings).toHaveLength(1);
const [{ line, column, text }] = warnings;
expect(text).toBe(
"Unexpected unresolveable import (plugin/at-import-no-unresolveable)"
);
expect(line).toBe(1);
expect(column).toBe(1);
});
it("doesn't warn for fileless sources", async () => {
const {
results: [{ warnings, parseErrors }]
} = await lint({
code: "@import url(unknown.css)",
config
});
expect(parseErrors).toHaveLength(0);
expect(warnings).toHaveLength(0);
});
```
Alternatively, if you don't want to use Jest you'll find more tools in [awesome stylelint](https://github.com/stylelint/awesome-stylelint#tools).
## `stylelint.utils`
stylelint exposes some useful utilities.
### `stylelint.utils.report`
Adds violations from your plugin to the list of violations that stylelint will report to the user.
Use `stylelint.utils.report` to ensure your plugin respects disabled ranges and other possible future features of stylelint. _Do not use PostCSS's `node.warn()` method directly._
### `stylelint.utils.ruleMessages`
Tailors your messages to the format of standard stylelint rules.
### `stylelint.utils.validateOptions`
Validates the options for your rule.
### `stylelint.utils.checkAgainstRule`
Checks CSS against a standard stylelint rule _within your own rule_. This function provides power and flexibility for plugins authors who wish to modify, constrain, or extend the functionality of existing stylelint rules.
It accepts an options object and a callback that is invoked with warnings from the specified rule. The options are:
- `ruleName`: the name of the rule you are invoking
- `ruleSettings`: settings for the rule you are invoking
- `root`: the root node to run this rule against
Use the warning to create a _new_ warning _from your plugin rule_ that you report with `stylelint.utils.report`.
For example, imagine you want to create a plugin that runs `at-rule-no-unknown` with a built-in list of exceptions for at-rules provided by your preprocessor-of-choice:
```js
const allowableAtRules = [
/* .. */
];
function myPluginRule(primaryOption, secondaryOptionObject) {
return function (postcssRoot, postcssResult) {
const defaultedOptions = Object.assign({}, secondaryOptionObject, {
ignoreAtRules: allowableAtRules.concat(options.ignoreAtRules || [])
});
stylelint.utils.checkAgainstRule(
{
ruleName: "at-rule-no-unknown",
ruleSettings: [primaryOption, defaultedOptions],
root: postcssRoot
},
(warning) => {
stylelint.utils.report({
message: myMessage,
ruleName: myRuleName,
result: postcssResult,
node: warning.node,
line: warning.line,
column: warning.column
});
}
);
};
}
```
## `stylelint.rules`
All of the rule functions are available at `stylelint.rules`. This allows you to build on top of existing rules for your particular needs.
A typical use-case is to build in more complex conditionals that the rule's options allow for. For example, maybe your codebase uses special comment directives to customize rule options for specific stylesheets. You could build a plugin that checks those directives and then runs the appropriate rules with the right options (or doesn't run them at all).
All rules share a common signature. They are a function that accepts two arguments: a primary option and a secondary options object. And that functions returns a function that has the signature of a PostCSS plugin, expecting a PostCSS root and result as its arguments.
Here's an example of a plugin that runs `color-hex-case` only if there is a special directive `@@check-color-hex-case` somewhere in the stylesheet:
```js
module.exports = stylelint.createPlugin(ruleName, function (expectation) {
const runColorHexCase = stylelint.rules["color-hex-case"](expectation);
return (root, result) => {
if (root.toString().indexOf("@@check-color-hex-case") === -1) {
return;
}
runColorHexCase(root, result);
};
});
```
## Allow primary option arrays
If your plugin can accept an array as its primary option, you must designate this by setting the property `primaryOptionArray = true` on your rule function. For more information, check out the ["Working on rules"](rules.md) doc.
## External helper modules
In addition to the standard parsers mentioned in the ["Working on rules"](rules.md) doc, there are other external modules used within stylelint that we recommend using. These include:
- [normalize-selector](https://github.com/getify/normalize-selector): normalize CSS selectors.
- [postcss-resolve-nested-selector](https://github.com/davidtheclark/postcss-resolve-nested-selector): given a (nested) selector in a PostCSS AST, return an array of resolved selectors.
Have a look through [stylelint's internal utils](https://github.com/stylelint/stylelint/tree/master/lib/utils) and if you come across one that you need in your plugin, then please consider helping us extract it out into an external module.
## Peer dependencies
You should express, within the `peerDependencies` key (and **not** within the `dependencies` key) of your plugin's `package.json`, what version(s) of stylelint your plugin can be used with. This is to ensure that different versions of stylelint are not unexpectedly installed.
For example, to express that your plugin can be used with stylelint versions 7 and 8:
```json
{
"peerDependencies": {
"stylelint": "^7.0.0 || ^8.0.0"
}
}
```
## Plugin packs
To make a single module provide multiple rules, export an array of plugin objects (rather than a single object).
## Sharing plugins and plugin packs
Use the `stylelint-plugin` keyword within your `package.json`.
+28
View File
@@ -0,0 +1,28 @@
# Writing processors
Processors are functions that hook into stylelint's pipeline, modifying code on its way into stylelint and modifying results on their way out.
**Their use is discouraged in favor of [PostCSS syntaxes](../about/syntaxes.md).**
Processor modules are functions that accept an options object and return an object with the following the functions, which hook into the processing of each file:
- **code**: A function that accepts two arguments, the file's code and the file's path, and returns a string for stylelint to lint.
- **result**: A function that accepts two arguments, the file's stylelint result object and the file's path, and either mutates the result object (returning nothing) or returns a new one.
```js
// my-processor.js
module.exports = function (options) {
return {
code: function (input, filepath) {
// ...
return transformedCode;
},
result: function (stylelintResult, filepath) {
// ...
return transformedResult;
}
};
};
```
_Processor options must be JSON-friendly_ because users will need to include them in `.stylelintrc` files.
+244
View File
@@ -0,0 +1,244 @@
# Working on rules
Please help us create, enhance, and debug our rules!
## Add a rule
You should:
1. Get yourself ready to [contribute code](../../CONTRIBUTING.md#code-contributions).
2. Familiarize yourself with the [conventions and patterns](../user-guide/rules/about.md) for rules.
### Write the rule
When writing the rule, you should:
- make the rule strict by default
- add secondary `ignore` options to make the rule more permissive
- not include code specific to language extensions, e.g. SCSS
You should make use of the:
- PostCSS API
- construct-specific parsers
- utility functions
#### PostCSS API
Use the [PostCSS API](https://api.postcss.org/) to navigate and analyze the CSS syntax tree. We recommend using the `walk` iterators (e.g. `walkDecls`), rather than using `forEach` to loop through the nodes.
When using array methods on nodes, e.g. `find`, `some`, `filter` etc, you should explicitly check the `type` property of the node before attempting to access other properties. For example:
```js
const hasProperty = nodes.find(
({ type, prop }) => type === "decl" && prop === propertyName
);
```
Use `node.raws` instead of `node.raw()` when accessing raw strings from the [PostCSS AST](https://astexplorer.net/#/gist/ef718daf3e03f1d200b03dc5a550ec60/c8cbe9c6809a85894cebf3fb66de46215c377f1a).
#### Construct-specific parsers
Depending on the rule, we also recommend using:
- [postcss-value-parser](https://github.com/TrySound/postcss-value-parser)
- [postcss-selector-parser](https://github.com/postcss/postcss-selector-parser)
There are significant benefits to using these parsers instead of regular expressions or `indexOf` searches (even if they aren't always the most performant method).
#### Utility functions
stylelint has [utility functions](https://github.com/stylelint/stylelint/tree/master/lib/utils) that are used in existing rules and might prove useful to you, as well. Please look through those so that you know what's available. (And if you have a new function that you think might prove generally helpful, let's add it to the list!).
Use the:
- `validateOptions()` utility to warn users about invalid options
- `isStandardSyntax*` utilities to ignore non-standard syntax
### Add options
Only add an option to a rule if it addresses a _requested_ use case to avoid polluting the tool with unused features.
If your rule can accept an array as its primary option, you must designate this by setting the property `primaryOptionArray = true` on your rule function. For example:
```js
function rule(primary, secondary) {
return (root, result) => {
/* .. */
};
}
rule.primaryOptionArray = true;
module.exports = rule;
```
There is one caveat here: If your rule accepts a primary option array, it cannot also accept a primary option object. Whenever possible, if you want your rule to accept a primary option array, you should make an array the only possibility, instead of allowing for various data structures.
### Add autofix
Depending on the rule, it might be possible to automatically fix the rule's violations by mutating the PostCSS AST (Abstract Syntax Tree) using the [PostCSS API](http://api.postcss.org/).
Add `context` variable to rule parameters:
```js
function rule(primary, secondary, context) {
return (root, result) => {
/* .. */
};
}
```
`context` is an object which could have two properties:
- `fix`(boolean): If `true`, your rule can apply autofixes.
- `newline`(string): Line-ending used in current linted file.
If `context.fix` is `true`, then change `root` using PostCSS API and return early before `report()` is called.
```js
if (context.fix) {
// Apply fixes using PostCSS API
return; // Return and don't report a problem
}
report(/* .. */);
```
### Write tests
Each rule must have tests that cover all patterns that:
- are considered violations
- should _not_ be considered violations
Write as many as you can stand to.
You should:
- test errors in multiple positions, not the same place every time
- use realistic (if simple) CSS, and avoid the use of ellipses
- use standard CSS syntax by default, and only swap parsers when testing a specific piece of non-standard syntax
#### Commonly overlooked edge-cases
You should ask yourself how does your rule handle:
- variables (`$sass`, `@less` or `var(--custom-property)`)?
- CSS strings (e.g. `content: "anything goes";`)?
- CSS comments (e.g. `/* anything goes */`)?
- `url()` functions, including data URIs (e.g. `url(anything/goes.jpg)`)?
- vendor prefixes (e.g. `@-webkit-keyframes name {}`)?
- case sensitivity (e.g. `@KEYFRAMES name {}`)?
- a pseudo-class _combined_ with a pseudo-element (e.g. `a:hover::before`)?
- nesting (e.g. do you resolve `& a {}`, or check it as is?)?
- whitespace and punctuation (e.g. comparing `rgb(0,0,0)` with `rgb(0, 0, 0)`)?
### Write the README
You should:
- only use standard CSS syntax in example code and options
- use `<!-- prettier-ignore -->` before `css` code fences
- use "this rule" to refer to the rule, e.g. "This rule ignores ..."
- align the arrows within the prototypical code example with the beginning of the highlighted construct
- align the text within the prototypical code example as far to the left as possible
For example:
<!-- prettier-ignore -->
```css
@media screen and (min-width: 768px) {}
/** ↑ ↑
* These names and values */
```
When writing examples, you should use:
- complete CSS patterns i.e. avoid ellipses (`...`)
- the minimum amount of code possible to communicate the pattern, e.g. if the rule targets selectors then use an empty rule, e.g. `{}`
- `{}`, rather than `{ }` for empty rules
- the `a` type selector by default
- the `@media` at-rules by default
- the `color` property by default
- _foo_, _bar_ and _baz_ for names, e.g. `.foo`, `#bar`, `--baz`
Look at the READMEs of other rules to glean more conventional patterns.
### Wire up the rule
The final step is to add references to the new rule in the following places:
- [The rules `index.js` file](../../lib/rules/index.js)
- [The list of rules](../user-guide/rules/list.md)
## Add an option to a rule
You should:
1. Get ready to [contribute code](../../CONTRIBUTING.md#code-contributions).
2. Change the rule's validation to allow for the new option.
3. Add new unit tests to test the option.
4. Add (as little as possible) logic to the rule to make the tests pass.
5. Add documentation about the new option.
## Fix a bug in a rule
You should:
1. Get ready to [contribute code](../../CONTRIBUTING.md#code-contributions).
2. Write failing unit tests that exemplify the bug.
3. Fiddle with the rule until those new tests pass.
## Deprecate a rule
Deprecating rules doesn't happen very often. When you do, you must:
1. Point the `stylelintReference` link to the specific version of the rule README on the GitHub website, so that it is always accessible.
2. Add the appropriate meta data to mark the rule as deprecated.
## Improve the performance of a rule
You can run a benchmarks on any given rule with any valid config using:
```shell
npm run benchmark-rule -- ruleName ruleOptions [ruleContext]
```
If the `ruleOptions` argument is anything other than a string or a boolean, it must be valid JSON wrapped in quotation marks.
```shell
npm run benchmark-rule -- selector-combinator-space-after never
```
```shell
npm run benchmark-rule -- selector-combinator-space-after always
```
```shell
npm run benchmark-rule -- block-opening-brace-space-before "[\"always\", {\"ignoreAtRules\": [\"else\"]}]"
```
If the `ruleContext` argument is specified, the sames procedure would apply:
```shell
npm run benchmark-rule -- block-opening-brace-space-before "[\"always\", {\"ignoreAtRules\": [\"else\"]}]" "{\"fix\": \"true\"}"
```
The script loads Bootstrap's CSS (from its CDN) and runs it through the configured rule.
It will end up printing some simple stats like this:
```shell
Warnings: 1441
Mean: 74.17598357142856 ms
Deviation: 16.63969674310928 ms
```
When writing new rules or refactoring existing rules, use these measurements to determine the efficiency of your code.
A stylelint rule can repeat its core logic many, many times (e.g. checking every value node of every declaration in a vast CSS codebase). So it's worth paying attention to performance and doing what we can to improve it!
**Improving the performance of a rule is a great way to contribute if you want a quick little project.** Try picking a rule and seeing if there's anything you can do to speed it up.
Make sure you include benchmark measurements in your pull request!
+35
View File
@@ -0,0 +1,35 @@
# Working on syntaxes
Please help us enhance and debug the [syntaxes](../about/syntaxes.md) we use in stylelint:
- [postcss-css-in-js](https://github.com/stylelint/postcss-css-in-js)
- [postcss-html](https://github.com/gucong3000/postcss-html)
- [postcss-less](https://github.com/webschik/postcss-less)
- [postcss-markdown](https://github.com/stylelint/postcss-markdown)
- [postcss-sass](https://github.com/AleshaOleg/postcss-sass)
- [postcss-scss](https://github.com/postcss/postcss-scss)
To contribute to a syntax, you should:
1. Familiarize yourself with PostCSS's [how to write custom syntax](https://github.com/postcss/postcss/blob/master/docs/syntax.md) guide.
2. Use the [`syntax: *` labels](https://github.com/stylelint/stylelint/labels?utf8=%E2%9C%93&q=syntax%3A) to identify which syntax is behind an issue.
3. Go to the repository for that syntax.
4. Read their contributing guidelines.
## Workarounds
Fixing bugs in syntaxes can take time. stylelint can work around these bug by turning off autofix for incompatible sources. Autofix can then remain safe to use while contributors try to fix the underlying issue.
### Current workarounds
stylelint currently turns off autofix for sources that contain:
- ~~nested tagged template literals ([issue #4119](https://github.com/stylelint/stylelint/issues/4119))~~
### Add a workaround
To add a new workaround, you should:
1. Add code to [`lib/lintSource.js`](https://github.com/stylelint/stylelint/blob/master/lib/lintSource.js) to detect the incompatible pattern.
2. Add a corresponding test to [`lib/__tests__/standalone-fix.test.js`](https://github.com/stylelint/stylelint/blob/master/lib/__tests__/standalone-fix.test.js).
3. Document the workaround in [`docs/developer-guides/syntaxes.md`](https://github.com/stylelint/stylelint/blob/master/docs/developer-guide/syntaxes.md).
+23
View File
@@ -0,0 +1,23 @@
# Writing system tests
System tests verify that stylelint works as expected. They are another line of defense against regressions, after the unit tests and integration tests.
Each of these system tests asserts that we end up with some expected output, given a configuration and a stylesheet.
These tests should not be comprehensive and systematic (_the unit tests should_). They should reproduce real use-cases and verify that those use-cases work as expected.
## Jest snapshots
The tests use Jest snapshots, so we can easily:
- assert against potentially large objects and strings
- update expectations as needed.
## The pattern
To add a system test, you should:
- add a test-case folder to `system-tests/` incrementing the number from existing test cases
- add a configuration file and a stylesheet
- add a `fs.test.js` and `no-fs.test.js` following the format established by existing tests, and using the `systemTestUtils`
- take a snapshot of `output`