if some problems reported by the rule are automatically fixable by the --fix
command line option
if the "extends": "eslint:recommended"
property in a configuration file enables the rule.
-
-![]() -Bryan Mishkin - - |
-
-![]() -Francesco Trotta - - |
-
-![]() -Yosuke Ota + + ![]() +Josh Goldberg ⨠|
![]() Tanuj Kanti + |
+
+![]() +ëŖ¨ë°LuMir + |
if some problems reported by the rule are automatically fixable by the --fix
command line option
if the "extends": "eslint:recommended"
property in a configuration file enables the rule.
if some problems reported by the rule are manually fixable by editor suggestions
-
- The "extends": "eslint:recommended"
property in a configuration file enables this rule
+ Using the recommended
config from @eslint/js
in a configuration file
+ enables this rule
- Some problems reported by this rule are automatically fixable by the --fix
command line option
+ Some problems reported by this rule are automatically fixable by the --fix
command line option
- Some problems reported by this rule are manually fixable by editor suggestions + Some problems reported by this rule are manually fixable by editor suggestions +
++ This rule is currently frozen and is not accepting feature requests.
- if the "extends": "eslint:recommended"
property in a configuration file enables the rule.
+ if the recommended
config from @eslint/js
in a configuration file enables the rule.
+ if the rule is currently frozen and not accepting feature requests. +
+{{ rule }}
{% endfor %}
+{%- macro replacementRuleList(params) -%}
+ {% for specifier in params.specifiers %}
+ {{ specifier.rule.name }}
+ {% if specifier.plugin %} in {{ specifier.plugin.name }}
{% endif %}
+ {%- if loop.length > 1 and not loop.last -%} or Replaced by {{ ruleList({ rules: params.replacedBy }) }}
+Replaced by {{ replacementRuleList({ specifiers: params.replacedBy }) }}
{%- else -%}{{ params.description }}
{%- endif -%} {%- elseif params.removed == true -%} @@ -17,12 +17,17 @@ {{ params.name }} removed - {%- if params.replacedBy -%} -Replaced by {{ ruleList({ rules: params.replacedBy }) }}
+ {%- if params.replacedBy|length -%} +Replaced by {{ replacementRuleList({ specifiers: params.replacedBy }) }}
{%- else -%}{{ params.description }}
{%- endif -%} {%- else -%} +âī¸ Frozen
+ {%- endif -%} +{{ params.description }}
{%- endif -%} diff --git a/docs/src/_includes/components/search.html b/docs/src/_includes/components/search.html index 336385869b82..9635900f7529 100644 --- a/docs/src/_includes/components/search.html +++ b/docs/src/_includes/components/search.html @@ -9,14 +9,51 @@Results will be shown and updated as you type.
This rule was deprecated in ESLint v{{ rule_meta.deprecated.deprecatedSince }}. + + {% if rule_meta.deprecated.replacedBy.length === 0 %} + There is no replacement rule. + {% elif rule_meta.deprecated.replacedBy[0].plugin %} + Please use the corresponding rule in {{ rule_meta.deprecated.replacedBy[0].plugin.name }}. + {% else %} + Please replace the rule with {{ rule_meta.deprecated.replacedBy[0].rule.name }}. + {% endif %} + {% endset %} + + {% important deprecated_description, rule_meta.deprecated.url %} + {% endif %} {% endif %} {% include 'components/docs-toc.html' %} @@ -101,7 +119,13 @@
${result}
`;
+ return `${result}
`;
};
/**
@@ -58,33 +61,33 @@ const highlighter = function (md, str, lang) {
* @param {MarkdownIt} md
* @license MIT License. See file header.
*/
-const lineNumberPlugin = (md) => {
- const fence = md.renderer.rules.fence;
- md.renderer.rules.fence = (...args) => {
- const [tokens, idx] = args;
- const lang = tokens[idx].info.trim();
- const rawCode = fence(...args);
- const code = rawCode.slice(
- rawCode.indexOf(""),
- rawCode.indexOf("
")
- );
- const lines = code.split("\n");
- const lineNumbersCode = [...Array(lines.length - 1)]
- .map(
- (line, index) =>
- `${index + 1}"),
+ rawCode.indexOf("
"),
+ );
+ const lines = code.split("\n");
+ const lineNumbersCode = [...Array(lines.length - 1)]
+ .map(
+ (line, index) =>
+ `${index + 1}/, ``)
+ const finalCode = rawCode
+ .replace(/<\/pre>\n/, `${lineNumbersWrapperCode}`)
+ .replace(/"(language-\S*?)"/, '"$1 line-numbers-mode"')
+ .replace(//, ``);
- return finalCode;
- };
+ return finalCode;
+ };
};
module.exports.highlighter = highlighter;
diff --git a/docs/src/_plugins/pre-wrapper.js b/docs/src/_plugins/pre-wrapper.js
new file mode 100644
index 000000000000..beb227486366
--- /dev/null
+++ b/docs/src/_plugins/pre-wrapper.js
@@ -0,0 +1,23 @@
+module.exports = md => {
+ const defaultFenceRenderer = md.renderer.rules.fence;
+
+ md.renderer.rules.fence = (...args) => {
+ const [tokens, index] = args;
+
+ if (/^\s*(?:in)?correct(?!\S)/u.test(tokens[index - 1].info)) {
+ return defaultFenceRenderer(...args);
+ }
+
+ return `
+
+ ${defaultFenceRenderer(...args)}
+
+
+`;
+ };
+};
diff --git a/docs/src/about/index.md b/docs/src/about/index.md
index 0f59d9cf39c0..53bf47e185eb 100644
--- a/docs/src/about/index.md
+++ b/docs/src/about/index.md
@@ -1,6 +1,5 @@
---
title: About
-
---
ESLint is an open source JavaScript linting utility originally created by Nicholas C. Zakas in June 2013. Code [linting][] is a type of static analysis that is frequently used to find problematic patterns or code that doesn't adhere to certain style guidelines. There are code linters for most programming languages, and compilers sometimes incorporate linting into the compilation process.
@@ -18,24 +17,24 @@ ESLint is written using Node.js to provide a fast runtime environment and easy i
Everything is pluggable:
-* Rule API is used both by bundled and custom rules
-* Formatter API is used both by bundled and custom formatters
-* Additional rules and formatters can be specified at runtime
-* Rules and formatters don't have to be bundled to be used
+- Rule API is used both by bundled and custom rules.
+- Formatter API is used both by bundled and custom formatters.
+- Additional rules and formatters can be specified at runtime.
+- Rules and formatters don't have to be bundled to be used.
Every rule:
-* Is standalone
-* Can be turned off or on (nothing can be deemed "too important to turn off")
-* Can be set to a warning or error individually
+- Is standalone.
+- Can be turned off or on (nothing can be deemed "too important to turn off").
+- Can be set to a warning or error individually.
Additionally:
-* Rules are "agenda free" - ESLint does not promote any particular coding style
-* Any bundled rules are generalizable
+- Rules are "agenda free" - ESLint does not promote any particular coding style.
+- Any bundled rules are generalizable.
The project:
-* Values documentation and clear communication
-* Is as transparent as possible
-* Believes in the importance of testing
+- Values documentation and clear communication.
+- Is as transparent as possible.
+- Believes in the importance of testing.
diff --git a/docs/src/assets/images/404.png b/docs/src/assets/images/404.png
index 347d16086ef8..2663176abc42 100644
Binary files a/docs/src/assets/images/404.png and b/docs/src/assets/images/404.png differ
diff --git a/docs/src/assets/images/architecture/dependency.svg b/docs/src/assets/images/architecture/dependency.svg
index 1609b53e1d99..b4809cc5137e 100644
--- a/docs/src/assets/images/architecture/dependency.svg
+++ b/docs/src/assets/images/architecture/dependency.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-dowhilestatement.svg b/docs/src/assets/images/code-path-analysis/example-dowhilestatement.svg
index f81d36123c4a..c8c189697f83 100644
--- a/docs/src/assets/images/code-path-analysis/example-dowhilestatement.svg
+++ b/docs/src/assets/images/code-path-analysis/example-dowhilestatement.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-forinstatement.svg b/docs/src/assets/images/code-path-analysis/example-forinstatement.svg
index a6bc754b1be8..e9c6e60d0c92 100644
--- a/docs/src/assets/images/code-path-analysis/example-forinstatement.svg
+++ b/docs/src/assets/images/code-path-analysis/example-forinstatement.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-forstatement-for-ever.svg b/docs/src/assets/images/code-path-analysis/example-forstatement-for-ever.svg
index 4d334ca62d9e..603fe468c172 100644
--- a/docs/src/assets/images/code-path-analysis/example-forstatement-for-ever.svg
+++ b/docs/src/assets/images/code-path-analysis/example-forstatement-for-ever.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-forstatement.svg b/docs/src/assets/images/code-path-analysis/example-forstatement.svg
index aa0ccf0d82f1..7b06b6ab4a48 100644
--- a/docs/src/assets/images/code-path-analysis/example-forstatement.svg
+++ b/docs/src/assets/images/code-path-analysis/example-forstatement.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-hello-world.svg b/docs/src/assets/images/code-path-analysis/example-hello-world.svg
index fc28d1fdaf9c..7b0d775c6030 100644
--- a/docs/src/assets/images/code-path-analysis/example-hello-world.svg
+++ b/docs/src/assets/images/code-path-analysis/example-hello-world.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-ifstatement-chain.svg b/docs/src/assets/images/code-path-analysis/example-ifstatement-chain.svg
index 0944c3bcf59c..e21fd583f251 100644
--- a/docs/src/assets/images/code-path-analysis/example-ifstatement-chain.svg
+++ b/docs/src/assets/images/code-path-analysis/example-ifstatement-chain.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-ifstatement.svg b/docs/src/assets/images/code-path-analysis/example-ifstatement.svg
index b83c67b51061..25061c57b9f0 100644
--- a/docs/src/assets/images/code-path-analysis/example-ifstatement.svg
+++ b/docs/src/assets/images/code-path-analysis/example-ifstatement.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-switchstatement-has-default.svg b/docs/src/assets/images/code-path-analysis/example-switchstatement-has-default.svg
index 5d6d73998b37..2afa1c551838 100644
--- a/docs/src/assets/images/code-path-analysis/example-switchstatement-has-default.svg
+++ b/docs/src/assets/images/code-path-analysis/example-switchstatement-has-default.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-switchstatement.svg b/docs/src/assets/images/code-path-analysis/example-switchstatement.svg
index e43e5e11190b..4ec5982178c6 100644
--- a/docs/src/assets/images/code-path-analysis/example-switchstatement.svg
+++ b/docs/src/assets/images/code-path-analysis/example-switchstatement.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-trystatement-try-catch-finally.svg b/docs/src/assets/images/code-path-analysis/example-trystatement-try-catch-finally.svg
index 60ec1cdf69b6..fcd016cdc160 100644
--- a/docs/src/assets/images/code-path-analysis/example-trystatement-try-catch-finally.svg
+++ b/docs/src/assets/images/code-path-analysis/example-trystatement-try-catch-finally.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-trystatement-try-catch.svg b/docs/src/assets/images/code-path-analysis/example-trystatement-try-catch.svg
index a2a0c8af2507..611b290effad 100644
--- a/docs/src/assets/images/code-path-analysis/example-trystatement-try-catch.svg
+++ b/docs/src/assets/images/code-path-analysis/example-trystatement-try-catch.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-trystatement-try-finally.svg b/docs/src/assets/images/code-path-analysis/example-trystatement-try-finally.svg
index 68c7801b7cd8..5125c96f111c 100644
--- a/docs/src/assets/images/code-path-analysis/example-trystatement-try-finally.svg
+++ b/docs/src/assets/images/code-path-analysis/example-trystatement-try-finally.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-when-there-is-a-function-f.svg b/docs/src/assets/images/code-path-analysis/example-when-there-is-a-function-f.svg
index 53bb946cf162..bd527c0017ad 100644
--- a/docs/src/assets/images/code-path-analysis/example-when-there-is-a-function-f.svg
+++ b/docs/src/assets/images/code-path-analysis/example-when-there-is-a-function-f.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-when-there-is-a-function-g.svg b/docs/src/assets/images/code-path-analysis/example-when-there-is-a-function-g.svg
index 4d3fe12b4a8f..ca22f81e2a09 100644
--- a/docs/src/assets/images/code-path-analysis/example-when-there-is-a-function-g.svg
+++ b/docs/src/assets/images/code-path-analysis/example-when-there-is-a-function-g.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/example-whilestatement.svg b/docs/src/assets/images/code-path-analysis/example-whilestatement.svg
index f03944389cda..8f785b160af0 100644
--- a/docs/src/assets/images/code-path-analysis/example-whilestatement.svg
+++ b/docs/src/assets/images/code-path-analysis/example-whilestatement.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/helo.svg b/docs/src/assets/images/code-path-analysis/helo.svg
index cd72a37d9aff..00c84fbea56a 100644
--- a/docs/src/assets/images/code-path-analysis/helo.svg
+++ b/docs/src/assets/images/code-path-analysis/helo.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/loop-event-example-for-1.svg b/docs/src/assets/images/code-path-analysis/loop-event-example-for-1.svg
index 727ec12b132d..8a09c3e80b9a 100644
--- a/docs/src/assets/images/code-path-analysis/loop-event-example-for-1.svg
+++ b/docs/src/assets/images/code-path-analysis/loop-event-example-for-1.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/loop-event-example-for-2.svg b/docs/src/assets/images/code-path-analysis/loop-event-example-for-2.svg
index 70d762f38bc1..06e4e7d15eba 100644
--- a/docs/src/assets/images/code-path-analysis/loop-event-example-for-2.svg
+++ b/docs/src/assets/images/code-path-analysis/loop-event-example-for-2.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/loop-event-example-for-3.svg b/docs/src/assets/images/code-path-analysis/loop-event-example-for-3.svg
index 5adea136b40e..78bb754d93f4 100644
--- a/docs/src/assets/images/code-path-analysis/loop-event-example-for-3.svg
+++ b/docs/src/assets/images/code-path-analysis/loop-event-example-for-3.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/loop-event-example-for-4.svg b/docs/src/assets/images/code-path-analysis/loop-event-example-for-4.svg
index 99389751f340..433dec08d24c 100644
--- a/docs/src/assets/images/code-path-analysis/loop-event-example-for-4.svg
+++ b/docs/src/assets/images/code-path-analysis/loop-event-example-for-4.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/loop-event-example-for-5.svg b/docs/src/assets/images/code-path-analysis/loop-event-example-for-5.svg
index 070decb12924..e5f32155b70d 100644
--- a/docs/src/assets/images/code-path-analysis/loop-event-example-for-5.svg
+++ b/docs/src/assets/images/code-path-analysis/loop-event-example-for-5.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/loop-event-example-while-1.svg b/docs/src/assets/images/code-path-analysis/loop-event-example-while-1.svg
index 7d0c1a02d686..2db2fa29193f 100644
--- a/docs/src/assets/images/code-path-analysis/loop-event-example-while-1.svg
+++ b/docs/src/assets/images/code-path-analysis/loop-event-example-while-1.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/loop-event-example-while-2.svg b/docs/src/assets/images/code-path-analysis/loop-event-example-while-2.svg
index d5c31e276ca9..88c61fc4f51f 100644
--- a/docs/src/assets/images/code-path-analysis/loop-event-example-while-2.svg
+++ b/docs/src/assets/images/code-path-analysis/loop-event-example-while-2.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/code-path-analysis/loop-event-example-while-3.svg b/docs/src/assets/images/code-path-analysis/loop-event-example-while-3.svg
index 3f4e02c17db2..a372021d71a4 100644
--- a/docs/src/assets/images/code-path-analysis/loop-event-example-while-3.svg
+++ b/docs/src/assets/images/code-path-analysis/loop-event-example-while-3.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/configure/config-inspector.png b/docs/src/assets/images/configure/config-inspector.png
new file mode 100644
index 000000000000..b5faf7928190
Binary files /dev/null and b/docs/src/assets/images/configure/config-inspector.png differ
diff --git a/docs/src/assets/images/icons/arrow-left.svg b/docs/src/assets/images/icons/arrow-left.svg
index 83483a7f256a..c5e777a8bba4 100644
--- a/docs/src/assets/images/icons/arrow-left.svg
+++ b/docs/src/assets/images/icons/arrow-left.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/arrow-right.svg b/docs/src/assets/images/icons/arrow-right.svg
index 22bb24fc3d4d..c1117a5cd29c 100644
--- a/docs/src/assets/images/icons/arrow-right.svg
+++ b/docs/src/assets/images/icons/arrow-right.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/arrow-top-right.svg b/docs/src/assets/images/icons/arrow-top-right.svg
index 58bbed85264f..37705b13cbc5 100644
--- a/docs/src/assets/images/icons/arrow-top-right.svg
+++ b/docs/src/assets/images/icons/arrow-top-right.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/chevron-down.svg b/docs/src/assets/images/icons/chevron-down.svg
index b09f7f73216a..46788a0f8626 100644
--- a/docs/src/assets/images/icons/chevron-down.svg
+++ b/docs/src/assets/images/icons/chevron-down.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/copy.svg b/docs/src/assets/images/icons/copy.svg
index 24fc6afae9c7..29fd98808f9f 100644
--- a/docs/src/assets/images/icons/copy.svg
+++ b/docs/src/assets/images/icons/copy.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/correct.svg b/docs/src/assets/images/icons/correct.svg
index 4f589241cb28..a99d10ca0dac 100644
--- a/docs/src/assets/images/icons/correct.svg
+++ b/docs/src/assets/images/icons/correct.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/discord.svg b/docs/src/assets/images/icons/discord.svg
index 16bae7b3c46e..03032099b171 100644
--- a/docs/src/assets/images/icons/discord.svg
+++ b/docs/src/assets/images/icons/discord.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/facebook.svg b/docs/src/assets/images/icons/facebook.svg
index 194c83485029..1fe1fbeef8ae 100644
--- a/docs/src/assets/images/icons/facebook.svg
+++ b/docs/src/assets/images/icons/facebook.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/features-list-icon.svg b/docs/src/assets/images/icons/features-list-icon.svg
index 2e576cff9fd8..9a09f471be6f 100644
--- a/docs/src/assets/images/icons/features-list-icon.svg
+++ b/docs/src/assets/images/icons/features-list-icon.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/github-icon-mono.svg b/docs/src/assets/images/icons/github-icon-mono.svg
index f73b88b55b4f..2aca9b078599 100644
--- a/docs/src/assets/images/icons/github-icon-mono.svg
+++ b/docs/src/assets/images/icons/github-icon-mono.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/github-img.svg b/docs/src/assets/images/icons/github-img.svg
index 51ad25a46eb8..3144d364f9d8 100644
--- a/docs/src/assets/images/icons/github-img.svg
+++ b/docs/src/assets/images/icons/github-img.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/github-large.svg b/docs/src/assets/images/icons/github-large.svg
index c540e36168fc..14905d7e65d9 100644
--- a/docs/src/assets/images/icons/github-large.svg
+++ b/docs/src/assets/images/icons/github-large.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/github-small.svg b/docs/src/assets/images/icons/github-small.svg
index b410d3adcdff..31621c7815bf 100644
--- a/docs/src/assets/images/icons/github-small.svg
+++ b/docs/src/assets/images/icons/github-small.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/github.svg b/docs/src/assets/images/icons/github.svg
index 0f3149634b9e..fe7c60f0836f 100644
--- a/docs/src/assets/images/icons/github.svg
+++ b/docs/src/assets/images/icons/github.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/google.svg b/docs/src/assets/images/icons/google.svg
index 8b149df54a95..3675e9cd20f1 100644
--- a/docs/src/assets/images/icons/google.svg
+++ b/docs/src/assets/images/icons/google.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/incorrect.svg b/docs/src/assets/images/icons/incorrect.svg
index 666811ebe475..e58babe33c7e 100644
--- a/docs/src/assets/images/icons/incorrect.svg
+++ b/docs/src/assets/images/icons/incorrect.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/languages.svg b/docs/src/assets/images/icons/languages.svg
index 2653515fe681..7ab95865e607 100644
--- a/docs/src/assets/images/icons/languages.svg
+++ b/docs/src/assets/images/icons/languages.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/learn-more-arrow.svg b/docs/src/assets/images/icons/learn-more-arrow.svg
index 8aab0b95e408..de1617f34e09 100644
--- a/docs/src/assets/images/icons/learn-more-arrow.svg
+++ b/docs/src/assets/images/icons/learn-more-arrow.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/link.svg b/docs/src/assets/images/icons/link.svg
index 6dfe15866b02..7301ad7a097a 100644
--- a/docs/src/assets/images/icons/link.svg
+++ b/docs/src/assets/images/icons/link.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/linkedin.svg b/docs/src/assets/images/icons/linkedin.svg
index a7c36f64e258..84b8f078499f 100644
--- a/docs/src/assets/images/icons/linkedin.svg
+++ b/docs/src/assets/images/icons/linkedin.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/menu.svg b/docs/src/assets/images/icons/menu.svg
index d068dbd04db0..83648a811009 100644
--- a/docs/src/assets/images/icons/menu.svg
+++ b/docs/src/assets/images/icons/menu.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/minus-circle.svg b/docs/src/assets/images/icons/minus-circle.svg
index f8e8023389a0..6f4a04b93be1 100644
--- a/docs/src/assets/images/icons/minus-circle.svg
+++ b/docs/src/assets/images/icons/minus-circle.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/npm.svg b/docs/src/assets/images/icons/npm.svg
index c9baf323174d..1a40a8c8aab9 100644
--- a/docs/src/assets/images/icons/npm.svg
+++ b/docs/src/assets/images/icons/npm.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/open-collectione-mono.svg b/docs/src/assets/images/icons/open-collectione-mono.svg
index 660478343ac0..2d042792a152 100644
--- a/docs/src/assets/images/icons/open-collectione-mono.svg
+++ b/docs/src/assets/images/icons/open-collectione-mono.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/opencollective-img.svg b/docs/src/assets/images/icons/opencollective-img.svg
index a3b46dcd5d4b..c6f318c77ae0 100644
--- a/docs/src/assets/images/icons/opencollective-img.svg
+++ b/docs/src/assets/images/icons/opencollective-img.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/plus-circle.svg b/docs/src/assets/images/icons/plus-circle.svg
index 58533a0b7bdf..54b257e96c76 100644
--- a/docs/src/assets/images/icons/plus-circle.svg
+++ b/docs/src/assets/images/icons/plus-circle.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/search.svg b/docs/src/assets/images/icons/search.svg
index 6c70237669ba..76d7735d1f40 100644
--- a/docs/src/assets/images/icons/search.svg
+++ b/docs/src/assets/images/icons/search.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/icons/twitter.svg b/docs/src/assets/images/icons/twitter.svg
index ffee249edaed..4aa1168ccf2d 100644
--- a/docs/src/assets/images/icons/twitter.svg
+++ b/docs/src/assets/images/icons/twitter.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/logo/brand-colors.svg b/docs/src/assets/images/logo/brand-colors.svg
index 2c2048de281d..3b9f9a78c760 100644
--- a/docs/src/assets/images/logo/brand-colors.svg
+++ b/docs/src/assets/images/logo/brand-colors.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/logo/eslint-logo-color.png b/docs/src/assets/images/logo/eslint-logo-color.png
index efa54ec778d2..27c0be651419 100644
Binary files a/docs/src/assets/images/logo/eslint-logo-color.png and b/docs/src/assets/images/logo/eslint-logo-color.png differ
diff --git a/docs/src/assets/images/logo/eslint-logo-color.svg b/docs/src/assets/images/logo/eslint-logo-color.svg
index 5a8dbfc6818f..5f780a8f73c4 100644
--- a/docs/src/assets/images/logo/eslint-logo-color.svg
+++ b/docs/src/assets/images/logo/eslint-logo-color.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/images/logo/eslint-logo-white.svg b/docs/src/assets/images/logo/eslint-logo-white.svg
index 2493dc4cfdfc..b9608ce950e7 100644
--- a/docs/src/assets/images/logo/eslint-logo-white.svg
+++ b/docs/src/assets/images/logo/eslint-logo-white.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/src/assets/js/components-index.js b/docs/src/assets/js/components-index.js
index 9e3bc0f5cf9c..3e26526a3e8e 100644
--- a/docs/src/assets/js/components-index.js
+++ b/docs/src/assets/js/components-index.js
@@ -1,35 +1,35 @@
-(function() {
- var index_trigger = document.getElementById("js-index-toggle"),
- index = document.getElementById("js-index-list"),
- body = document.getElementsByTagName("body")[0],
- open = false;
+(function () {
+ var index_trigger = document.getElementById("js-index-toggle"),
+ index = document.getElementById("js-index-list"),
+ body = document.getElementsByTagName("body")[0],
+ open = false;
- if (matchMedia) {
- const mq = window.matchMedia("(max-width: 1023px)");
- mq.addEventListener('change', WidthChange);
- WidthChange(mq);
- }
+ if (matchMedia) {
+ const mq = window.matchMedia("(max-width: 1023px)");
+ mq.addEventListener("change", WidthChange);
+ WidthChange(mq);
+ }
- function WidthChange(mq) {
- initIndex();
- }
+ function WidthChange(mq) {
+ initIndex();
+ }
- function toggleindex(e) {
- if (!open) {
- this.setAttribute("aria-expanded", "true");
- index.setAttribute("data-open", "true");
- open = true;
- } else {
- this.setAttribute("aria-expanded", "false");
- index.setAttribute("data-open", "false");
- open = false;
- }
- }
+ function toggleindex(e) {
+ if (!open) {
+ this.setAttribute("aria-expanded", "true");
+ index.setAttribute("data-open", "true");
+ open = true;
+ } else {
+ this.setAttribute("aria-expanded", "false");
+ index.setAttribute("data-open", "false");
+ open = false;
+ }
+ }
- function initIndex() {
- index_trigger.removeAttribute("hidden");
- index_trigger.setAttribute("aria-expanded", "false");
- index.setAttribute("data-open", "false");
- index_trigger.addEventListener("click", toggleindex, false);
- }
+ function initIndex() {
+ index_trigger.removeAttribute("hidden");
+ index_trigger.setAttribute("aria-expanded", "false");
+ index.setAttribute("data-open", "false");
+ index_trigger.addEventListener("click", toggleindex, false);
+ }
})();
diff --git a/docs/src/assets/js/css-vars-ponyfill@2.js b/docs/src/assets/js/css-vars-ponyfill@2.js
index 3285a577a2a9..7a8d79b7a822 100644
--- a/docs/src/assets/js/css-vars-ponyfill@2.js
+++ b/docs/src/assets/js/css-vars-ponyfill@2.js
@@ -5,43 +5,1494 @@
* (c) 2018-2019 John Hildenbiddle
* MIT license
*/
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).cssVars=t()}(this,function(){"use strict";function e(){return(e=Object.assign||function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{},r={mimeType:t.mimeType||null,onBeforeSend:t.onBeforeSend||Function.prototype,onSuccess:t.onSuccess||Function.prototype,onError:t.onError||Function.prototype,onComplete:t.onComplete||Function.prototype},n=Array.isArray(e)?e:[e],o=Array.apply(null,Array(n.length)).map(function(e){return null});function s(){return!("<"===(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"").trim().charAt(0))}function a(e,t){r.onError(e,n[t],t)}function c(e,t){var s=r.onSuccess(e,n[t],t);e=!1===s?"":s||e,o[t]=e,-1===o.indexOf(null)&&r.onComplete(o)}var i=document.createElement("a");n.forEach(function(e,t){if(i.setAttribute("href",e),i.href=String(i.href),Boolean(document.all&&!window.atob)&&i.host.split(":")[0]!==location.host.split(":")[0]){if(i.protocol===location.protocol){var n=new XDomainRequest;n.open("GET",e),n.timeout=0,n.onprogress=Function.prototype,n.ontimeout=Function.prototype,n.onload=function(){s(n.responseText)?c(n.responseText,t):a(n,t)},n.onerror=function(e){a(n,t)},setTimeout(function(){n.send()},0)}else console.warn("Internet Explorer 9 Cross-Origin (CORS) requests must use the same protocol (".concat(e,")")),a(null,t)}else{var o=new XMLHttpRequest;o.open("GET",e),r.mimeType&&o.overrideMimeType&&o.overrideMimeType(r.mimeType),r.onBeforeSend(o,e,t),o.onreadystatechange=function(){4===o.readyState&&(200===o.status&&s(o.responseText)?c(o.responseText,t):a(o,t))},o.send()}})}function n(e){var t={cssComments:/\/\*[\s\S]+?\*\//g,cssImports:/(?:@import\s*)(?:url\(\s*)?(?:['"])([^'"]*)(?:['"])(?:\s*\))?(?:[^;]*;)/g},n={rootElement:e.rootElement||document,include:e.include||'style,link[rel="stylesheet"]',exclude:e.exclude||null,filter:e.filter||null,useCSSOM:e.useCSSOM||!1,onBeforeSend:e.onBeforeSend||Function.prototype,onSuccess:e.onSuccess||Function.prototype,onError:e.onError||Function.prototype,onComplete:e.onComplete||Function.prototype},s=Array.apply(null,n.rootElement.querySelectorAll(n.include)).filter(function(e){return t=e,r=n.exclude,!(t.matches||t.matchesSelector||t.webkitMatchesSelector||t.mozMatchesSelector||t.msMatchesSelector||t.oMatchesSelector).call(t,r);var t,r}),a=Array.apply(null,Array(s.length)).map(function(e){return null});function c(){if(-1===a.indexOf(null)){var e=a.join("");n.onComplete(e,a,s)}}function i(e,t,o,s){var i=n.onSuccess(e,o,s);(function e(t,o,s,a){var c=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[];var i=arguments.length>5&&void 0!==arguments[5]?arguments[5]:[];var l=u(t,s,i);l.rules.length?r(l.absoluteUrls,{onBeforeSend:function(e,t,r){n.onBeforeSend(e,o,t)},onSuccess:function(e,t,r){var s=n.onSuccess(e,o,t),a=u(e=!1===s?"":s||e,t,i);return a.rules.forEach(function(t,r){e=e.replace(t,a.absoluteRules[r])}),e},onError:function(r,n,u){c.push({xhr:r,url:n}),i.push(l.rules[u]),e(t,o,s,a,c,i)},onComplete:function(r){r.forEach(function(e,r){t=t.replace(l.rules[r],e)}),e(t,o,s,a,c,i)}}):a(t,c)})(e=void 0!==i&&!1===Boolean(i)?"":i||e,o,s,function(e,r){null===a[t]&&(r.forEach(function(e){return n.onError(e.xhr,o,e.url)}),!n.filter||n.filter.test(e)?a[t]=e:a[t]="",c())})}function u(e,r){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],s={};return s.rules=(e.replace(t.cssComments,"").match(t.cssImports)||[]).filter(function(e){return-1===n.indexOf(e)}),s.urls=s.rules.map(function(e){return e.replace(t.cssImports,"$1")}),s.absoluteUrls=s.urls.map(function(e){return o(e,r)}),s.absoluteRules=s.rules.map(function(e,t){var n=s.urls[t],a=o(s.absoluteUrls[t],r);return e.replace(n,a)}),s}s.length?s.forEach(function(e,t){var s=e.getAttribute("href"),u=e.getAttribute("rel"),l="LINK"===e.nodeName&&s&&u&&"stylesheet"===u.toLowerCase(),f="STYLE"===e.nodeName;if(l)r(s,{mimeType:"text/css",onBeforeSend:function(t,r,o){n.onBeforeSend(t,e,r)},onSuccess:function(r,n,a){var c=o(s,location.href);i(r,t,e,c)},onError:function(r,o,s){a[t]="",n.onError(r,e,o),c()}});else if(f){var d=e.textContent;n.useCSSOM&&(d=Array.apply(null,e.sheet.cssRules).map(function(e){return e.cssText}).join("")),i(d,t,e,location.href)}else a[t]="",c()}):n.onComplete("",[])}function o(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:location.href,r=document.implementation.createHTMLDocument(""),n=r.createElement("base"),o=r.createElement("a");return r.head.appendChild(n),r.body.appendChild(o),n.href=t,o.href=e,o.href}var s=a;function a(e,t,r){e instanceof RegExp&&(e=c(e,r)),t instanceof RegExp&&(t=c(t,r));var n=i(e,t,r);return n&&{start:n[0],end:n[1],pre:r.slice(0,n[0]),body:r.slice(n[0]+e.length,n[1]),post:r.slice(n[1]+t.length)}}function c(e,t){var r=t.match(e);return r?r[0]:null}function i(e,t,r){var n,o,s,a,c,i=r.indexOf(e),u=r.indexOf(t,i+1),l=i;if(i>=0&&u>0){for(n=[],s=r.length;l>=0&&!c;)l==i?(n.push(l),i=r.indexOf(e,l+1)):1==n.length?c=[n.pop(),u]:((o=n.pop())=0?i:u;n.length&&(c=[s,a])}return c}function u(t){var r=e({},{preserveStatic:!0,removeComments:!1},arguments.length>1&&void 0!==arguments[1]?arguments[1]:{});function n(e){throw new Error("CSS parse error: ".concat(e))}function o(e){var r=e.exec(t);if(r)return t=t.slice(r[0].length),r}function a(){return o(/^{\s*/)}function c(){return o(/^}/)}function i(){o(/^\s*/)}function u(){if(i(),"/"===t[0]&&"*"===t[1]){for(var e=2;t[e]&&("*"!==t[e]||"/"!==t[e+1]);)e++;if(!t[e])return n("end of comment is missing");var r=t.slice(2,e);return t=t.slice(e+2),{type:"comment",comment:r}}}function l(){for(var e,t=[];e=u();)t.push(e);return r.removeComments?[]:t}function f(){for(i();"}"===t[0];)n("extra closing bracket");var e=o(/^(("(?:\\"|[^"])*"|'(?:\\'|[^'])*'|[^{])+)/);if(e)return e[0].trim().replace(/\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*\/+/g,"").replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g,function(e){return e.replace(/,/g,"â")}).split(/\s*(?![^(]*\)),\s*/).map(function(e){return e.replace(/\u200C/g,",")})}function d(){o(/^([;\s]*)+/);var e=/\/\*[^*]*\*+([^\/*][^*]*\*+)*\//g,t=o(/^(\*?[-#\/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/);if(t){if(t=t[0].trim(),!o(/^:\s*/))return n("property missing ':'");var r=o(/^((?:\/\*.*?\*\/|'(?:\\'|.)*?'|"(?:\\"|.)*?"|\((\s*'(?:\\'|.)*?'|"(?:\\"|.)*?"|[^)]*?)\s*\)|[^};])+)/),s={type:"declaration",property:t.replace(e,""),value:r?r[0].replace(e,"").trim():""};return o(/^[;\s]*/),s}}function p(){if(!a())return n("missing '{'");for(var e,t=l();e=d();)t.push(e),t=t.concat(l());return c()?t:n("missing '}'")}function m(){i();for(var e,t=[];e=o(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/);)t.push(e[1]),o(/^,\s*/);if(t.length)return{type:"keyframe",values:t,declarations:p()}}function v(){if(i(),"@"===t[0]){var e=function(){var e=o(/^@([-\w]+)?keyframes\s*/);if(e){var t=e[1];if(!(e=o(/^([-\w]+)\s*/)))return n("@keyframes missing name");var r,s=e[1];if(!a())return n("@keyframes missing '{'");for(var i=l();r=m();)i.push(r),i=i.concat(l());return c()?{type:"keyframes",name:s,vendor:t,keyframes:i}:n("@keyframes missing '}'")}}()||function(){var e=o(/^@supports *([^{]+)/);if(e)return{type:"supports",supports:e[1].trim(),rules:y()}}()||function(){if(o(/^@host\s*/))return{type:"host",rules:y()}}()||function(){var e=o(/^@media([^{]+)*/);if(e)return{type:"media",media:(e[1]||"").trim(),rules:y()}}()||function(){var e=o(/^@custom-media\s+(--[^\s]+)\s*([^{;]+);/);if(e)return{type:"custom-media",name:e[1].trim(),media:e[2].trim()}}()||function(){if(o(/^@page */))return{type:"page",selectors:f()||[],declarations:p()}}()||function(){var e=o(/^@([-\w]+)?document *([^{]+)/);if(e)return{type:"document",document:e[2].trim(),vendor:e[1]?e[1].trim():null,rules:y()}}()||function(){if(o(/^@font-face\s*/))return{type:"font-face",declarations:p()}}()||function(){var e=o(/^@(import|charset|namespace)\s*([^;]+);/);if(e)return{type:e[1],name:e[2].trim()}}();if(e&&!r.preserveStatic){var s=!1;if(e.declarations)s=e.declarations.some(function(e){return/var\(/.test(e.value)});else s=(e.keyframes||e.rules||[]).some(function(e){return(e.declarations||[]).some(function(e){return/var\(/.test(e.value)})});return s?e:{}}return e}}function h(){if(!r.preserveStatic){var e=s("{","}",t);if(e){var o=/:(?:root|host)(?![.:#(])/.test(e.pre)&&/--\S*\s*:/.test(e.body),a=/var\(/.test(e.body);if(!o&&!a)return t=t.slice(e.end+1),{}}}var c=f()||[],i=r.preserveStatic?p():p().filter(function(e){var t=c.some(function(e){return/:(?:root|host)(?![.:#(])/.test(e)})&&/^--\S/.test(e.property),r=/var\(/.test(e.value);return t||r});return c.length||n("selector missing"),{type:"rule",selectors:c,declarations:i}}function y(e){if(!e&&!a())return n("missing '{'");for(var r,o=l();t.length&&(e||"}"!==t[0])&&(r=v()||h());)r.type&&o.push(r),o=o.concat(l());return e||c()?o:n("missing '}'")}return{type:"stylesheet",stylesheet:{rules:y(!0),errors:[]}}}function l(t){var r=e({},{parseHost:!1,store:{},onWarning:function(){}},arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}),n=new RegExp(":".concat(r.parseHost?"host":"root","(?![.:#(])"));return"string"==typeof t&&(t=u(t,r)),t.stylesheet.rules.forEach(function(e){"rule"===e.type&&e.selectors.some(function(e){return n.test(e)})&&e.declarations.forEach(function(e,t){var n=e.property,o=e.value;n&&0===n.indexOf("--")&&(r.store[n]=o)})}),r.store}function f(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",r=arguments.length>2?arguments[2]:void 0,n={charset:function(e){return"@charset "+e.name+";"},comment:function(e){return 0===e.comment.indexOf("__CSSVARSPONYFILL")?"/*"+e.comment+"*/":""},"custom-media":function(e){return"@custom-media "+e.name+" "+e.media+";"},declaration:function(e){return e.property+":"+e.value+";"},document:function(e){return"@"+(e.vendor||"")+"document "+e.document+"{"+o(e.rules)+"}"},"font-face":function(e){return"@font-face{"+o(e.declarations)+"}"},host:function(e){return"@host{"+o(e.rules)+"}"},import:function(e){return"@import "+e.name+";"},keyframe:function(e){return e.values.join(",")+"{"+o(e.declarations)+"}"},keyframes:function(e){return"@"+(e.vendor||"")+"keyframes "+e.name+"{"+o(e.keyframes)+"}"},media:function(e){return"@media "+e.media+"{"+o(e.rules)+"}"},namespace:function(e){return"@namespace "+e.name+";"},page:function(e){return"@page "+(e.selectors.length?e.selectors.join(", "):"")+"{"+o(e.declarations)+"}"},rule:function(e){var t=e.declarations;if(t.length)return e.selectors.join(",")+"{"+o(t)+"}"},supports:function(e){return"@supports "+e.supports+"{"+o(e.rules)+"}"}};function o(e){for(var o="",s=0;s1&&void 0!==arguments[1]?arguments[1]:{});return"string"==typeof t&&(t=u(t,r)),function e(t,r){t.rules.forEach(function(n){n.rules?e(n,r):n.keyframes?n.keyframes.forEach(function(e){"keyframe"===e.type&&r(e.declarations,n)}):n.declarations&&r(n.declarations,t)})}(t.stylesheet,function(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2?arguments[2]:void 0;if(-1===e.indexOf("var("))return e;var n=s("(",")",e);return n?"var"===n.pre.slice(-3)?0===n.body.trim().length?(t.onWarning("var() must contain a non-whitespace string"),e):n.pre.slice(0,-3)+function(e){var n=e.split(",")[0].replace(/[\s\n\t]/g,""),o=(e.match(/(?:\s*,\s*){1}(.*)?/)||[])[1],s=Object.prototype.hasOwnProperty.call(t.variables,n)?String(t.variables[n]):void 0,a=s||(o?String(o):void 0),c=r||e;return s||t.onWarning('variable "'.concat(n,'" is undefined')),a&&"undefined"!==a&&a.length>0?h(a,t,c):"var(".concat(c,")")}(n.body)+h(n.post,t):n.pre+"(".concat(h(n.body,t),")")+h(n.post,t):(-1!==e.indexOf("var(")&&t.onWarning('missing closing ")" in the value "'.concat(e,'"')),e)}var y="undefined"!=typeof window,g=y&&window.CSS&&window.CSS.supports&&window.CSS.supports("(--a: 0)"),S={group:0,job:0},b={rootElement:y?document:null,shadowDOM:!1,include:"style,link[rel=stylesheet]",exclude:"",variables:{},onlyLegacy:!0,preserveStatic:!0,preserveVars:!1,silent:!1,updateDOM:!0,updateURLs:!0,watch:null,onBeforeSend:function(){},onWarning:function(){},onError:function(){},onSuccess:function(){},onComplete:function(){}},E={cssComments:/\/\*[\s\S]+?\*\//g,cssKeyframes:/@(?:-\w*-)?keyframes/,cssMediaQueries:/@media[^{]+\{([\s\S]+?})\s*}/g,cssUrls:/url\((?!['"]?(?:data|http|\/\/):)['"]?([^'")]*)['"]?\)/g,cssVarDeclRules:/(?::(?:root|host)(?![.:#(])[\s,]*[^{]*{\s*[^}]*})/g,cssVarDecls:/(?:[\s;]*)(-{2}\w[\w-]*)(?:\s*:\s*)([^;]*);/g,cssVarFunc:/var\(\s*--[\w-]/,cssVars:/(?:(?::(?:root|host)(?![.:#(])[\s,]*[^{]*{\s*[^;]*;*\s*)|(?:var\(\s*))(--[^:)]+)(?:\s*[:)])/},w={dom:{},job:{},user:{}},C=!1,O=null,A=0,x=null,j=!1;function k(){var r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o="cssVars(): ",s=e({},b,r);function a(e,t,r,n){!s.silent&&window.console&&console.error("".concat(o).concat(e,"\n"),t),s.onError(e,t,r,n)}function c(e){!s.silent&&window.console&&console.warn("".concat(o).concat(e)),s.onWarning(e)}if(y){if(s.watch)return s.watch=b.watch,function(e){function t(e){return"LINK"===e.tagName&&-1!==(e.getAttribute("rel")||"").indexOf("stylesheet")&&!e.disabled}if(!window.MutationObserver)return;O&&(O.disconnect(),O=null);(O=new MutationObserver(function(r){r.some(function(r){var n,o=!1;return"attributes"===r.type?o=t(r.target):"childList"===r.type&&(n=r.addedNodes,o=Array.apply(null,n).some(function(e){var r=1===e.nodeType&&e.hasAttribute("data-cssvars"),n=function(e){return"STYLE"===e.tagName&&!e.disabled}(e)&&E.cssVars.test(e.textContent);return!r&&(t(e)||n)})||function(t){return Array.apply(null,t).some(function(t){var r=1===t.nodeType,n=r&&"out"===t.getAttribute("data-cssvars"),o=r&&"src"===t.getAttribute("data-cssvars"),s=o;if(o||n){var a=t.getAttribute("data-cssvars-group"),c=e.rootElement.querySelector('[data-cssvars-group="'.concat(a,'"]'));o&&(L(e.rootElement),w.dom={}),c&&c.parentNode.removeChild(c)}return s})}(r.removedNodes)),o})&&k(e)})).observe(document.documentElement,{attributes:!0,attributeFilter:["disabled","href"],childList:!0,subtree:!0})}(s),void k(s);if(!1===s.watch&&O&&(O.disconnect(),O=null),!s.__benchmark){if(C===s.rootElement)return void function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100;clearTimeout(x),x=setTimeout(function(){e.__benchmark=null,k(e)},t)}(r);if(s.__benchmark=T(),s.exclude=[O?'[data-cssvars]:not([data-cssvars=""])':'[data-cssvars="out"]',s.exclude].filter(function(e){return e}).join(","),s.variables=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=/^-{2}/;return Object.keys(e).reduce(function(r,n){return r[t.test(n)?n:"--".concat(n.replace(/^-+/,""))]=e[n],r},{})}(s.variables),!O)if(Array.apply(null,s.rootElement.querySelectorAll('[data-cssvars="out"]')).forEach(function(e){var t=e.getAttribute("data-cssvars-group");(t?s.rootElement.querySelector('[data-cssvars="src"][data-cssvars-group="'.concat(t,'"]')):null)||e.parentNode.removeChild(e)}),A){var i=s.rootElement.querySelectorAll('[data-cssvars]:not([data-cssvars="out"])');i.length2&&void 0!==arguments[2]?arguments[2]:[],i={},d=s.updateDOM?w.dom:Object.keys(w.job).length?w.job:w.job=JSON.parse(JSON.stringify(w.dom)),p=!1;if(o.forEach(function(e,t){if(E.cssVars.test(n[t]))try{var r=u(n[t],{preserveStatic:s.preserveStatic,removeComments:!0});l(r,{parseHost:Boolean(s.rootElement.host),store:i,onWarning:c}),e.__cssVars={tree:r}}catch(t){a(t.message,e)}}),s.updateDOM&&e(w.user,s.variables),e(i,s.variables),p=Boolean((document.querySelector("[data-cssvars]")||Object.keys(w.dom).length)&&Object.keys(i).some(function(e){return i[e]!==d[e]})),e(d,w.user,i),p)L(s.rootElement),k(s);else{var v=[],h=[],y=!1;if(w.job={},s.updateDOM&&S.job++,o.forEach(function(t){var r=!t.__cssVars;if(t.__cssVars)try{m(t.__cssVars.tree,e({},s,{variables:d,onWarning:c}));var n=f(t.__cssVars.tree);if(s.updateDOM){if(t.getAttribute("data-cssvars")||t.setAttribute("data-cssvars","src"),n.length){var o=t.getAttribute("data-cssvars-group")||++S.group,i=n.replace(/\s/g,""),u=s.rootElement.querySelector('[data-cssvars="out"][data-cssvars-group="'.concat(o,'"]'))||document.createElement("style");y=y||E.cssKeyframes.test(n),u.hasAttribute("data-cssvars")||u.setAttribute("data-cssvars","out"),i===t.textContent.replace(/\s/g,"")?(r=!0,u&&u.parentNode&&(t.removeAttribute("data-cssvars-group"),u.parentNode.removeChild(u))):i!==u.textContent.replace(/\s/g,"")&&([t,u].forEach(function(e){e.setAttribute("data-cssvars-job",S.job),e.setAttribute("data-cssvars-group",o)}),u.textContent=n,v.push(n),h.push(u),u.parentNode||t.parentNode.insertBefore(u,t.nextSibling))}}else t.textContent.replace(/\s/g,"")!==n&&v.push(n)}catch(e){a(e.message,t)}r&&t.setAttribute("data-cssvars","skip"),t.hasAttribute("data-cssvars-job")||t.setAttribute("data-cssvars-job",S.job)}),A=s.rootElement.querySelectorAll('[data-cssvars]:not([data-cssvars="out"])').length,s.shadowDOM)for(var g,b=[s.rootElement].concat(t(s.rootElement.querySelectorAll("*"))),O=0;g=b[O];++O)if(g.shadowRoot&&g.shadowRoot.querySelector("style")){var x=e({},s,{rootElement:g.shadowRoot});k(x)}s.updateDOM&&y&&M(s.rootElement),C=!1,s.onComplete(v.join(""),h,JSON.parse(JSON.stringify(d)),T()-s.__benchmark)}}}));else document.addEventListener("DOMContentLoaded",function e(t){k(r),document.removeEventListener("DOMContentLoaded",e)})}}function M(e){var t=["animation-name","-moz-animation-name","-webkit-animation-name"].filter(function(e){return getComputedStyle(document.body)[e]})[0];if(t){for(var r=e.getElementsByTagName("*"),n=[],o=0,s=r.length;o1&&void 0!==arguments[1]?arguments[1]:location.href,r=document.implementation.createHTMLDocument(""),n=r.createElement("base"),o=r.createElement("a");return r.head.appendChild(n),r.body.appendChild(o),n.href=t,o.href=e,o.href}function T(){return y&&(window.performance||{}).now?window.performance.now():(new Date).getTime()}function L(e){Array.apply(null,e.querySelectorAll('[data-cssvars="skip"],[data-cssvars="src"]')).forEach(function(e){return e.setAttribute("data-cssvars","")})}return k.reset=function(){for(var e in C=!1,O&&(O.disconnect(),O=null),A=0,x=null,j=!1,w)w[e]={}},k});
+!(function (e, t) {
+ "object" == typeof exports && "undefined" != typeof module
+ ? (module.exports = t())
+ : "function" == typeof define && define.amd
+ ? define(t)
+ : ((e = e || self).cssVars = t());
+})(this, function () {
+ "use strict";
+ function e() {
+ return (e =
+ Object.assign ||
+ function (e) {
+ for (var t = 1; t < arguments.length; t++) {
+ var r = arguments[t];
+ for (var n in r)
+ Object.prototype.hasOwnProperty.call(r, n) &&
+ (e[n] = r[n]);
+ }
+ return e;
+ }).apply(this, arguments);
+ }
+ function t(e) {
+ return (
+ (function (e) {
+ if (Array.isArray(e)) {
+ for (var t = 0, r = new Array(e.length); t < e.length; t++)
+ r[t] = e[t];
+ return r;
+ }
+ })(e) ||
+ (function (e) {
+ if (
+ Symbol.iterator in Object(e) ||
+ "[object Arguments]" === Object.prototype.toString.call(e)
+ )
+ return Array.from(e);
+ })(e) ||
+ (function () {
+ throw new TypeError(
+ "Invalid attempt to spread non-iterable instance",
+ );
+ })()
+ );
+ }
+ function r(e) {
+ var t =
+ arguments.length > 1 && void 0 !== arguments[1]
+ ? arguments[1]
+ : {},
+ r = {
+ mimeType: t.mimeType || null,
+ onBeforeSend: t.onBeforeSend || Function.prototype,
+ onSuccess: t.onSuccess || Function.prototype,
+ onError: t.onError || Function.prototype,
+ onComplete: t.onComplete || Function.prototype,
+ },
+ n = Array.isArray(e) ? e : [e],
+ o = Array.apply(null, Array(n.length)).map(function (e) {
+ return null;
+ });
+ function s() {
+ return !(
+ "<" ===
+ (arguments.length > 0 && void 0 !== arguments[0]
+ ? arguments[0]
+ : ""
+ )
+ .trim()
+ .charAt(0)
+ );
+ }
+ function a(e, t) {
+ r.onError(e, n[t], t);
+ }
+ function c(e, t) {
+ var s = r.onSuccess(e, n[t], t);
+ (e = !1 === s ? "" : s || e),
+ (o[t] = e),
+ -1 === o.indexOf(null) && r.onComplete(o);
+ }
+ var i = document.createElement("a");
+ n.forEach(function (e, t) {
+ if (
+ (i.setAttribute("href", e),
+ (i.href = String(i.href)),
+ Boolean(document.all && !window.atob) &&
+ i.host.split(":")[0] !== location.host.split(":")[0])
+ ) {
+ if (i.protocol === location.protocol) {
+ var n = new XDomainRequest();
+ n.open("GET", e),
+ (n.timeout = 0),
+ (n.onprogress = Function.prototype),
+ (n.ontimeout = Function.prototype),
+ (n.onload = function () {
+ s(n.responseText) ? c(n.responseText, t) : a(n, t);
+ }),
+ (n.onerror = function (e) {
+ a(n, t);
+ }),
+ setTimeout(function () {
+ n.send();
+ }, 0);
+ } else
+ console.warn(
+ "Internet Explorer 9 Cross-Origin (CORS) requests must use the same protocol (".concat(
+ e,
+ ")",
+ ),
+ ),
+ a(null, t);
+ } else {
+ var o = new XMLHttpRequest();
+ o.open("GET", e),
+ r.mimeType &&
+ o.overrideMimeType &&
+ o.overrideMimeType(r.mimeType),
+ r.onBeforeSend(o, e, t),
+ (o.onreadystatechange = function () {
+ 4 === o.readyState &&
+ (200 === o.status && s(o.responseText)
+ ? c(o.responseText, t)
+ : a(o, t));
+ }),
+ o.send();
+ }
+ });
+ }
+ function n(e) {
+ var t = {
+ cssComments: /\/\*[\s\S]+?\*\//g,
+ cssImports:
+ /(?:@import\s*)(?:url\(\s*)?(?:['"])([^'"]*)(?:['"])(?:\s*\))?(?:[^;]*;)/g,
+ },
+ n = {
+ rootElement: e.rootElement || document,
+ include: e.include || 'style,link[rel="stylesheet"]',
+ exclude: e.exclude || null,
+ filter: e.filter || null,
+ useCSSOM: e.useCSSOM || !1,
+ onBeforeSend: e.onBeforeSend || Function.prototype,
+ onSuccess: e.onSuccess || Function.prototype,
+ onError: e.onError || Function.prototype,
+ onComplete: e.onComplete || Function.prototype,
+ },
+ s = Array.apply(
+ null,
+ n.rootElement.querySelectorAll(n.include),
+ ).filter(function (e) {
+ return (
+ (t = e),
+ (r = n.exclude),
+ !(
+ t.matches ||
+ t.matchesSelector ||
+ t.webkitMatchesSelector ||
+ t.mozMatchesSelector ||
+ t.msMatchesSelector ||
+ t.oMatchesSelector
+ ).call(t, r)
+ );
+ var t, r;
+ }),
+ a = Array.apply(null, Array(s.length)).map(function (e) {
+ return null;
+ });
+ function c() {
+ if (-1 === a.indexOf(null)) {
+ var e = a.join("");
+ n.onComplete(e, a, s);
+ }
+ }
+ function i(e, t, o, s) {
+ var i = n.onSuccess(e, o, s);
+ (function e(t, o, s, a) {
+ var c =
+ arguments.length > 4 && void 0 !== arguments[4]
+ ? arguments[4]
+ : [];
+ var i =
+ arguments.length > 5 && void 0 !== arguments[5]
+ ? arguments[5]
+ : [];
+ var l = u(t, s, i);
+ l.rules.length
+ ? r(l.absoluteUrls, {
+ onBeforeSend: function (e, t, r) {
+ n.onBeforeSend(e, o, t);
+ },
+ onSuccess: function (e, t, r) {
+ var s = n.onSuccess(e, o, t),
+ a = u((e = !1 === s ? "" : s || e), t, i);
+ return (
+ a.rules.forEach(function (t, r) {
+ e = e.replace(t, a.absoluteRules[r]);
+ }),
+ e
+ );
+ },
+ onError: function (r, n, u) {
+ c.push({ xhr: r, url: n }),
+ i.push(l.rules[u]),
+ e(t, o, s, a, c, i);
+ },
+ onComplete: function (r) {
+ r.forEach(function (e, r) {
+ t = t.replace(l.rules[r], e);
+ }),
+ e(t, o, s, a, c, i);
+ },
+ })
+ : a(t, c);
+ })(
+ (e = void 0 !== i && !1 === Boolean(i) ? "" : i || e),
+ o,
+ s,
+ function (e, r) {
+ null === a[t] &&
+ (r.forEach(function (e) {
+ return n.onError(e.xhr, o, e.url);
+ }),
+ !n.filter || n.filter.test(e)
+ ? (a[t] = e)
+ : (a[t] = ""),
+ c());
+ },
+ );
+ }
+ function u(e, r) {
+ var n =
+ arguments.length > 2 && void 0 !== arguments[2]
+ ? arguments[2]
+ : [],
+ s = {};
+ return (
+ (s.rules = (
+ e.replace(t.cssComments, "").match(t.cssImports) || []
+ ).filter(function (e) {
+ return -1 === n.indexOf(e);
+ })),
+ (s.urls = s.rules.map(function (e) {
+ return e.replace(t.cssImports, "$1");
+ })),
+ (s.absoluteUrls = s.urls.map(function (e) {
+ return o(e, r);
+ })),
+ (s.absoluteRules = s.rules.map(function (e, t) {
+ var n = s.urls[t],
+ a = o(s.absoluteUrls[t], r);
+ return e.replace(n, a);
+ })),
+ s
+ );
+ }
+ s.length
+ ? s.forEach(function (e, t) {
+ var s = e.getAttribute("href"),
+ u = e.getAttribute("rel"),
+ l =
+ "LINK" === e.nodeName &&
+ s &&
+ u &&
+ "stylesheet" === u.toLowerCase(),
+ f = "STYLE" === e.nodeName;
+ if (l)
+ r(s, {
+ mimeType: "text/css",
+ onBeforeSend: function (t, r, o) {
+ n.onBeforeSend(t, e, r);
+ },
+ onSuccess: function (r, n, a) {
+ var c = o(s, location.href);
+ i(r, t, e, c);
+ },
+ onError: function (r, o, s) {
+ (a[t] = ""), n.onError(r, e, o), c();
+ },
+ });
+ else if (f) {
+ var d = e.textContent;
+ n.useCSSOM &&
+ (d = Array.apply(null, e.sheet.cssRules)
+ .map(function (e) {
+ return e.cssText;
+ })
+ .join("")),
+ i(d, t, e, location.href);
+ } else (a[t] = ""), c();
+ })
+ : n.onComplete("", []);
+ }
+ function o(e) {
+ var t =
+ arguments.length > 1 && void 0 !== arguments[1]
+ ? arguments[1]
+ : location.href,
+ r = document.implementation.createHTMLDocument(""),
+ n = r.createElement("base"),
+ o = r.createElement("a");
+ return (
+ r.head.appendChild(n),
+ r.body.appendChild(o),
+ (n.href = t),
+ (o.href = e),
+ o.href
+ );
+ }
+ var s = a;
+ function a(e, t, r) {
+ e instanceof RegExp && (e = c(e, r)),
+ t instanceof RegExp && (t = c(t, r));
+ var n = i(e, t, r);
+ return (
+ n && {
+ start: n[0],
+ end: n[1],
+ pre: r.slice(0, n[0]),
+ body: r.slice(n[0] + e.length, n[1]),
+ post: r.slice(n[1] + t.length),
+ }
+ );
+ }
+ function c(e, t) {
+ var r = t.match(e);
+ return r ? r[0] : null;
+ }
+ function i(e, t, r) {
+ var n,
+ o,
+ s,
+ a,
+ c,
+ i = r.indexOf(e),
+ u = r.indexOf(t, i + 1),
+ l = i;
+ if (i >= 0 && u > 0) {
+ for (n = [], s = r.length; l >= 0 && !c; )
+ l == i
+ ? (n.push(l), (i = r.indexOf(e, l + 1)))
+ : 1 == n.length
+ ? (c = [n.pop(), u])
+ : ((o = n.pop()) < s && ((s = o), (a = u)),
+ (u = r.indexOf(t, l + 1))),
+ (l = i < u && i >= 0 ? i : u);
+ n.length && (c = [s, a]);
+ }
+ return c;
+ }
+ function u(t) {
+ var r = e(
+ {},
+ { preserveStatic: !0, removeComments: !1 },
+ arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {},
+ );
+ function n(e) {
+ throw new Error("CSS parse error: ".concat(e));
+ }
+ function o(e) {
+ var r = e.exec(t);
+ if (r) return (t = t.slice(r[0].length)), r;
+ }
+ function a() {
+ return o(/^{\s*/);
+ }
+ function c() {
+ return o(/^}/);
+ }
+ function i() {
+ o(/^\s*/);
+ }
+ function u() {
+ if ((i(), "/" === t[0] && "*" === t[1])) {
+ for (var e = 2; t[e] && ("*" !== t[e] || "/" !== t[e + 1]); )
+ e++;
+ if (!t[e]) return n("end of comment is missing");
+ var r = t.slice(2, e);
+ return (t = t.slice(e + 2)), { type: "comment", comment: r };
+ }
+ }
+ function l() {
+ for (var e, t = []; (e = u()); ) t.push(e);
+ return r.removeComments ? [] : t;
+ }
+ function f() {
+ for (i(); "}" === t[0]; ) n("extra closing bracket");
+ var e = o(/^(("(?:\\"|[^"])*"|'(?:\\'|[^'])*'|[^{])+)/);
+ if (e)
+ return e[0]
+ .trim()
+ .replace(
+ /\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*\/+/g,
+ "",
+ )
+ .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, function (e) {
+ return e.replace(/,/g, "â");
+ })
+ .split(/\s*(?![^(]*\)),\s*/)
+ .map(function (e) {
+ return e.replace(/\u200C/g, ",");
+ });
+ }
+ function d() {
+ o(/^([;\s]*)+/);
+ var e = /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//g,
+ t = o(/^(\*?[-#\/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/);
+ if (t) {
+ if (((t = t[0].trim()), !o(/^:\s*/)))
+ return n("property missing ':'");
+ var r = o(
+ /^((?:\/\*.*?\*\/|'(?:\\'|.)*?'|"(?:\\"|.)*?"|\((\s*'(?:\\'|.)*?'|"(?:\\"|.)*?"|[^)]*?)\s*\)|[^};])+)/,
+ ),
+ s = {
+ type: "declaration",
+ property: t.replace(e, ""),
+ value: r ? r[0].replace(e, "").trim() : "",
+ };
+ return o(/^[;\s]*/), s;
+ }
+ }
+ function p() {
+ if (!a()) return n("missing '{'");
+ for (var e, t = l(); (e = d()); ) t.push(e), (t = t.concat(l()));
+ return c() ? t : n("missing '}'");
+ }
+ function m() {
+ i();
+ for (
+ var e, t = [];
+ (e = o(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/));
+ )
+ t.push(e[1]), o(/^,\s*/);
+ if (t.length)
+ return { type: "keyframe", values: t, declarations: p() };
+ }
+ function v() {
+ if ((i(), "@" === t[0])) {
+ var e =
+ (function () {
+ var e = o(/^@([-\w]+)?keyframes\s*/);
+ if (e) {
+ var t = e[1];
+ if (!(e = o(/^([-\w]+)\s*/)))
+ return n("@keyframes missing name");
+ var r,
+ s = e[1];
+ if (!a()) return n("@keyframes missing '{'");
+ for (var i = l(); (r = m()); )
+ i.push(r), (i = i.concat(l()));
+ return c()
+ ? {
+ type: "keyframes",
+ name: s,
+ vendor: t,
+ keyframes: i,
+ }
+ : n("@keyframes missing '}'");
+ }
+ })() ||
+ (function () {
+ var e = o(/^@supports *([^{]+)/);
+ if (e)
+ return {
+ type: "supports",
+ supports: e[1].trim(),
+ rules: y(),
+ };
+ })() ||
+ (function () {
+ if (o(/^@host\s*/)) return { type: "host", rules: y() };
+ })() ||
+ (function () {
+ var e = o(/^@media([^{]+)*/);
+ if (e)
+ return {
+ type: "media",
+ media: (e[1] || "").trim(),
+ rules: y(),
+ };
+ })() ||
+ (function () {
+ var e = o(/^@custom-media\s+(--[^\s]+)\s*([^{;]+);/);
+ if (e)
+ return {
+ type: "custom-media",
+ name: e[1].trim(),
+ media: e[2].trim(),
+ };
+ })() ||
+ (function () {
+ if (o(/^@page */))
+ return {
+ type: "page",
+ selectors: f() || [],
+ declarations: p(),
+ };
+ })() ||
+ (function () {
+ var e = o(/^@([-\w]+)?document *([^{]+)/);
+ if (e)
+ return {
+ type: "document",
+ document: e[2].trim(),
+ vendor: e[1] ? e[1].trim() : null,
+ rules: y(),
+ };
+ })() ||
+ (function () {
+ if (o(/^@font-face\s*/))
+ return { type: "font-face", declarations: p() };
+ })() ||
+ (function () {
+ var e = o(/^@(import|charset|namespace)\s*([^;]+);/);
+ if (e) return { type: e[1], name: e[2].trim() };
+ })();
+ if (e && !r.preserveStatic) {
+ var s = !1;
+ if (e.declarations)
+ s = e.declarations.some(function (e) {
+ return /var\(/.test(e.value);
+ });
+ else
+ s = (e.keyframes || e.rules || []).some(function (e) {
+ return (e.declarations || []).some(function (e) {
+ return /var\(/.test(e.value);
+ });
+ });
+ return s ? e : {};
+ }
+ return e;
+ }
+ }
+ function h() {
+ if (!r.preserveStatic) {
+ var e = s("{", "}", t);
+ if (e) {
+ var o =
+ /:(?:root|host)(?![.:#(])/.test(e.pre) &&
+ /--\S*\s*:/.test(e.body),
+ a = /var\(/.test(e.body);
+ if (!o && !a) return (t = t.slice(e.end + 1)), {};
+ }
+ }
+ var c = f() || [],
+ i = r.preserveStatic
+ ? p()
+ : p().filter(function (e) {
+ var t =
+ c.some(function (e) {
+ return /:(?:root|host)(?![.:#(])/.test(
+ e,
+ );
+ }) && /^--\S/.test(e.property),
+ r = /var\(/.test(e.value);
+ return t || r;
+ });
+ return (
+ c.length || n("selector missing"),
+ { type: "rule", selectors: c, declarations: i }
+ );
+ }
+ function y(e) {
+ if (!e && !a()) return n("missing '{'");
+ for (
+ var r, o = l();
+ t.length && (e || "}" !== t[0]) && (r = v() || h());
+
+ )
+ r.type && o.push(r), (o = o.concat(l()));
+ return e || c() ? o : n("missing '}'");
+ }
+ return { type: "stylesheet", stylesheet: { rules: y(!0), errors: [] } };
+ }
+ function l(t) {
+ var r = e(
+ {},
+ { parseHost: !1, store: {}, onWarning: function () {} },
+ arguments.length > 1 && void 0 !== arguments[1]
+ ? arguments[1]
+ : {},
+ ),
+ n = new RegExp(
+ ":".concat(r.parseHost ? "host" : "root", "(?![.:#(])"),
+ );
+ return (
+ "string" == typeof t && (t = u(t, r)),
+ t.stylesheet.rules.forEach(function (e) {
+ "rule" === e.type &&
+ e.selectors.some(function (e) {
+ return n.test(e);
+ }) &&
+ e.declarations.forEach(function (e, t) {
+ var n = e.property,
+ o = e.value;
+ n && 0 === n.indexOf("--") && (r.store[n] = o);
+ });
+ }),
+ r.store
+ );
+ }
+ function f(e) {
+ var t =
+ arguments.length > 1 && void 0 !== arguments[1]
+ ? arguments[1]
+ : "",
+ r = arguments.length > 2 ? arguments[2] : void 0,
+ n = {
+ charset: function (e) {
+ return "@charset " + e.name + ";";
+ },
+ comment: function (e) {
+ return 0 === e.comment.indexOf("__CSSVARSPONYFILL")
+ ? "/*" + e.comment + "*/"
+ : "";
+ },
+ "custom-media": function (e) {
+ return "@custom-media " + e.name + " " + e.media + ";";
+ },
+ declaration: function (e) {
+ return e.property + ":" + e.value + ";";
+ },
+ document: function (e) {
+ return (
+ "@" +
+ (e.vendor || "") +
+ "document " +
+ e.document +
+ "{" +
+ o(e.rules) +
+ "}"
+ );
+ },
+ "font-face": function (e) {
+ return "@font-face{" + o(e.declarations) + "}";
+ },
+ host: function (e) {
+ return "@host{" + o(e.rules) + "}";
+ },
+ import: function (e) {
+ return "@import " + e.name + ";";
+ },
+ keyframe: function (e) {
+ return e.values.join(",") + "{" + o(e.declarations) + "}";
+ },
+ keyframes: function (e) {
+ return (
+ "@" +
+ (e.vendor || "") +
+ "keyframes " +
+ e.name +
+ "{" +
+ o(e.keyframes) +
+ "}"
+ );
+ },
+ media: function (e) {
+ return "@media " + e.media + "{" + o(e.rules) + "}";
+ },
+ namespace: function (e) {
+ return "@namespace " + e.name + ";";
+ },
+ page: function (e) {
+ return (
+ "@page " +
+ (e.selectors.length ? e.selectors.join(", ") : "") +
+ "{" +
+ o(e.declarations) +
+ "}"
+ );
+ },
+ rule: function (e) {
+ var t = e.declarations;
+ if (t.length)
+ return e.selectors.join(",") + "{" + o(t) + "}";
+ },
+ supports: function (e) {
+ return "@supports " + e.supports + "{" + o(e.rules) + "}";
+ },
+ };
+ function o(e) {
+ for (var o = "", s = 0; s < e.length; s++) {
+ var a = e[s];
+ r && r(a);
+ var c = n[a.type](a);
+ c && ((o += c), c.length && a.selectors && (o += t));
+ }
+ return o;
+ }
+ return o(e.stylesheet.rules);
+ }
+ a.range = i;
+ var d = "--",
+ p = "var";
+ function m(t) {
+ var r = e(
+ {},
+ {
+ preserveStatic: !0,
+ preserveVars: !1,
+ variables: {},
+ onWarning: function () {},
+ },
+ arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {},
+ );
+ return (
+ "string" == typeof t && (t = u(t, r)),
+ (function e(t, r) {
+ t.rules.forEach(function (n) {
+ n.rules
+ ? e(n, r)
+ : n.keyframes
+ ? n.keyframes.forEach(function (e) {
+ "keyframe" === e.type &&
+ r(e.declarations, n);
+ })
+ : n.declarations && r(n.declarations, t);
+ });
+ })(t.stylesheet, function (e, t) {
+ for (var n = 0; n < e.length; n++) {
+ var o = e[n],
+ s = o.type,
+ a = o.property,
+ c = o.value;
+ if ("declaration" === s)
+ if (r.preserveVars || !a || 0 !== a.indexOf(d)) {
+ if (-1 !== c.indexOf(p + "(")) {
+ var i = h(c, r);
+ i !== o.value &&
+ ((i = v(i)),
+ r.preserveVars
+ ? (e.splice(n, 0, {
+ type: s,
+ property: a,
+ value: i,
+ }),
+ n++)
+ : (o.value = i));
+ }
+ } else e.splice(n, 1), n--;
+ }
+ }),
+ f(t)
+ );
+ }
+ function v(e) {
+ return (
+ (e.match(/calc\(([^)]+)\)/g) || []).forEach(function (t) {
+ var r = "calc".concat(t.split("calc").join(""));
+ e = e.replace(t, r);
+ }),
+ e
+ );
+ }
+ function h(e) {
+ var t =
+ arguments.length > 1 && void 0 !== arguments[1]
+ ? arguments[1]
+ : {},
+ r = arguments.length > 2 ? arguments[2] : void 0;
+ if (-1 === e.indexOf("var(")) return e;
+ var n = s("(", ")", e);
+ return n
+ ? "var" === n.pre.slice(-3)
+ ? 0 === n.body.trim().length
+ ? (t.onWarning(
+ "var() must contain a non-whitespace string",
+ ),
+ e)
+ : n.pre.slice(0, -3) +
+ (function (e) {
+ var n = e.split(",")[0].replace(/[\s\n\t]/g, ""),
+ o = (e.match(/(?:\s*,\s*){1}(.*)?/) || [])[1],
+ s = Object.prototype.hasOwnProperty.call(
+ t.variables,
+ n,
+ )
+ ? String(t.variables[n])
+ : void 0,
+ a = s || (o ? String(o) : void 0),
+ c = r || e;
+ return (
+ s ||
+ t.onWarning(
+ 'variable "'.concat(
+ n,
+ '" is undefined',
+ ),
+ ),
+ a && "undefined" !== a && a.length > 0
+ ? h(a, t, c)
+ : "var(".concat(c, ")")
+ );
+ })(n.body) +
+ h(n.post, t)
+ : n.pre + "(".concat(h(n.body, t), ")") + h(n.post, t)
+ : (-1 !== e.indexOf("var(") &&
+ t.onWarning(
+ 'missing closing ")" in the value "'.concat(e, '"'),
+ ),
+ e);
+ }
+ var y = "undefined" != typeof window,
+ g =
+ y &&
+ window.CSS &&
+ window.CSS.supports &&
+ window.CSS.supports("(--a: 0)"),
+ S = { group: 0, job: 0 },
+ b = {
+ rootElement: y ? document : null,
+ shadowDOM: !1,
+ include: "style,link[rel=stylesheet]",
+ exclude: "",
+ variables: {},
+ onlyLegacy: !0,
+ preserveStatic: !0,
+ preserveVars: !1,
+ silent: !1,
+ updateDOM: !0,
+ updateURLs: !0,
+ watch: null,
+ onBeforeSend: function () {},
+ onWarning: function () {},
+ onError: function () {},
+ onSuccess: function () {},
+ onComplete: function () {},
+ },
+ E = {
+ cssComments: /\/\*[\s\S]+?\*\//g,
+ cssKeyframes: /@(?:-\w*-)?keyframes/,
+ cssMediaQueries: /@media[^{]+\{([\s\S]+?})\s*}/g,
+ cssUrls: /url\((?!['"]?(?:data|http|\/\/):)['"]?([^'")]*)['"]?\)/g,
+ cssVarDeclRules:
+ /(?::(?:root|host)(?![.:#(])[\s,]*[^{]*{\s*[^}]*})/g,
+ cssVarDecls: /(?:[\s;]*)(-{2}\w[\w-]*)(?:\s*:\s*)([^;]*);/g,
+ cssVarFunc: /var\(\s*--[\w-]/,
+ cssVars:
+ /(?:(?::(?:root|host)(?![.:#(])[\s,]*[^{]*{\s*[^;]*;*\s*)|(?:var\(\s*))(--[^:)]+)(?:\s*[:)])/,
+ },
+ w = { dom: {}, job: {}, user: {} },
+ C = !1,
+ O = null,
+ A = 0,
+ x = null,
+ j = !1;
+ function k() {
+ var r =
+ arguments.length > 0 && void 0 !== arguments[0]
+ ? arguments[0]
+ : {},
+ o = "cssVars(): ",
+ s = e({}, b, r);
+ function a(e, t, r, n) {
+ !s.silent &&
+ window.console &&
+ console.error("".concat(o).concat(e, "\n"), t),
+ s.onError(e, t, r, n);
+ }
+ function c(e) {
+ !s.silent && window.console && console.warn("".concat(o).concat(e)),
+ s.onWarning(e);
+ }
+ if (y) {
+ if (s.watch)
+ return (
+ (s.watch = b.watch),
+ (function (e) {
+ function t(e) {
+ return (
+ "LINK" === e.tagName &&
+ -1 !==
+ (e.getAttribute("rel") || "").indexOf(
+ "stylesheet",
+ ) &&
+ !e.disabled
+ );
+ }
+ if (!window.MutationObserver) return;
+ O && (O.disconnect(), (O = null));
+ (O = new MutationObserver(function (r) {
+ r.some(function (r) {
+ var n,
+ o = !1;
+ return (
+ "attributes" === r.type
+ ? (o = t(r.target))
+ : "childList" === r.type &&
+ ((n = r.addedNodes),
+ (o =
+ Array.apply(null, n).some(
+ function (e) {
+ var r =
+ 1 ===
+ e.nodeType &&
+ e.hasAttribute(
+ "data-cssvars",
+ ),
+ n =
+ (function (e) {
+ return (
+ "STYLE" ===
+ e.tagName &&
+ !e.disabled
+ );
+ })(e) &&
+ E.cssVars.test(
+ e.textContent,
+ );
+ return (
+ !r && (t(e) || n)
+ );
+ },
+ ) ||
+ (function (t) {
+ return Array.apply(
+ null,
+ t,
+ ).some(function (t) {
+ var r =
+ 1 ===
+ t.nodeType,
+ n =
+ r &&
+ "out" ===
+ t.getAttribute(
+ "data-cssvars",
+ ),
+ o =
+ r &&
+ "src" ===
+ t.getAttribute(
+ "data-cssvars",
+ ),
+ s = o;
+ if (o || n) {
+ var a =
+ t.getAttribute(
+ "data-cssvars-group",
+ ),
+ c =
+ e.rootElement.querySelector(
+ '[data-cssvars-group="'.concat(
+ a,
+ '"]',
+ ),
+ );
+ o &&
+ (L(
+ e.rootElement,
+ ),
+ (w.dom = {})),
+ c &&
+ c.parentNode.removeChild(
+ c,
+ );
+ }
+ return s;
+ });
+ })(r.removedNodes))),
+ o
+ );
+ }) && k(e);
+ })).observe(document.documentElement, {
+ attributes: !0,
+ attributeFilter: ["disabled", "href"],
+ childList: !0,
+ subtree: !0,
+ });
+ })(s),
+ void k(s)
+ );
+ if (
+ (!1 === s.watch && O && (O.disconnect(), (O = null)),
+ !s.__benchmark)
+ ) {
+ if (C === s.rootElement)
+ return void (function (e) {
+ var t =
+ arguments.length > 1 && void 0 !== arguments[1]
+ ? arguments[1]
+ : 100;
+ clearTimeout(x),
+ (x = setTimeout(function () {
+ (e.__benchmark = null), k(e);
+ }, t));
+ })(r);
+ if (
+ ((s.__benchmark = T()),
+ (s.exclude = [
+ O
+ ? '[data-cssvars]:not([data-cssvars=""])'
+ : '[data-cssvars="out"]',
+ s.exclude,
+ ]
+ .filter(function (e) {
+ return e;
+ })
+ .join(",")),
+ (s.variables = (function () {
+ var e =
+ arguments.length > 0 && void 0 !== arguments[0]
+ ? arguments[0]
+ : {},
+ t = /^-{2}/;
+ return Object.keys(e).reduce(function (r, n) {
+ return (
+ (r[
+ t.test(n)
+ ? n
+ : "--".concat(n.replace(/^-+/, ""))
+ ] = e[n]),
+ r
+ );
+ }, {});
+ })(s.variables)),
+ !O)
+ )
+ if (
+ (Array.apply(
+ null,
+ s.rootElement.querySelectorAll(
+ '[data-cssvars="out"]',
+ ),
+ ).forEach(function (e) {
+ var t = e.getAttribute("data-cssvars-group");
+ (t
+ ? s.rootElement.querySelector(
+ '[data-cssvars="src"][data-cssvars-group="'.concat(
+ t,
+ '"]',
+ ),
+ )
+ : null) || e.parentNode.removeChild(e);
+ }),
+ A)
+ ) {
+ var i = s.rootElement.querySelectorAll(
+ '[data-cssvars]:not([data-cssvars="out"])',
+ );
+ i.length < A && ((A = i.length), (w.dom = {}));
+ }
+ }
+ if ("loading" !== document.readyState)
+ if (g && s.onlyLegacy) {
+ if (s.updateDOM) {
+ var d =
+ s.rootElement.host ||
+ (s.rootElement === document
+ ? document.documentElement
+ : s.rootElement);
+ Object.keys(s.variables).forEach(function (e) {
+ d.style.setProperty(e, s.variables[e]);
+ });
+ }
+ } else
+ !j &&
+ (s.shadowDOM ||
+ s.rootElement.shadowRoot ||
+ s.rootElement.host)
+ ? n({
+ rootElement: b.rootElement,
+ include: b.include,
+ exclude: s.exclude,
+ onSuccess: function (e, t, r) {
+ return (
+ (e = (
+ (e = e
+ .replace(E.cssComments, "")
+ .replace(
+ E.cssMediaQueries,
+ "",
+ )).match(E.cssVarDeclRules) ||
+ []
+ ).join("")) || !1
+ );
+ },
+ onComplete: function (e, t, r) {
+ l(e, { store: w.dom, onWarning: c }),
+ (j = !0),
+ k(s);
+ },
+ })
+ : ((C = s.rootElement),
+ n({
+ rootElement: s.rootElement,
+ include: s.include,
+ exclude: s.exclude,
+ onBeforeSend: s.onBeforeSend,
+ onError: function (e, t, r) {
+ var n =
+ e.responseURL ||
+ _(r, location.href),
+ o = e.statusText
+ ? "(".concat(e.statusText, ")")
+ : "Unspecified Error" +
+ (0 === e.status
+ ? " (possibly CORS related)"
+ : "");
+ a(
+ "CSS XHR Error: "
+ .concat(n, " ")
+ .concat(e.status, " ")
+ .concat(o),
+ t,
+ e,
+ n,
+ );
+ },
+ onSuccess: function (e, t, r) {
+ var n = s.onSuccess(e, t, r);
+ return (
+ (e =
+ void 0 !== n && !1 === Boolean(n)
+ ? ""
+ : n || e),
+ s.updateURLs &&
+ (e = (function (e, t) {
+ return (
+ (
+ e
+ .replace(
+ E.cssComments,
+ "",
+ )
+ .match(E.cssUrls) ||
+ []
+ ).forEach(function (r) {
+ var n = r.replace(
+ E.cssUrls,
+ "$1",
+ ),
+ o = _(n, t);
+ e = e.replace(
+ r,
+ r.replace(n, o),
+ );
+ }),
+ e
+ );
+ })(e, r)),
+ e
+ );
+ },
+ onComplete: function (r, n) {
+ var o =
+ arguments.length > 2 &&
+ void 0 !== arguments[2]
+ ? arguments[2]
+ : [],
+ i = {},
+ d = s.updateDOM
+ ? w.dom
+ : Object.keys(w.job).length
+ ? w.job
+ : (w.job = JSON.parse(
+ JSON.stringify(w.dom),
+ )),
+ p = !1;
+ if (
+ (o.forEach(function (e, t) {
+ if (E.cssVars.test(n[t]))
+ try {
+ var r = u(n[t], {
+ preserveStatic:
+ s.preserveStatic,
+ removeComments: !0,
+ });
+ l(r, {
+ parseHost: Boolean(
+ s.rootElement.host,
+ ),
+ store: i,
+ onWarning: c,
+ }),
+ (e.__cssVars = {
+ tree: r,
+ });
+ } catch (t) {
+ a(t.message, e);
+ }
+ }),
+ s.updateDOM && e(w.user, s.variables),
+ e(i, s.variables),
+ (p = Boolean(
+ (document.querySelector(
+ "[data-cssvars]",
+ ) ||
+ Object.keys(w.dom).length) &&
+ Object.keys(i).some(
+ function (e) {
+ return i[e] !== d[e];
+ },
+ ),
+ )),
+ e(d, w.user, i),
+ p)
+ )
+ L(s.rootElement), k(s);
+ else {
+ var v = [],
+ h = [],
+ y = !1;
+ if (
+ ((w.job = {}),
+ s.updateDOM && S.job++,
+ o.forEach(function (t) {
+ var r = !t.__cssVars;
+ if (t.__cssVars)
+ try {
+ m(
+ t.__cssVars.tree,
+ e({}, s, {
+ variables: d,
+ onWarning: c,
+ }),
+ );
+ var n = f(
+ t.__cssVars.tree,
+ );
+ if (s.updateDOM) {
+ if (
+ (t.getAttribute(
+ "data-cssvars",
+ ) ||
+ t.setAttribute(
+ "data-cssvars",
+ "src",
+ ),
+ n.length)
+ ) {
+ var o =
+ t.getAttribute(
+ "data-cssvars-group",
+ ) ||
+ ++S.group,
+ i =
+ n.replace(
+ /\s/g,
+ "",
+ ),
+ u =
+ s.rootElement.querySelector(
+ '[data-cssvars="out"][data-cssvars-group="'.concat(
+ o,
+ '"]',
+ ),
+ ) ||
+ document.createElement(
+ "style",
+ );
+ (y =
+ y ||
+ E.cssKeyframes.test(
+ n,
+ )),
+ u.hasAttribute(
+ "data-cssvars",
+ ) ||
+ u.setAttribute(
+ "data-cssvars",
+ "out",
+ ),
+ i ===
+ t.textContent.replace(
+ /\s/g,
+ "",
+ )
+ ? ((r =
+ !0),
+ u &&
+ u.parentNode &&
+ (t.removeAttribute(
+ "data-cssvars-group",
+ ),
+ u.parentNode.removeChild(
+ u,
+ )))
+ : i !==
+ u.textContent.replace(
+ /\s/g,
+ "",
+ ) &&
+ ([
+ t,
+ u,
+ ].forEach(
+ function (
+ e,
+ ) {
+ e.setAttribute(
+ "data-cssvars-job",
+ S.job,
+ ),
+ e.setAttribute(
+ "data-cssvars-group",
+ o,
+ );
+ },
+ ),
+ (u.textContent =
+ n),
+ v.push(
+ n,
+ ),
+ h.push(
+ u,
+ ),
+ u.parentNode ||
+ t.parentNode.insertBefore(
+ u,
+ t.nextSibling,
+ ));
+ }
+ } else
+ t.textContent.replace(
+ /\s/g,
+ "",
+ ) !== n &&
+ v.push(n);
+ } catch (e) {
+ a(e.message, t);
+ }
+ r &&
+ t.setAttribute(
+ "data-cssvars",
+ "skip",
+ ),
+ t.hasAttribute(
+ "data-cssvars-job",
+ ) ||
+ t.setAttribute(
+ "data-cssvars-job",
+ S.job,
+ );
+ }),
+ (A = s.rootElement.querySelectorAll(
+ '[data-cssvars]:not([data-cssvars="out"])',
+ ).length),
+ s.shadowDOM)
+ )
+ for (
+ var g,
+ b = [s.rootElement].concat(
+ t(
+ s.rootElement.querySelectorAll(
+ "*",
+ ),
+ ),
+ ),
+ O = 0;
+ (g = b[O]);
+ ++O
+ )
+ if (
+ g.shadowRoot &&
+ g.shadowRoot.querySelector(
+ "style",
+ )
+ ) {
+ var x = e({}, s, {
+ rootElement:
+ g.shadowRoot,
+ });
+ k(x);
+ }
+ s.updateDOM && y && M(s.rootElement),
+ (C = !1),
+ s.onComplete(
+ v.join(""),
+ h,
+ JSON.parse(JSON.stringify(d)),
+ T() - s.__benchmark,
+ );
+ }
+ },
+ }));
+ else
+ document.addEventListener("DOMContentLoaded", function e(t) {
+ k(r), document.removeEventListener("DOMContentLoaded", e);
+ });
+ }
+ }
+ function M(e) {
+ var t = [
+ "animation-name",
+ "-moz-animation-name",
+ "-webkit-animation-name",
+ ].filter(function (e) {
+ return getComputedStyle(document.body)[e];
+ })[0];
+ if (t) {
+ for (
+ var r = e.getElementsByTagName("*"),
+ n = [],
+ o = 0,
+ s = r.length;
+ o < s;
+ o++
+ ) {
+ var a = r[o];
+ "none" !== getComputedStyle(a)[t] &&
+ ((a.style[t] += "__CSSVARSPONYFILL-KEYFRAMES__"),
+ n.push(a));
+ }
+ document.body.offsetHeight;
+ for (var c = 0, i = n.length; c < i; c++) {
+ var u = n[c].style;
+ u[t] = u[t].replace("__CSSVARSPONYFILL-KEYFRAMES__", "");
+ }
+ }
+ }
+ function _(e) {
+ var t =
+ arguments.length > 1 && void 0 !== arguments[1]
+ ? arguments[1]
+ : location.href,
+ r = document.implementation.createHTMLDocument(""),
+ n = r.createElement("base"),
+ o = r.createElement("a");
+ return (
+ r.head.appendChild(n),
+ r.body.appendChild(o),
+ (n.href = t),
+ (o.href = e),
+ o.href
+ );
+ }
+ function T() {
+ return y && (window.performance || {}).now
+ ? window.performance.now()
+ : new Date().getTime();
+ }
+ function L(e) {
+ Array.apply(
+ null,
+ e.querySelectorAll('[data-cssvars="skip"],[data-cssvars="src"]'),
+ ).forEach(function (e) {
+ return e.setAttribute("data-cssvars", "");
+ });
+ }
+ return (
+ (k.reset = function () {
+ for (var e in ((C = !1),
+ O && (O.disconnect(), (O = null)),
+ (A = 0),
+ (x = null),
+ (j = !1),
+ w))
+ w[e] = {};
+ }),
+ k
+ );
+});
// Default values
cssVars({
- // Targets
- rootElement: document,
- shadowDOM: false,
+ // Targets
+ rootElement: document,
+ shadowDOM: false,
- // Sources
- include: 'link[rel=stylesheet],style',
- exclude: '',
- variables: {},
+ // Sources
+ include: "link[rel=stylesheet],style",
+ exclude: "",
+ variables: {},
- // Options
- onlyLegacy: true,
- preserveStatic: true,
- preserveVars: false,
- silent: false,
- updateDOM: true,
- updateURLs: true,
- watch: false,
+ // Options
+ onlyLegacy: true,
+ preserveStatic: true,
+ preserveVars: false,
+ silent: false,
+ updateDOM: true,
+ updateURLs: true,
+ watch: false,
- // Callbacks
- onBeforeSend(xhr, elm, url) {
- // ...
- },
- onWarning(message) {
- // ...
- },
- onError(message, elm, xhr, url) {
- // ...
- },
- onSuccess(cssText, elm, url) {
- // ...
- },
- onComplete(cssText, styleElms, cssVariables, benchmark) {
- // ...
- }
+ // Callbacks
+ onBeforeSend(xhr, elm, url) {
+ // ...
+ },
+ onWarning(message) {
+ // ...
+ },
+ onError(message, elm, xhr, url) {
+ // ...
+ },
+ onSuccess(cssText, elm, url) {
+ // ...
+ },
+ onComplete(cssText, styleElms, cssVariables, benchmark) {
+ // ...
+ },
});
diff --git a/docs/src/assets/js/focus-visible.js b/docs/src/assets/js/focus-visible.js
index c95845112cf4..da377d4b3331 100644
--- a/docs/src/assets/js/focus-visible.js
+++ b/docs/src/assets/js/focus-visible.js
@@ -1,4 +1,3 @@
-
/**
* Applies the :focus-visible polyfill at the given scope.
* A scope in this case is either the top-level Document or a Shadow Root.
@@ -7,299 +6,299 @@
* @see https://github.com/WICG/focus-visible
*/
function applyFocusVisiblePolyfill(scope) {
- var hadKeyboardEvent = true;
- var hadFocusVisibleRecently = false;
- var hadFocusVisibleRecentlyTimeout = null;
-
- var inputTypesWhitelist = {
- text: true,
- search: true,
- url: true,
- tel: true,
- email: true,
- password: true,
- number: true,
- date: true,
- month: true,
- week: true,
- time: true,
- datetime: true,
- 'datetime-local': true
- };
-
- /**
- * Helper function for legacy browsers and iframes which sometimes focus
- * elements like document, body, and non-interactive SVG.
- * @param {Element} el
- */
- function isValidFocusTarget(el) {
- if (
- el &&
- el !== document &&
- el.nodeName !== 'HTML' &&
- el.nodeName !== 'BODY' &&
- 'classList' in el &&
- 'contains' in el.classList
- ) {
- return true;
- }
- return false;
- }
-
- /**
- * Computes whether the given element should automatically trigger the
- * `focus-visible` class being added, i.e. whether it should always match
- * `:focus-visible` when focused.
- * @param {Element} el
- * @return {boolean}
- */
- function focusTriggersKeyboardModality(el) {
- var type = el.type;
- var tagName = el.tagName;
-
- if (tagName === 'INPUT' && inputTypesWhitelist[type] && !el.readOnly) {
- return true;
- }
-
- if (tagName === 'TEXTAREA' && !el.readOnly) {
- return true;
- }
-
- if (el.isContentEditable) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Add the `focus-visible` class to the given element if it was not added by
- * the author.
- * @param {Element} el
- */
- function addFocusVisibleClass(el) {
- if (el.classList.contains('focus-visible')) {
- return;
- }
- el.classList.add('focus-visible');
- el.setAttribute('data-focus-visible-added', '');
- }
-
- /**
- * Remove the `focus-visible` class from the given element if it was not
- * originally added by the author.
- * @param {Element} el
- */
- function removeFocusVisibleClass(el) {
- if (!el.hasAttribute('data-focus-visible-added')) {
- return;
- }
- el.classList.remove('focus-visible');
- el.removeAttribute('data-focus-visible-added');
- }
-
- /**
- * If the most recent user interaction was via the keyboard;
- * and the key press did not include a meta, alt/option, or control key;
- * then the modality is keyboard. Otherwise, the modality is not keyboard.
- * Apply `focus-visible` to any current active element and keep track
- * of our keyboard modality state with `hadKeyboardEvent`.
- * @param {KeyboardEvent} e
- */
- function onKeyDown(e) {
- if (e.metaKey || e.altKey || e.ctrlKey) {
- return;
- }
-
- if (isValidFocusTarget(scope.activeElement)) {
- addFocusVisibleClass(scope.activeElement);
- }
-
- hadKeyboardEvent = true;
- }
-
- /**
- * If at any point a user clicks with a pointing device, ensure that we change
- * the modality away from keyboard.
- * This avoids the situation where a user presses a key on an already focused
- * element, and then clicks on a different element, focusing it with a
- * pointing device, while we still think we're in keyboard modality.
- * @param {Event} e
- */
- function onPointerDown(e) {
- hadKeyboardEvent = false;
- }
-
- /**
- * On `focus`, add the `focus-visible` class to the target if:
- * - the target received focus as a result of keyboard navigation, or
- * - the event target is an element that will likely require interaction
- * via the keyboard (e.g. a text box)
- * @param {Event} e
- */
- function onFocus(e) {
- // Prevent IE from focusing the document or HTML element.
- if (!isValidFocusTarget(e.target)) {
- return;
- }
-
- if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {
- addFocusVisibleClass(e.target);
- }
- }
-
- /**
- * On `blur`, remove the `focus-visible` class from the target.
- * @param {Event} e
- */
- function onBlur(e) {
- if (!isValidFocusTarget(e.target)) {
- return;
- }
-
- if (
- e.target.classList.contains('focus-visible') ||
- e.target.hasAttribute('data-focus-visible-added')
- ) {
- // To detect a tab/window switch, we look for a blur event followed
- // rapidly by a visibility change.
- // If we don't see a visibility change within 100ms, it's probably a
- // regular focus change.
- hadFocusVisibleRecently = true;
- window.clearTimeout(hadFocusVisibleRecentlyTimeout);
- hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {
- hadFocusVisibleRecently = false;
- window.clearTimeout(hadFocusVisibleRecentlyTimeout);
- }, 100);
- removeFocusVisibleClass(e.target);
- }
- }
-
- /**
- * If the user changes tabs, keep track of whether or not the previously
- * focused element had .focus-visible.
- * @param {Event} e
- */
- function onVisibilityChange(e) {
- if (document.visibilityState === 'hidden') {
- // If the tab becomes active again, the browser will handle calling focus
- // on the element (Safari actually calls it twice).
- // If this tab change caused a blur on an element with focus-visible,
- // re-apply the class when the user switches back to the tab.
- if (hadFocusVisibleRecently) {
- hadKeyboardEvent = true;
- }
- addInitialPointerMoveListeners();
- }
- }
-
- /**
- * Add a group of listeners to detect usage of any pointing devices.
- * These listeners will be added when the polyfill first loads, and anytime
- * the window is blurred, so that they are active when the window regains
- * focus.
- */
- function addInitialPointerMoveListeners() {
- document.addEventListener('mousemove', onInitialPointerMove);
- document.addEventListener('mousedown', onInitialPointerMove);
- document.addEventListener('mouseup', onInitialPointerMove);
- document.addEventListener('pointermove', onInitialPointerMove);
- document.addEventListener('pointerdown', onInitialPointerMove);
- document.addEventListener('pointerup', onInitialPointerMove);
- document.addEventListener('touchmove', onInitialPointerMove);
- document.addEventListener('touchstart', onInitialPointerMove);
- document.addEventListener('touchend', onInitialPointerMove);
- }
-
- function removeInitialPointerMoveListeners() {
- document.removeEventListener('mousemove', onInitialPointerMove);
- document.removeEventListener('mousedown', onInitialPointerMove);
- document.removeEventListener('mouseup', onInitialPointerMove);
- document.removeEventListener('pointermove', onInitialPointerMove);
- document.removeEventListener('pointerdown', onInitialPointerMove);
- document.removeEventListener('pointerup', onInitialPointerMove);
- document.removeEventListener('touchmove', onInitialPointerMove);
- document.removeEventListener('touchstart', onInitialPointerMove);
- document.removeEventListener('touchend', onInitialPointerMove);
- }
-
- /**
- * When the polyfill first loads, assume the user is in keyboard modality.
- * If any event is received from a pointing device (e.g. mouse, pointer,
- * touch), turn off keyboard modality.
- * This accounts for situations where focus enters the page from the URL bar.
- * @param {Event} e
- */
- function onInitialPointerMove(e) {
- // Work around a Safari quirk that fires a mousemove on whenever the
- // window blurs, even if you're tabbing out of the page. ¯\_(ã)_/¯
- if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {
- return;
- }
-
- hadKeyboardEvent = false;
- removeInitialPointerMoveListeners();
- }
-
- // For some kinds of state, we are interested in changes at the global scope
- // only. For example, global pointer input, global key presses and global
- // visibility change should affect the state at every scope:
- document.addEventListener('keydown', onKeyDown, true);
- document.addEventListener('mousedown', onPointerDown, true);
- document.addEventListener('pointerdown', onPointerDown, true);
- document.addEventListener('touchstart', onPointerDown, true);
- document.addEventListener('visibilitychange', onVisibilityChange, true);
-
- addInitialPointerMoveListeners();
-
- // For focus and blur, we specifically care about state changes in the local
- // scope. This is because focus / blur events that originate from within a
- // shadow root are not re-dispatched from the host element if it was already
- // the active element in its own scope:
- scope.addEventListener('focus', onFocus, true);
- scope.addEventListener('blur', onBlur, true);
-
- // We detect that a node is a ShadowRoot by ensuring that it is a
- // DocumentFragment and also has a host property. This check covers native
- // implementation and polyfill implementation transparently. If we only cared
- // about the native implementation, we could just check if the scope was
- // an instance of a ShadowRoot.
- if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {
- // Since a ShadowRoot is a special kind of DocumentFragment, it does not
- // have a root element to add a class to. So, we add this attribute to the
- // host element instead:
- scope.host.setAttribute('data-js-focus-visible', '');
- } else if (scope.nodeType === Node.DOCUMENT_NODE) {
- document.documentElement.classList.add('js-focus-visible');
- }
+ var hadKeyboardEvent = true;
+ var hadFocusVisibleRecently = false;
+ var hadFocusVisibleRecentlyTimeout = null;
+
+ var inputTypesWhitelist = {
+ text: true,
+ search: true,
+ url: true,
+ tel: true,
+ email: true,
+ password: true,
+ number: true,
+ date: true,
+ month: true,
+ week: true,
+ time: true,
+ datetime: true,
+ "datetime-local": true,
+ };
+
+ /**
+ * Helper function for legacy browsers and iframes which sometimes focus
+ * elements like document, body, and non-interactive SVG.
+ * @param {Element} el
+ */
+ function isValidFocusTarget(el) {
+ if (
+ el &&
+ el !== document &&
+ el.nodeName !== "HTML" &&
+ el.nodeName !== "BODY" &&
+ "classList" in el &&
+ "contains" in el.classList
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Computes whether the given element should automatically trigger the
+ * `focus-visible` class being added, i.e. whether it should always match
+ * `:focus-visible` when focused.
+ * @param {Element} el
+ * @return {boolean}
+ */
+ function focusTriggersKeyboardModality(el) {
+ var type = el.type;
+ var tagName = el.tagName;
+
+ if (tagName === "INPUT" && inputTypesWhitelist[type] && !el.readOnly) {
+ return true;
+ }
+
+ if (tagName === "TEXTAREA" && !el.readOnly) {
+ return true;
+ }
+
+ if (el.isContentEditable) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Add the `focus-visible` class to the given element if it was not added by
+ * the author.
+ * @param {Element} el
+ */
+ function addFocusVisibleClass(el) {
+ if (el.classList.contains("focus-visible")) {
+ return;
+ }
+ el.classList.add("focus-visible");
+ el.setAttribute("data-focus-visible-added", "");
+ }
+
+ /**
+ * Remove the `focus-visible` class from the given element if it was not
+ * originally added by the author.
+ * @param {Element} el
+ */
+ function removeFocusVisibleClass(el) {
+ if (!el.hasAttribute("data-focus-visible-added")) {
+ return;
+ }
+ el.classList.remove("focus-visible");
+ el.removeAttribute("data-focus-visible-added");
+ }
+
+ /**
+ * If the most recent user interaction was via the keyboard;
+ * and the key press did not include a meta, alt/option, or control key;
+ * then the modality is keyboard. Otherwise, the modality is not keyboard.
+ * Apply `focus-visible` to any current active element and keep track
+ * of our keyboard modality state with `hadKeyboardEvent`.
+ * @param {KeyboardEvent} e
+ */
+ function onKeyDown(e) {
+ if (e.metaKey || e.altKey || e.ctrlKey) {
+ return;
+ }
+
+ if (isValidFocusTarget(scope.activeElement)) {
+ addFocusVisibleClass(scope.activeElement);
+ }
+
+ hadKeyboardEvent = true;
+ }
+
+ /**
+ * If at any point a user clicks with a pointing device, ensure that we change
+ * the modality away from keyboard.
+ * This avoids the situation where a user presses a key on an already focused
+ * element, and then clicks on a different element, focusing it with a
+ * pointing device, while we still think we're in keyboard modality.
+ * @param {Event} e
+ */
+ function onPointerDown(e) {
+ hadKeyboardEvent = false;
+ }
+
+ /**
+ * On `focus`, add the `focus-visible` class to the target if:
+ * - the target received focus as a result of keyboard navigation, or
+ * - the event target is an element that will likely require interaction
+ * via the keyboard (e.g. a text box)
+ * @param {Event} e
+ */
+ function onFocus(e) {
+ // Prevent IE from focusing the document or HTML element.
+ if (!isValidFocusTarget(e.target)) {
+ return;
+ }
+
+ if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {
+ addFocusVisibleClass(e.target);
+ }
+ }
+
+ /**
+ * On `blur`, remove the `focus-visible` class from the target.
+ * @param {Event} e
+ */
+ function onBlur(e) {
+ if (!isValidFocusTarget(e.target)) {
+ return;
+ }
+
+ if (
+ e.target.classList.contains("focus-visible") ||
+ e.target.hasAttribute("data-focus-visible-added")
+ ) {
+ // To detect a tab/window switch, we look for a blur event followed
+ // rapidly by a visibility change.
+ // If we don't see a visibility change within 100ms, it's probably a
+ // regular focus change.
+ hadFocusVisibleRecently = true;
+ window.clearTimeout(hadFocusVisibleRecentlyTimeout);
+ hadFocusVisibleRecentlyTimeout = window.setTimeout(function () {
+ hadFocusVisibleRecently = false;
+ window.clearTimeout(hadFocusVisibleRecentlyTimeout);
+ }, 100);
+ removeFocusVisibleClass(e.target);
+ }
+ }
+
+ /**
+ * If the user changes tabs, keep track of whether or not the previously
+ * focused element had .focus-visible.
+ * @param {Event} e
+ */
+ function onVisibilityChange(e) {
+ if (document.visibilityState === "hidden") {
+ // If the tab becomes active again, the browser will handle calling focus
+ // on the element (Safari actually calls it twice).
+ // If this tab change caused a blur on an element with focus-visible,
+ // re-apply the class when the user switches back to the tab.
+ if (hadFocusVisibleRecently) {
+ hadKeyboardEvent = true;
+ }
+ addInitialPointerMoveListeners();
+ }
+ }
+
+ /**
+ * Add a group of listeners to detect usage of any pointing devices.
+ * These listeners will be added when the polyfill first loads, and anytime
+ * the window is blurred, so that they are active when the window regains
+ * focus.
+ */
+ function addInitialPointerMoveListeners() {
+ document.addEventListener("mousemove", onInitialPointerMove);
+ document.addEventListener("mousedown", onInitialPointerMove);
+ document.addEventListener("mouseup", onInitialPointerMove);
+ document.addEventListener("pointermove", onInitialPointerMove);
+ document.addEventListener("pointerdown", onInitialPointerMove);
+ document.addEventListener("pointerup", onInitialPointerMove);
+ document.addEventListener("touchmove", onInitialPointerMove);
+ document.addEventListener("touchstart", onInitialPointerMove);
+ document.addEventListener("touchend", onInitialPointerMove);
+ }
+
+ function removeInitialPointerMoveListeners() {
+ document.removeEventListener("mousemove", onInitialPointerMove);
+ document.removeEventListener("mousedown", onInitialPointerMove);
+ document.removeEventListener("mouseup", onInitialPointerMove);
+ document.removeEventListener("pointermove", onInitialPointerMove);
+ document.removeEventListener("pointerdown", onInitialPointerMove);
+ document.removeEventListener("pointerup", onInitialPointerMove);
+ document.removeEventListener("touchmove", onInitialPointerMove);
+ document.removeEventListener("touchstart", onInitialPointerMove);
+ document.removeEventListener("touchend", onInitialPointerMove);
+ }
+
+ /**
+ * When the polyfill first loads, assume the user is in keyboard modality.
+ * If any event is received from a pointing device (e.g. mouse, pointer,
+ * touch), turn off keyboard modality.
+ * This accounts for situations where focus enters the page from the URL bar.
+ * @param {Event} e
+ */
+ function onInitialPointerMove(e) {
+ // Work around a Safari quirk that fires a mousemove on whenever the
+ // window blurs, even if you're tabbing out of the page. ¯\_(ã)_/¯
+ if (e.target.nodeName && e.target.nodeName.toLowerCase() === "html") {
+ return;
+ }
+
+ hadKeyboardEvent = false;
+ removeInitialPointerMoveListeners();
+ }
+
+ // For some kinds of state, we are interested in changes at the global scope
+ // only. For example, global pointer input, global key presses and global
+ // visibility change should affect the state at every scope:
+ document.addEventListener("keydown", onKeyDown, true);
+ document.addEventListener("mousedown", onPointerDown, true);
+ document.addEventListener("pointerdown", onPointerDown, true);
+ document.addEventListener("touchstart", onPointerDown, true);
+ document.addEventListener("visibilitychange", onVisibilityChange, true);
+
+ addInitialPointerMoveListeners();
+
+ // For focus and blur, we specifically care about state changes in the local
+ // scope. This is because focus / blur events that originate from within a
+ // shadow root are not re-dispatched from the host element if it was already
+ // the active element in its own scope:
+ scope.addEventListener("focus", onFocus, true);
+ scope.addEventListener("blur", onBlur, true);
+
+ // We detect that a node is a ShadowRoot by ensuring that it is a
+ // DocumentFragment and also has a host property. This check covers native
+ // implementation and polyfill implementation transparently. If we only cared
+ // about the native implementation, we could just check if the scope was
+ // an instance of a ShadowRoot.
+ if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {
+ // Since a ShadowRoot is a special kind of DocumentFragment, it does not
+ // have a root element to add a class to. So, we add this attribute to the
+ // host element instead:
+ scope.host.setAttribute("data-js-focus-visible", "");
+ } else if (scope.nodeType === Node.DOCUMENT_NODE) {
+ document.documentElement.classList.add("js-focus-visible");
+ }
}
// It is important to wrap all references to global window and document in
// these checks to support server-side rendering use cases
// @see https://github.com/WICG/focus-visible/issues/199
-if (typeof window !== 'undefined' && typeof document !== 'undefined') {
- // Make the polyfill helper globally available. This can be used as a signal
- // to interested libraries that wish to coordinate with the polyfill for e.g.,
- // applying the polyfill to a shadow root:
- window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;
-
- // Notify interested libraries of the polyfill's presence, in case the
- // polyfill was loaded lazily:
- var event;
-
- try {
- event = new CustomEvent('focus-visible-polyfill-ready');
- } catch (error) {
- // IE11 does not support using CustomEvent as a constructor directly:
- event = document.createEvent('CustomEvent');
- event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});
- }
-
- window.dispatchEvent(event);
+if (typeof window !== "undefined" && typeof document !== "undefined") {
+ // Make the polyfill helper globally available. This can be used as a signal
+ // to interested libraries that wish to coordinate with the polyfill for e.g.,
+ // applying the polyfill to a shadow root:
+ window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;
+
+ // Notify interested libraries of the polyfill's presence, in case the
+ // polyfill was loaded lazily:
+ var event;
+
+ try {
+ event = new CustomEvent("focus-visible-polyfill-ready");
+ } catch (error) {
+ // IE11 does not support using CustomEvent as a constructor directly:
+ event = document.createEvent("CustomEvent");
+ event.initCustomEvent("focus-visible-polyfill-ready", false, false, {});
+ }
+
+ window.dispatchEvent(event);
}
-if (typeof document !== 'undefined') {
- // Apply the polyfill to the global document, so that no JavaScript
- // coordination is required to use the polyfill in the top-level document:
- applyFocusVisiblePolyfill(document);
+if (typeof document !== "undefined") {
+ // Apply the polyfill to the global document, so that no JavaScript
+ // coordination is required to use the polyfill in the top-level document:
+ applyFocusVisiblePolyfill(document);
}
diff --git a/docs/src/assets/js/inert-polyfill.js b/docs/src/assets/js/inert-polyfill.js
index 11ae095ccf60..34e90291d3e4 100644
--- a/docs/src/assets/js/inert-polyfill.js
+++ b/docs/src/assets/js/inert-polyfill.js
@@ -1,23 +1,96 @@
-/* inert polyfill
+/* inert polyfill
* source: https://cdn.rawgit.com/GoogleChrome/inert-polyfill/v0.1.0/inert-polyfill.min.js
*/
window.addEventListener("load", function () {
- function h(a, b, c) { if (0 > b) { if (a.previousElementSibling) { for (a = a.previousElementSibling; a.lastElementChild;)a = a.lastElementChild; return a } return a.parentElement } if (a != c && a.firstElementChild) return a.firstElementChild; for (; null != a;) { if (a.nextElementSibling) return a.nextElementSibling; a = a.parentElement } return null } function g(a) { for (; a && a !== document.documentElement;) { if (a.hasAttribute("inert")) return a; a = a.parentElement } return null } (function (a) {
- var b = document.createElement("style");
- b.type = "text/css"; b.styleSheet ? b.styleSheet.cssText = a : b.appendChild(document.createTextNode(a)); document.body.appendChild(b)
- })("/*[inert]*/[inert]{position:relative!important;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}[inert]::before{content:'';display:block;position:absolute;top:0;left:0;right:0;bottom:0}"); var c = 0; document.addEventListener("keydown", function (a) { c = 9 === a.keyCode ? a.shiftKey ? -1 : 1 : 0 }); document.addEventListener("mousedown",
- function () { c = 0 }); document.body.addEventListener("focus", function (a) {
- var b = a.target, f = g(b); if (f) {
- if (document.hasFocus() && 0 !== c) {
- var d = document.activeElement, e = new KeyboardEvent("keydown", { keyCode: 9, which: 9, key: "Tab", code: "Tab", keyIdentifier: "U+0009", shiftKey: !!(0 > c), bubbles: !0 }); Object.defineProperty(e, "keyCode", { value: 9 }); document.activeElement.dispatchEvent(e); if (d != document.activeElement) return; for (d = f; ;) {
- d = h(d, c, f); if (!d) break; a: {
- e = b; if (!(0 > d.tabIndex) && (d.focus(), document.activeElement !== e)) {
- e =
- !0; break a
- } e = !1
- } if (e) return
- }
- } b.blur(); a.preventDefault(); a.stopPropagation()
- }
- }, !0); document.addEventListener("click", function (a) { g(a.target) && (a.preventDefault(), a.stopPropagation()) }, !0)
-});
\ No newline at end of file
+ function h(a, b, c) {
+ if (0 > b) {
+ if (a.previousElementSibling) {
+ for (a = a.previousElementSibling; a.lastElementChild; )
+ a = a.lastElementChild;
+ return a;
+ }
+ return a.parentElement;
+ }
+ if (a != c && a.firstElementChild) return a.firstElementChild;
+ for (; null != a; ) {
+ if (a.nextElementSibling) return a.nextElementSibling;
+ a = a.parentElement;
+ }
+ return null;
+ }
+ function g(a) {
+ for (; a && a !== document.documentElement; ) {
+ if (a.hasAttribute("inert")) return a;
+ a = a.parentElement;
+ }
+ return null;
+ }
+ (function (a) {
+ var b = document.createElement("style");
+ b.type = "text/css";
+ b.styleSheet
+ ? (b.styleSheet.cssText = a)
+ : b.appendChild(document.createTextNode(a));
+ document.body.appendChild(b);
+ })(
+ "/*[inert]*/[inert]{position:relative!important;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}[inert]::before{content:'';display:block;position:absolute;top:0;left:0;right:0;bottom:0}",
+ );
+ var c = 0;
+ document.addEventListener("keydown", function (a) {
+ c = 9 === a.keyCode ? (a.shiftKey ? -1 : 1) : 0;
+ });
+ document.addEventListener("mousedown", function () {
+ c = 0;
+ });
+ document.body.addEventListener(
+ "focus",
+ function (a) {
+ var b = a.target,
+ f = g(b);
+ if (f) {
+ if (document.hasFocus() && 0 !== c) {
+ var d = document.activeElement,
+ e = new KeyboardEvent("keydown", {
+ keyCode: 9,
+ which: 9,
+ key: "Tab",
+ code: "Tab",
+ keyIdentifier: "U+0009",
+ shiftKey: !!(0 > c),
+ bubbles: !0,
+ });
+ Object.defineProperty(e, "keyCode", { value: 9 });
+ document.activeElement.dispatchEvent(e);
+ if (d != document.activeElement) return;
+ for (d = f; ; ) {
+ d = h(d, c, f);
+ if (!d) break;
+ a: {
+ e = b;
+ if (
+ !(0 > d.tabIndex) &&
+ (d.focus(), document.activeElement !== e)
+ ) {
+ e = !0;
+ break a;
+ }
+ e = !1;
+ }
+ if (e) return;
+ }
+ }
+ b.blur();
+ a.preventDefault();
+ a.stopPropagation();
+ }
+ },
+ !0,
+ );
+ document.addEventListener(
+ "click",
+ function (a) {
+ g(a.target) && (a.preventDefault(), a.stopPropagation());
+ },
+ !0,
+ );
+});
diff --git a/docs/src/assets/js/main.js b/docs/src/assets/js/main.js
index 80168a136c91..83599a5d32d1 100644
--- a/docs/src/assets/js/main.js
+++ b/docs/src/assets/js/main.js
@@ -1,303 +1,304 @@
(function () {
- // for sticky table of contents
- const tocBody = document.querySelector(".docs-aside #js-toc-panel");
- const options = {
- root: null,
- rootMargin: `0px 0px -90% 0px`,
- threshold: 1.0,
- };
- const activeClassName = "active";
- const observer = new IntersectionObserver((entries) => {
- entries.forEach((entry) => {
- if (entry.isIntersecting) {
- const activeAnchor = tocBody.querySelector(
- `a.${activeClassName}`
- );
- if (activeAnchor) {
- activeAnchor.parentNode.classList.remove(activeClassName);
- activeAnchor.classList.remove(activeClassName);
- }
-
- const nextActiveAnchor = tocBody.querySelector(
- `a[href="#${entry.target.id}"]`
- );
- if (nextActiveAnchor) {
- nextActiveAnchor.parentNode.classList.add(activeClassName);
- nextActiveAnchor.classList.add(activeClassName);
- }
- }
- });
- }, options);
- if (window.matchMedia("(min-width: 1400px)").matches) {
- document
- .querySelectorAll(
- "#main > div > h2[id], #main > div > h3[id], #main > div > h4[id]" // only h2, h3, h4 are shown in toc
- )
- .forEach((el) => observer.observe(el));
- }
+ // for sticky table of contents
+ const tocBody = document.querySelector(".docs-aside #js-toc-panel");
+ const options = {
+ root: null,
+ rootMargin: `0px 0px -90% 0px`,
+ threshold: 1.0,
+ };
+ const activeClassName = "active";
+ const observer = new IntersectionObserver(entries => {
+ entries.forEach(entry => {
+ if (entry.isIntersecting) {
+ const activeAnchor = tocBody.querySelector(
+ `a.${activeClassName}`,
+ );
+ if (activeAnchor) {
+ activeAnchor.parentNode.classList.remove(activeClassName);
+ activeAnchor.classList.remove(activeClassName);
+ }
+
+ const nextActiveAnchor = tocBody.querySelector(
+ `a[href="#${entry.target.id}"]`,
+ );
+ if (nextActiveAnchor) {
+ nextActiveAnchor.parentNode.classList.add(activeClassName);
+ nextActiveAnchor.classList.add(activeClassName);
+ }
+ }
+ });
+ }, options);
+ if (window.matchMedia("(min-width: 1400px)").matches) {
+ document
+ .querySelectorAll(
+ "#main > div > h2[id], #main > div > h3[id], #main > div > h4[id]", // only h2, h3, h4 are shown in toc
+ )
+ .forEach(el => observer.observe(el));
+ }
})();
-(function() {
- var toc_trigger = document.getElementById("js-toc-label"),
- toc = document.getElementById("js-toc-panel"),
- body = document.getElementsByTagName("body")[0],
- open = false;
-
- if (toc && matchMedia) {
- const mq = window.matchMedia("(max-width: 1023px)");
- mq.addEventListener('change', WidthChange);
- WidthChange(mq);
- }
-
- // media query change
- function WidthChange(mq) {
- if (mq.matches && toc_trigger) {
- let text = toc_trigger.innerText;
- let headingButton = document.createElement("button");
- headingButton.setAttribute("aria-expanded", "false");
- headingButton.innerText = text;
- toc_trigger.innerHTML = "";
-
- toc_trigger.appendChild(headingButton);
- headingButton.innerHTML += ``;
-
- toc.setAttribute("data-open", "false");
- toc_trigger.setAttribute("aria-expanded", "false");
- headingButton.addEventListener("click", toggleTOC, true);
- } else {
- toc_trigger.innerHTML = 'Table of Contents';
- toc.setAttribute("data-open", "true");
- }
-
- }
-
- function toggleTOC(e) {
- if (!open) {
- this.setAttribute("aria-expanded", "true");
- toc.setAttribute("data-open", "true");
- open = true;
- } else {
- this.setAttribute("aria-expanded", "false");
- toc.setAttribute("data-open", "false");
- open = false;
- }
- }
+(function () {
+ var toc_trigger = document.getElementById("js-toc-label"),
+ toc = document.getElementById("js-toc-panel"),
+ body = document.getElementsByTagName("body")[0],
+ open = false;
+
+ if (toc && matchMedia) {
+ const mq = window.matchMedia("(max-width: 1023px)");
+ mq.addEventListener("change", WidthChange);
+ WidthChange(mq);
+ }
+
+ // media query change
+ function WidthChange(mq) {
+ if (mq.matches && toc_trigger) {
+ let text = toc_trigger.innerText;
+ let headingButton = document.createElement("button");
+ headingButton.setAttribute("aria-expanded", "false");
+ headingButton.innerText = text;
+ toc_trigger.innerHTML = "";
+
+ toc_trigger.appendChild(headingButton);
+ headingButton.innerHTML += ``;
+
+ toc.setAttribute("data-open", "false");
+ toc_trigger.setAttribute("aria-expanded", "false");
+ headingButton.addEventListener("click", toggleTOC, true);
+ } else {
+ toc_trigger.innerHTML = "Table of Contents";
+ toc.setAttribute("data-open", "true");
+ }
+ }
+
+ function toggleTOC(e) {
+ if (!open) {
+ this.setAttribute("aria-expanded", "true");
+ toc.setAttribute("data-open", "true");
+ open = true;
+ } else {
+ this.setAttribute("aria-expanded", "false");
+ toc.setAttribute("data-open", "false");
+ open = false;
+ }
+ }
})();
-(function() {
- var nav_trigger = document.getElementById("nav-toggle"),
- nav = document.getElementById("nav-panel"),
- body = document.getElementsByTagName("body")[0],
- open = false;
-
- if (matchMedia) {
- const mq = window.matchMedia("(max-width: 1023px)");
- mq.addEventListener('change', WidthChange);
- WidthChange(mq);
- }
-
- // media query change
- function WidthChange(mq) {
- if (mq.matches) {
- nav.setAttribute("data-open", "false");
- nav_trigger.removeAttribute("hidden");
- nav_trigger.setAttribute("aria-expanded", "false");
- nav_trigger.addEventListener("click", togglenav, false);
- } else {
- nav.setAttribute("data-open", "true");
- nav_trigger.setAttribute("hidden", "");
- nav_trigger.setAttribute("aria-expanded", "true");
- }
-
- }
-
- function togglenav(e) {
- if (!open) {
- this.setAttribute("aria-expanded", "true");
- nav.setAttribute("data-open", "true");
- open = true;
- } else {
- this.setAttribute("aria-expanded", "false");
- nav.setAttribute("data-open", "false");
- open = false;
- }
- }
+(function () {
+ var nav_trigger = document.getElementById("nav-toggle"),
+ nav = document.getElementById("nav-panel"),
+ body = document.getElementsByTagName("body")[0],
+ open = false;
+
+ if (matchMedia) {
+ const mq = window.matchMedia("(max-width: 1023px)");
+ mq.addEventListener("change", WidthChange);
+ WidthChange(mq);
+ }
+
+ // media query change
+ function WidthChange(mq) {
+ if (mq.matches) {
+ nav.setAttribute("data-open", "false");
+ nav_trigger.removeAttribute("hidden");
+ nav_trigger.setAttribute("aria-expanded", "false");
+ nav_trigger.addEventListener("click", togglenav, false);
+ } else {
+ nav.setAttribute("data-open", "true");
+ nav_trigger.setAttribute("hidden", "");
+ nav_trigger.setAttribute("aria-expanded", "true");
+ }
+ }
+
+ function togglenav(e) {
+ if (!open) {
+ this.setAttribute("aria-expanded", "true");
+ nav.setAttribute("data-open", "true");
+ open = true;
+ } else {
+ this.setAttribute("aria-expanded", "false");
+ nav.setAttribute("data-open", "false");
+ open = false;
+ }
+ }
})();
-(function() {
- var index_trigger = document.getElementById("js-docs-index-toggle"),
- index = document.getElementById("js-docs-index-panel"),
- body = document.getElementsByTagName("body")[0],
- open = false;
-
- if (matchMedia) {
- const mq = window.matchMedia("(max-width: 1023px)");
- mq.addEventListener('change', WidthChange);
- WidthChange(mq);
- }
-
- function WidthChange(mq) {
- initIndex();
- }
-
- function toggleindex(e) {
- if (!open) {
- this.setAttribute("aria-expanded", "true");
- index.setAttribute("data-open", "true");
- open = true;
- } else {
- this.setAttribute("aria-expanded", "false");
- index.setAttribute("data-open", "false");
- open = false;
- }
- }
-
- function initIndex() {
- if(index_trigger) {
-
- index_trigger.removeAttribute("hidden");
- index_trigger.setAttribute("aria-expanded", "false");
- index.setAttribute("data-open", "false");
-
- index.setAttribute("data-open", "false");
- index_trigger.addEventListener("click", toggleindex, false);
- }
- }
+(function () {
+ var index_trigger = document.getElementById("js-docs-index-toggle"),
+ index = document.getElementById("js-docs-index-panel"),
+ body = document.getElementsByTagName("body")[0],
+ open = false;
+
+ if (matchMedia) {
+ const mq = window.matchMedia("(max-width: 1023px)");
+ mq.addEventListener("change", WidthChange);
+ WidthChange(mq);
+ }
+
+ function WidthChange(mq) {
+ initIndex();
+ }
+
+ function toggleindex(e) {
+ if (!open) {
+ this.setAttribute("aria-expanded", "true");
+ index.setAttribute("data-open", "true");
+ open = true;
+ } else {
+ this.setAttribute("aria-expanded", "false");
+ index.setAttribute("data-open", "false");
+ open = false;
+ }
+ }
+
+ function initIndex() {
+ if (index_trigger) {
+ index_trigger.removeAttribute("hidden");
+ index_trigger.setAttribute("aria-expanded", "false");
+ index.setAttribute("data-open", "false");
+
+ index.setAttribute("data-open", "false");
+ index_trigger.addEventListener("click", toggleindex, false);
+ }
+ }
})();
-
-
-(function() {
- var switchers = document.querySelectorAll('.switcher'),
- fallbacks = document.querySelectorAll('.switcher-fallback');
-
- if (fallbacks != null) {
- fallbacks.forEach(el => {
- el.setAttribute('hidden', '');
- });
- }
-
- if (switchers != null) {
- switchers.forEach(element => {
- element.removeAttribute('hidden');
- const select = element.querySelector('select');
-
- select.addEventListener('change', function() {
- var selected = this.options[this.selectedIndex];
- url = selected.getAttribute('data-url');
-
- window.location.href = url;
- })
- });
- }
+(function () {
+ var switchers = document.querySelectorAll(".switcher"),
+ fallbacks = document.querySelectorAll(".switcher-fallback");
+
+ if (fallbacks != null) {
+ fallbacks.forEach(el => {
+ el.setAttribute("hidden", "");
+ });
+ }
+
+ if (switchers != null) {
+ switchers.forEach(element => {
+ element.removeAttribute("hidden");
+ const select = element.querySelector("select");
+
+ select.addEventListener("change", function () {
+ var selected = this.options[this.selectedIndex];
+ url = selected.getAttribute("data-url");
+
+ window.location.href = url;
+ });
+ });
+ }
})();
// add utilities
var util = {
- keyCodes: {
- UP: 38,
- DOWN: 40,
- LEFT: 37,
- RIGHT: 39,
- HOME: 36,
- END: 35,
- ENTER: 13,
- SPACE: 32,
- DELETE: 46,
- TAB: 9,
- },
-
- generateID: function(base) {
- return base + Math.floor(Math.random() * 999);
- },
-
- getDirectChildren: function(elm, selector) {
- return Array.prototype.filter.call(elm.children, function(child) {
- return child.matches(selector);
- });
- },
+ keyCodes: {
+ UP: 38,
+ DOWN: 40,
+ LEFT: 37,
+ RIGHT: 39,
+ HOME: 36,
+ END: 35,
+ ENTER: 13,
+ SPACE: 32,
+ DELETE: 46,
+ TAB: 9,
+ },
+
+ generateID: function (base) {
+ return base + Math.floor(Math.random() * 999);
+ },
+
+ getDirectChildren: function (elm, selector) {
+ return Array.prototype.filter.call(elm.children, function (child) {
+ return child.matches(selector);
+ });
+ },
};
-(function(w, doc, undefined) {
- var CollapsibleIndexOptions = {
- allCollapsed: false,
- icon: '',
- };
- var CollapsibleIndex = function(inst, options) {
- var _options = Object.assign(CollapsibleIndexOptions, options);
- var el = inst;
- var indexToggles = el.querySelectorAll(".docs-index .docs__index__panel > ul > .docs-index__item[data-has-children] > a"); // only top-most level
- var indexPanels = el.querySelectorAll(".docs-index .docs__index__panel > ul > .docs-index__item>[data-child-list]"); // the list
- var accID = util.generateID("c-index-");
-
- var init = function() {
- el.classList.add("index-js");
-
- setupindexToggles(indexToggles);
- setupindexPanels(indexPanels);
- };
-
-
- var setupindexToggles = function(indexToggles) {
- Array.from(indexToggles).forEach(function(item, index) {
- var $this = item;
-
- $this.setAttribute('role', 'button');
- $this.setAttribute("id", accID + "__item-" + index);
- $this.innerHTML += _options.icon;
-
- if (_options.allCollapsed) $this.setAttribute("aria-expanded", "false");
- else $this.setAttribute("aria-expanded", "true");
-
- $this.addEventListener("click", function(e) {
- e.preventDefault();
- togglePanel($this);
- });
- });
- };
-
- var setupindexPanels = function(indexPanels) {
- Array.from(indexPanels).forEach(function(item, index) {
- let $this = item;
-
- $this.setAttribute("id", accID + "__list-" + index);
- $this.setAttribute(
- "aria-labelledby",
- accID + "__item-" + index
- );
- if (_options.allCollapsed) $this.setAttribute("aria-hidden", "true");
- else $this.setAttribute("aria-hidden", "false");
- });
- };
-
- var togglePanel = function(toggleButton) {
- var thepanel = toggleButton.nextElementSibling;
-
- if (toggleButton.getAttribute("aria-expanded") == "true") {
- toggleButton.setAttribute("aria-expanded", "false");
- thepanel.setAttribute("aria-hidden", "true");
- } else {
- toggleButton.setAttribute("aria-expanded", "true");
- thepanel.setAttribute("aria-hidden", "false");
- }
- };
-
-
- init.call(this);
- return this;
- }; // CollapsibleIndex()
-
- w.CollapsibleIndex = CollapsibleIndex;
+(function (w, doc, undefined) {
+ var CollapsibleIndexOptions = {
+ allCollapsed: false,
+ icon: '',
+ };
+ var CollapsibleIndex = function (inst, options) {
+ var _options = Object.assign(CollapsibleIndexOptions, options);
+ var el = inst;
+ var indexToggles = el.querySelectorAll(
+ ".docs-index .docs__index__panel > ul > .docs-index__item[data-has-children] > a",
+ ); // only top-most level
+ var indexPanels = el.querySelectorAll(
+ ".docs-index .docs__index__panel > ul > .docs-index__item>[data-child-list]",
+ ); // the list
+ var accID = util.generateID("c-index-");
+
+ var init = function () {
+ el.classList.add("index-js");
+
+ setupindexToggles(indexToggles);
+ setupindexPanels(indexPanels);
+ };
+
+ var setupindexToggles = function (indexToggles) {
+ Array.from(indexToggles).forEach(function (item, index) {
+ var $this = item;
+
+ $this.setAttribute("role", "button");
+ $this.setAttribute("id", accID + "__item-" + index);
+ $this.innerHTML += _options.icon;
+
+ if (_options.allCollapsed)
+ $this.setAttribute("aria-expanded", "false");
+ else $this.setAttribute("aria-expanded", "true");
+
+ $this.addEventListener("click", function (e) {
+ e.preventDefault();
+ togglePanel($this);
+ });
+ });
+ };
+
+ var setupindexPanels = function (indexPanels) {
+ Array.from(indexPanels).forEach(function (item, index) {
+ let $this = item;
+
+ $this.setAttribute("id", accID + "__list-" + index);
+ $this.setAttribute(
+ "aria-labelledby",
+ accID + "__item-" + index,
+ );
+ if (_options.allCollapsed)
+ $this.setAttribute("aria-hidden", "true");
+ else $this.setAttribute("aria-hidden", "false");
+ });
+ };
+
+ var togglePanel = function (toggleButton) {
+ var thepanel = toggleButton.nextElementSibling;
+
+ if (toggleButton.getAttribute("aria-expanded") == "true") {
+ toggleButton.setAttribute("aria-expanded", "false");
+ thepanel.setAttribute("aria-hidden", "true");
+ } else {
+ toggleButton.setAttribute("aria-expanded", "true");
+ thepanel.setAttribute("aria-hidden", "false");
+ }
+ };
+
+ init.call(this);
+ return this;
+ }; // CollapsibleIndex()
+
+ w.CollapsibleIndex = CollapsibleIndex;
})(window, document);
// init
-var index = document.getElementById('docs-index');
+var index = document.getElementById("docs-index");
if (index) {
- index = new CollapsibleIndex(index, {
- allCollapsed: false
- });
+ index = new CollapsibleIndex(index, {
+ allCollapsed: false,
+ });
}
document.addEventListener("DOMContentLoaded", () => {
- anchors.add(".docs-content h2:not(.c-toc__label), .docs-content h3, .docs-content h4");
+ anchors.add(
+ ".docs-content h2:not(.c-toc__label), .docs-content h3, .docs-content h4",
+ );
});
diff --git a/docs/src/assets/js/scroll-up-btn.js b/docs/src/assets/js/scroll-up-btn.js
index cb77af1bcbe4..9a3cf4ed2ee5 100644
--- a/docs/src/assets/js/scroll-up-btn.js
+++ b/docs/src/assets/js/scroll-up-btn.js
@@ -1,13 +1,16 @@
(function () {
- const scrollUpBtn = document.getElementById("scroll-up-btn");
+ const scrollUpBtn = document.getElementById("scroll-up-btn");
- if(window.innerWidth < 1400) {
- window.addEventListener("scroll", function () {
- if(document.body.scrollTop > 500 || document.documentElement.scrollTop > 500) {
- scrollUpBtn.style.display = "flex";
- } else {
- scrollUpBtn.style.display = "none";
- }
- });
- }
-})();
\ No newline at end of file
+ if (window.innerWidth < 1400) {
+ window.addEventListener("scroll", function () {
+ if (
+ document.body.scrollTop > 500 ||
+ document.documentElement.scrollTop > 500
+ ) {
+ scrollUpBtn.style.display = "flex";
+ } else {
+ scrollUpBtn.style.display = "none";
+ }
+ });
+ }
+})();
diff --git a/docs/src/assets/js/search.js b/docs/src/assets/js/search.js
index 6d8eaa7b1b2b..ac7a3492cbcb 100644
--- a/docs/src/assets/js/search.js
+++ b/docs/src/assets/js/search.js
@@ -14,16 +14,20 @@ import algoliasearch from "./algoliasearch.js";
//-----------------------------------------------------------------------------
// search
-const client = algoliasearch('L633P0C2IR', 'bb6bbd2940351f3afc18844a6b06a6e8');
-const index = client.initIndex('eslint');
+const client = algoliasearch("L633P0C2IR", "bb6bbd2940351f3afc18844a6b06a6e8");
+const index = client.initIndex("eslint");
// page
-const resultsElement = document.querySelector('#search-results');
-const resultsLiveRegion = document.querySelector('#search-results-announcement');
-const searchInput = document.querySelector('#search');
-const searchClearBtn = document.querySelector('#search__clear-btn');
+const resultsElement = document.querySelector("#search-results");
+const resultsLiveRegion = document.querySelector(
+ "#search-results-announcement",
+);
+const searchInput = document.querySelector("#search");
+const searchClearBtn = document.querySelector("#search__clear-btn");
+const poweredByLink = document.querySelector(".search_powered-by-wrapper");
let activeIndex = -1;
let searchQuery;
+let caretPosition = 0;
//-----------------------------------------------------------------------------
// Helpers
@@ -35,20 +39,45 @@ let searchQuery;
* @returns {Promise>} The search results.
*/
function fetchSearchResults(query) {
- return index.search(query, {
- // facetFilters: ["tags:docs"]
- }).then(({ hits }) => hits);
+ return index
+ .search(query, {
+ facetFilters: ["tags:docs"],
+ })
+ .then(({ hits }) => hits);
}
/**
- * Removes any current search results from the display.
- * @returns {void}
+ * Clears the search results from the display.
+ * If the removeEventListener flag is true, removes the click event listener from the document.
+ * @param {boolean} [removeEventListener=false] - Optional flag to indicate if the click event listener should be removed. Default is false.
+ * @returns {void} - This function doesn't return anything.
+ */
+function clearSearchResults(removeEventListener = false) {
+ resultsElement.innerHTML = "";
+ if (removeEventListener && document.clickEventAdded) {
+ document.removeEventListener("click", handleDocumentClick);
+ document.clickEventAdded = false;
+ }
+}
+
+/**
+ * Displays a "No results found" message in both the live region and results display area.
+ * This is typically used when no matching results are found in the search.
+ * @returns {void} - This function doesn't return anything.
+ */
+function showNoResults() {
+ resultsLiveRegion.innerHTML = "No results found.";
+ resultsElement.innerHTML = "No results found.";
+ resultsElement.setAttribute("data-results", "false");
+}
+
+/**
+ * Clears any "No results found" message from the live region and results display area.
+ * @returns {void} - This function doesn't return anything.
*/
-function clearSearchResults() {
- while (resultsElement.firstChild) {
- resultsElement.removeChild(resultsElement.firstChild);
- }
- resultsElement.innerHTML = "";
+function clearNoResults() {
+ resultsLiveRegion.innerHTML = "";
+ resultsElement.innerHTML = "";
}
/**
@@ -57,58 +86,53 @@ function clearSearchResults() {
* @returns {void}
*/
function displaySearchResults(results) {
-
- clearSearchResults();
-
- if (results.length) {
-
- const list = document.createElement("ul");
- list.setAttribute('role', 'list');
- list.classList.add('search-results__list');
- resultsElement.append(list);
- resultsElement.setAttribute('data-results', 'true');
- activeIndex = -1;
-
- for (const result of results) {
- const listItem = document.createElement('li');
- listItem.classList.add('search-results__item');
- const maxLvl = Math.max(...Object.keys(result._highlightResult.hierarchy).map(k => Number(k.substring(3))));
- listItem.innerHTML = `
+ clearSearchResults();
+
+ if (results.length) {
+ const list = document.createElement("ul");
+ list.setAttribute("role", "list");
+ list.classList.add("search-results__list");
+ resultsElement.append(list);
+ resultsElement.setAttribute("data-results", "true");
+ activeIndex = -1;
+
+ for (const result of results) {
+ const listItem = document.createElement("li");
+ listItem.classList.add("search-results__item");
+ const maxLvl = Math.max(
+ ...Object.keys(result._highlightResult.hierarchy).map(k =>
+ Number(k.substring(3)),
+ ),
+ );
+ listItem.innerHTML = `
${result.hierarchy.lvl0}
- ${typeof result._highlightResult.content !== 'undefined' ? result._highlightResult.content.value : result._highlightResult.hierarchy[`lvl${maxLvl}`].value}
+ ${typeof result._highlightResult.content !== "undefined" ? result._highlightResult.content.value : result._highlightResult.hierarchy[`lvl${maxLvl}`].value}
`.trim();
- list.append(listItem);
- }
-
- } else {
- resultsLiveRegion.innerHTML = "No results found.";
- resultsElement.innerHTML = "No results found.";
- resultsElement.setAttribute('data-results', 'false');
- }
-
+ list.append(listItem);
+ }
+ } else {
+ showNoResults();
+ }
}
-
// Check if an element is currently scrollable
function isScrollable(element) {
- return element && element.clientHeight < element.scrollHeight;
+ return element && element.clientHeight < element.scrollHeight;
}
// Ensure given child element is within the parent's visible scroll area
function maintainScrollVisibility(activeElement, scrollParent) {
- const { offsetHeight, offsetTop } = activeElement;
- const { offsetHeight: parentOffsetHeight, scrollTop } = scrollParent;
+ const { offsetHeight, offsetTop } = activeElement;
+ const { offsetHeight: parentOffsetHeight, scrollTop } = scrollParent;
- const isAbove = offsetTop < scrollTop;
- const isBelow = (offsetTop + offsetHeight) > (scrollTop + parentOffsetHeight);
-
- if (isAbove) {
- scrollParent.scrollTo(0, offsetTop);
- }
- else if (isBelow) {
- scrollParent.scrollTo(0, offsetTop - parentOffsetHeight + offsetHeight);
- }
+ const isAbove = offsetTop < scrollTop;
+ const isBelow = offsetTop + offsetHeight > scrollTop + parentOffsetHeight;
+ if (isAbove) {
+ scrollParent.scrollTo(0, offsetTop);
+ } else if (isBelow) {
+ scrollParent.scrollTo(0, offsetTop - parentOffsetHeight + offsetHeight);
+ }
}
/**
@@ -118,92 +142,153 @@ function maintainScrollVisibility(activeElement, scrollParent) {
* @returns {Function} Returns the new debounced function.
*/
function debounce(callback, delay) {
- let timer;
- return (...args) => {
- if (timer) clearTimeout(timer);
- timer = setTimeout(() => callback.apply(this, args), delay);
- }
+ let timer;
+ return (...args) => {
+ if (timer) clearTimeout(timer);
+ timer = setTimeout(() => callback.apply(this, args), delay);
+ };
}
-const debouncedFetchSearchResults = debounce((query) => {
- fetchSearchResults(query)
- .then(displaySearchResults)
- .catch(clearSearchResults);
+/**
+ * Debounced function to fetch search results after 300ms of inactivity.
+ * Calls `fetchSearchResults` to retrieve data and `displaySearchResults` to show them.
+ * If an error occurs, clears the search results.
+ * @param {string} query - The search query.
+ * @returns {void} - No return value.
+ * @see debounce - Limits the number of requests during rapid typing.
+ */
+const debouncedFetchSearchResults = debounce(query => {
+ fetchSearchResults(query)
+ .then(displaySearchResults)
+ .catch(() => {
+ clearSearchResults(true);
+ });
}, 300);
+/**
+ * Handles the document click event to clear search results if the user clicks outside of the search input or results element.
+ * @param {MouseEvent} e - The event object representing the click event.
+ * @returns {void} - This function does not return any value. It directly interacts with the UI by clearing search results.
+ */
+const handleDocumentClick = e => {
+ if (e.target !== resultsElement && e.target !== searchInput) {
+ clearSearchResults(true);
+ }
+};
+
//-----------------------------------------------------------------------------
// Event Handlers
//-----------------------------------------------------------------------------
// listen for input changes
if (searchInput)
- searchInput.addEventListener('keyup', function (e) {
- const query = searchInput.value;
-
- if (query === searchQuery) return;
-
- if (query.length) searchClearBtn.removeAttribute('hidden');
- else searchClearBtn.setAttribute('hidden', '');
-
- if (query.length > 2) {
-
- debouncedFetchSearchResults(query);
-
- document.addEventListener('click', function (e) {
- if (e.target !== resultsElement) clearSearchResults();
- });
- } else {
- clearSearchResults();
- }
-
- searchQuery = query
-
- });
-
-
-if (searchClearBtn)
- searchClearBtn.addEventListener('click', function (e) {
- searchInput.value = '';
- searchInput.focus();
- clearSearchResults();
- searchClearBtn.setAttribute('hidden', '');
- });
-
-document.addEventListener('keydown', function (e) {
-
- const searchResults = Array.from(document.querySelectorAll('.search-results__item'));
-
- if (e.key === 'Escape') {
- e.preventDefault();
- if (searchResults.length) {
- clearSearchResults();
- searchInput.focus();
- }
- }
-
- if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
- e.preventDefault();
- searchInput.focus();
- document.querySelector('.search').scrollIntoView({ behavior: "smooth", block: "start" });
- }
-
- if (!searchResults.length) return;
-
- switch (e.key) {
- case "ArrowUp":
- e.preventDefault();
- activeIndex = activeIndex - 1 < 0 ? searchResults.length - 1 : activeIndex - 1;
- break;
- case "ArrowDown":
- e.preventDefault();
- activeIndex = activeIndex + 1 < searchResults.length ? activeIndex + 1 : 0;
- break;
- }
-
- if (activeIndex === -1) return;
- const activeSearchResult = searchResults[activeIndex];
- activeSearchResult.querySelector('a').focus();
- if (isScrollable(resultsElement)) {
- maintainScrollVisibility(activeSearchResult, resultsElement);
- }
+ searchInput.addEventListener("keyup", function (e) {
+ const query = searchInput.value;
+
+ if (query === searchQuery) return;
+
+ if (query.length) searchClearBtn.removeAttribute("hidden");
+ else searchClearBtn.setAttribute("hidden", "");
+
+ if (query.length > 2) {
+ debouncedFetchSearchResults(query);
+ if (!document.clickEventAdded) {
+ document.addEventListener("click", handleDocumentClick);
+ document.clickEventAdded = true;
+ }
+ } else {
+ clearSearchResults(true);
+ }
+
+ searchQuery = query;
+ });
+
+if (searchClearBtn) {
+ searchClearBtn.addEventListener("click", function () {
+ searchInput.value = "";
+ searchInput.focus();
+ clearSearchResults(true);
+ searchClearBtn.setAttribute("hidden", "");
+ });
+
+ searchInput.addEventListener("blur", function () {
+ caretPosition = searchInput.selectionStart;
+ });
+
+ searchInput.addEventListener("focus", function () {
+ if (searchInput.selectionStart !== caretPosition) {
+ searchInput.setSelectionRange(caretPosition, caretPosition);
+ }
+ });
+}
+
+if (poweredByLink) {
+ poweredByLink.addEventListener("focus", function () {
+ clearSearchResults();
+ });
+}
+
+if (resultsElement) {
+ resultsElement.addEventListener("keydown", e => {
+ if (
+ e.key !== "ArrowUp" &&
+ e.key !== "ArrowDown" &&
+ e.key !== "Tab" &&
+ e.key !== "Shift" &&
+ e.key !== "Enter"
+ ) {
+ searchInput.focus();
+ }
+ });
+}
+
+document.addEventListener("keydown", function (e) {
+ const searchResults = Array.from(
+ document.querySelectorAll(".search-results__item"),
+ );
+ const isArrowKey = e.key === "ArrowUp" || e.key === "ArrowDown";
+
+ if (e.key === "Escape") {
+ e.preventDefault();
+ if (searchResults.length) {
+ clearSearchResults(true);
+ searchInput.focus();
+ } else if (document.activeElement === searchInput) {
+ clearNoResults();
+ searchInput.blur();
+ }
+ }
+
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
+ e.preventDefault();
+ searchInput.focus();
+ document
+ .querySelector(".search")
+ .scrollIntoView({ behavior: "smooth", block: "start" });
+ }
+
+ if (!searchResults.length) return;
+
+ if (isArrowKey) {
+ e.preventDefault();
+
+ if (e.key === "ArrowUp") {
+ activeIndex =
+ activeIndex - 1 < 0
+ ? searchResults.length - 1
+ : activeIndex - 1;
+ } else if (e.key === "ArrowDown") {
+ activeIndex =
+ activeIndex + 1 < searchResults.length ? activeIndex + 1 : 0;
+ }
+
+ if (activeIndex !== -1) {
+ const activeSearchResult = searchResults[activeIndex];
+ activeSearchResult.querySelector("a").focus();
+
+ if (isScrollable(resultsElement)) {
+ maintainScrollVisibility(activeSearchResult, resultsElement);
+ }
+ }
+ }
});
diff --git a/docs/src/assets/js/tabs.js b/docs/src/assets/js/tabs.js
index a22159385389..a8613c7a0d75 100644
--- a/docs/src/assets/js/tabs.js
+++ b/docs/src/assets/js/tabs.js
@@ -1,337 +1,339 @@
"use strict";
if (typeof Object.assign != "function") {
- // Must be writable: true, enumerable: false, configurable: true
- Object.defineProperty(Object, "assign", {
- value: function assign(target, varArgs) {
- // .length of function is 2
-
- if (target == null) {
- // TypeError if undefined or null
- throw new TypeError(
- "Cannot convert undefined or null to object"
- );
- }
-
- var to = Object(target);
-
- for (var index = 1; index < arguments.length; index++) {
- var nextSource = arguments[index];
-
- if (nextSource != null) {
- // Skip over if undefined or null
- for (var nextKey in nextSource) {
- // Avoid bugs when hasOwnProperty is shadowed
- if (
- Object.prototype.hasOwnProperty.call(
- nextSource,
- nextKey
- )
- ) {
- to[nextKey] = nextSource[nextKey];
- }
- }
- }
- }
- return to;
- },
- writable: true,
- configurable: true
- });
+ // Must be writable: true, enumerable: false, configurable: true
+ Object.defineProperty(Object, "assign", {
+ value: function assign(target, varArgs) {
+ // .length of function is 2
+
+ if (target == null) {
+ // TypeError if undefined or null
+ throw new TypeError(
+ "Cannot convert undefined or null to object",
+ );
+ }
+
+ var to = Object(target);
+
+ for (var index = 1; index < arguments.length; index++) {
+ var nextSource = arguments[index];
+
+ if (nextSource != null) {
+ // Skip over if undefined or null
+ for (var nextKey in nextSource) {
+ // Avoid bugs when hasOwnProperty is shadowed
+ if (
+ Object.prototype.hasOwnProperty.call(
+ nextSource,
+ nextKey,
+ )
+ ) {
+ to[nextKey] = nextSource[nextKey];
+ }
+ }
+ }
+ }
+ return to;
+ },
+ writable: true,
+ configurable: true,
+ });
}
// add utilities; borrowed from: https://scottaohara.github.io/a11y_tab_widget/
var util = {
- keyCodes: {
- UP: 38,
- DOWN: 40,
- LEFT: 37,
- RIGHT: 39,
- HOME: 36,
- END: 35,
- ENTER: 13,
- SPACE: 32,
- DELETE: 46,
- TAB: 9
- },
-
- generateID: function (base) {
- return base + Math.floor(Math.random() * 999);
- },
-
-
- getUrlHash: function () {
- return window.location.hash.replace('#', '');
- },
-
- /**
- * Use history.replaceState so clicking through Tabs
- * does not create dozens of new history entries.
- * Browser back should navigate to the previous page
- * regardless of how many Tabs were activated.
- *
- * @param {string} hash
- */
- setUrlHash: function (hash) {
- if (history.replaceState) {
- history.replaceState(null, '', '#' + hash);
- } else {
- location.hash = hash;
- }
- }
+ keyCodes: {
+ UP: 38,
+ DOWN: 40,
+ LEFT: 37,
+ RIGHT: 39,
+ HOME: 36,
+ END: 35,
+ ENTER: 13,
+ SPACE: 32,
+ DELETE: 46,
+ TAB: 9,
+ },
+
+ generateID: function (base) {
+ return base + Math.floor(Math.random() * 999);
+ },
+
+ getUrlHash: function () {
+ return window.location.hash.replace("#", "");
+ },
+
+ /**
+ * Use history.replaceState so clicking through Tabs
+ * does not create dozens of new history entries.
+ * Browser back should navigate to the previous page
+ * regardless of how many Tabs were activated.
+ *
+ * @param {string} hash
+ */
+ setUrlHash: function (hash) {
+ if (history.replaceState) {
+ history.replaceState(null, "", "#" + hash);
+ } else {
+ location.hash = hash;
+ }
+ },
};
-
-
-
(function (w, doc, undefined) {
-
- var ARIAaccOptions = {
- manual: true,
- open: 0
- }
-
- var ARIAtabs = function (inst, options) {
- var _options = Object.assign(ARIAaccOptions, options);
- var el = inst;
- var tablist = el.querySelector("[data-tablist]");
- var tabs = Array.from(el.querySelectorAll("[data-tab]"));
- var tabpanels = Array.from(el.querySelectorAll("[data-tabpanel]"));
- var tabsID = util.generateID('ps__tabs-');
- var orientation = el.getAttribute('data-tabs-orientation');
- var currentIndex = _options.open;
- var selectedTab = currentIndex;
- var manual = _options.manual;
-
- el.setAttribute('id', tabsID);
-
- var init = function () {
- el.classList.add('js-tabs');
- tablist.removeAttribute('hidden');
- setupTabList();
- setupTabs();
- setupTabPanels();
- };
-
- var setupTabList = function () {
- tablist.setAttribute("role", "tablist");
- if (orientation == 'vertical') tablist.setAttribute("aria-orientation", "vertical");
- }
-
- var setupTabs = function () {
-
- tabs.forEach((tab, index) => {
- tab.setAttribute('role', 'tab');
- // each tab needs an ID that will be used to label its corresponding panel
- tab.setAttribute('id', tabsID + '__tab-' + index);
- tab.setAttribute('data-controls', tabpanels[index].getAttribute('id'));
-
- // first tab is initially active
- if (index === currentIndex) {
- selectTab(tab);
- // updateUrlHash();
- }
-
- if (tab.getAttribute('data-controls') === util.getUrlHash()) {
- currentIndex = index;
- selectedTab = index;
- selectTab(tab);
- }
-
- tab.addEventListener('click', (e) => {
- e.preventDefault();
- currentIndex = index;
- selectedTab = index;
- focusCurrentTab();
- selectTab(tab);
- // updateUrlHash();
- }, false);
-
- tab.addEventListener('keydown', (e) => {
- tabKeyboardRespond(e, tab);
- }, false);
- });
- }
-
- var focusCurrentTab = function () {
- tabs[currentIndex].focus();
- }
-
- var updateUrlHash = function () {
- var active = tabs[selectedTab];
- util.setUrlHash(active.getAttribute('data-controls'));
- };
-
- var selectTab = function (tab) {
- // unactivate all other tabs
- tabs.forEach(tab => {
- tab.setAttribute('aria-selected', 'false');
- tab.setAttribute('tabindex', '-1');
- });
- //activate current tab
- tab.setAttribute('aria-selected', 'true');
- tab.setAttribute('tabindex', '0');
-
- // activate corresponding panel
- showTabpanel(tab);
- }
-
- var setupTabPanels = function () {
- tabpanels.forEach((tabpanel, index) => {
- tabpanel.setAttribute('role', 'tabpanel');
- tabpanel.setAttribute('tabindex', '-1');
- tabpanel.setAttribute('hidden', '');
-
- if (index == currentIndex) {
- tabpanel.removeAttribute('hidden');
- }
-
- tabpanel.addEventListener('keydown', (e) => {
- panelKeyboardRespond(e);
- }, false);
-
- tabpanel.addEventListener("blur", () => {
- tabpanel.setAttribute('tabindex', '-1');
- }, false);
- });
- }
-
-
- var panelKeyboardRespond = function (e) {
- var keyCode = e.keyCode || e.which;
-
- switch (keyCode) {
- case util.keyCodes.TAB:
- tabpanels[currentIndex].setAttribute('tabindex', '-1');
- break;
-
- default:
- break;
- }
- }
-
-
- var showTabpanel = function (tab) {
- tabpanels.forEach((tabpanel, index) => {
- tabpanel.setAttribute('hidden', '');
- tabpanel.removeAttribute('tabindex');
-
- if (index == currentIndex) {
- tabpanel.removeAttribute('hidden');
- tabpanel.setAttribute('aria-labelledby', tabs[currentIndex].getAttribute('id'));
- tabpanel.setAttribute('tabindex', '0');
- }
- });
- }
-
- var incrementcurrentIndex = function () {
- if (currentIndex < tabs.length - 1) {
- return ++currentIndex;
- }
- else {
- currentIndex = 0;
- return currentIndex;
- }
- };
-
-
- var decrementcurrentIndex = function () {
- if (currentIndex > 0) {
- return --currentIndex;
- }
- else {
- currentIndex = tabs.length - 1;
- return currentIndex;
- }
- };
-
-
-
- var tabKeyboardRespond = function (e, tab) {
- var firstTab = tabs[0];
- var lastTab = tabs[tabs.length - 1];
-
- var keyCode = e.keyCode || e.which;
-
- switch (keyCode) {
- case util.keyCodes.UP:
- case util.keyCodes.LEFT:
- e.preventDefault();
- decrementcurrentIndex();
- focusCurrentTab();
-
- if (!manual) {
- selectedTab = currentIndex;
- selectTab(tabs[selectedTab]);
- // updateUrlHash();
- }
-
- break;
-
-
- case util.keyCodes.DOWN:
- case util.keyCodes.RIGHT:
- e.preventDefault();
- incrementcurrentIndex();
- focusCurrentTab();
-
- if (!manual) {
- selectedTab = currentIndex;
- selectTab(tabs[selectedTab]);
- // updateUrlHash();
- }
-
- break;
-
-
- case util.keyCodes.ENTER:
- case util.keyCodes.SPACE:
- e.preventDefault();
- selectedTab = currentIndex;
- selectTab(tabs[selectedTab]);
- // updateUrlHash();
-
- break;
-
-
- case util.keyCodes.TAB:
- tabpanels[selectedTab].setAttribute('tabindex', '0');
- currentIndex = selectedTab;
-
- break;
-
-
- case util.keyCodes.HOME:
- e.preventDefault();
- firstTab.focus();
- // updateUrlHash();
-
- break;
-
-
- case util.keyCodes.END:
- e.preventDefault();
- lastTab.focus();
- // updateUrlHash();
-
- break;
- }
-
- }
-
- init.call(this);
- return this;
- }; // ARIAtabs()
-
- w.ARIAtabs = ARIAtabs;
-
+ var ARIAaccOptions = {
+ manual: true,
+ open: 0,
+ };
+
+ var ARIAtabs = function (inst, options) {
+ var _options = Object.assign(ARIAaccOptions, options);
+ var el = inst;
+ var tablist = el.querySelector("[data-tablist]");
+ var tabs = Array.from(el.querySelectorAll("[data-tab]"));
+ var tabpanels = Array.from(el.querySelectorAll("[data-tabpanel]"));
+ var tabsID = util.generateID("ps__tabs-");
+ var orientation = el.getAttribute("data-tabs-orientation");
+ var currentIndex = _options.open;
+ var selectedTab = currentIndex;
+ var manual = _options.manual;
+
+ el.setAttribute("id", tabsID);
+
+ var init = function () {
+ el.classList.add("js-tabs");
+ tablist.removeAttribute("hidden");
+ setupTabList();
+ setupTabs();
+ setupTabPanels();
+ };
+
+ var setupTabList = function () {
+ tablist.setAttribute("role", "tablist");
+ if (orientation == "vertical")
+ tablist.setAttribute("aria-orientation", "vertical");
+ };
+
+ var setupTabs = function () {
+ tabs.forEach((tab, index) => {
+ tab.setAttribute("role", "tab");
+ // each tab needs an ID that will be used to label its corresponding panel
+ tab.setAttribute("id", tabsID + "__tab-" + index);
+ tab.setAttribute(
+ "data-controls",
+ tabpanels[index].getAttribute("id"),
+ );
+
+ // first tab is initially active
+ if (index === currentIndex) {
+ selectTab(tab);
+ // updateUrlHash();
+ }
+
+ if (tab.getAttribute("data-controls") === util.getUrlHash()) {
+ currentIndex = index;
+ selectedTab = index;
+ selectTab(tab);
+ }
+
+ tab.addEventListener(
+ "click",
+ e => {
+ e.preventDefault();
+ currentIndex = index;
+ selectedTab = index;
+ focusCurrentTab();
+ selectTab(tab);
+ // updateUrlHash();
+ },
+ false,
+ );
+
+ tab.addEventListener(
+ "keydown",
+ e => {
+ tabKeyboardRespond(e, tab);
+ },
+ false,
+ );
+ });
+ };
+
+ var focusCurrentTab = function () {
+ tabs[currentIndex].focus();
+ };
+
+ var updateUrlHash = function () {
+ var active = tabs[selectedTab];
+ util.setUrlHash(active.getAttribute("data-controls"));
+ };
+
+ var selectTab = function (tab) {
+ // unactivate all other tabs
+ tabs.forEach(tab => {
+ tab.setAttribute("aria-selected", "false");
+ tab.setAttribute("tabindex", "-1");
+ });
+ //activate current tab
+ tab.setAttribute("aria-selected", "true");
+ tab.setAttribute("tabindex", "0");
+
+ // activate corresponding panel
+ showTabpanel(tab);
+ };
+
+ var setupTabPanels = function () {
+ tabpanels.forEach((tabpanel, index) => {
+ tabpanel.setAttribute("role", "tabpanel");
+ tabpanel.setAttribute("tabindex", "-1");
+ tabpanel.setAttribute("hidden", "");
+
+ if (index == currentIndex) {
+ tabpanel.removeAttribute("hidden");
+ }
+
+ tabpanel.addEventListener(
+ "keydown",
+ e => {
+ panelKeyboardRespond(e);
+ },
+ false,
+ );
+
+ tabpanel.addEventListener(
+ "blur",
+ () => {
+ tabpanel.setAttribute("tabindex", "-1");
+ },
+ false,
+ );
+ });
+ };
+
+ var panelKeyboardRespond = function (e) {
+ var keyCode = e.keyCode || e.which;
+
+ switch (keyCode) {
+ case util.keyCodes.TAB:
+ tabpanels[currentIndex].setAttribute("tabindex", "-1");
+ break;
+
+ default:
+ break;
+ }
+ };
+
+ var showTabpanel = function (tab) {
+ tabpanels.forEach((tabpanel, index) => {
+ tabpanel.setAttribute("hidden", "");
+ tabpanel.removeAttribute("tabindex");
+
+ if (index == currentIndex) {
+ tabpanel.removeAttribute("hidden");
+ tabpanel.setAttribute(
+ "aria-labelledby",
+ tabs[currentIndex].getAttribute("id"),
+ );
+ tabpanel.setAttribute("tabindex", "0");
+ }
+ });
+ };
+
+ var incrementcurrentIndex = function () {
+ if (currentIndex < tabs.length - 1) {
+ return ++currentIndex;
+ } else {
+ currentIndex = 0;
+ return currentIndex;
+ }
+ };
+
+ var decrementcurrentIndex = function () {
+ if (currentIndex > 0) {
+ return --currentIndex;
+ } else {
+ currentIndex = tabs.length - 1;
+ return currentIndex;
+ }
+ };
+
+ var tabKeyboardRespond = function (e, tab) {
+ var firstTab = tabs[0];
+ var lastTab = tabs[tabs.length - 1];
+
+ var keyCode = e.keyCode || e.which;
+
+ switch (keyCode) {
+ case util.keyCodes.UP:
+ case util.keyCodes.LEFT:
+ e.preventDefault();
+ decrementcurrentIndex();
+ focusCurrentTab();
+
+ if (!manual) {
+ selectedTab = currentIndex;
+ selectTab(tabs[selectedTab]);
+ // updateUrlHash();
+ }
+
+ break;
+
+ case util.keyCodes.DOWN:
+ case util.keyCodes.RIGHT:
+ e.preventDefault();
+ incrementcurrentIndex();
+ focusCurrentTab();
+
+ if (!manual) {
+ selectedTab = currentIndex;
+ selectTab(tabs[selectedTab]);
+ // updateUrlHash();
+ }
+
+ break;
+
+ case util.keyCodes.ENTER:
+ case util.keyCodes.SPACE:
+ e.preventDefault();
+ selectedTab = currentIndex;
+ selectTab(tabs[selectedTab]);
+ // updateUrlHash();
+
+ break;
+
+ case util.keyCodes.TAB:
+ tabpanels[selectedTab].setAttribute("tabindex", "0");
+ currentIndex = selectedTab;
+
+ break;
+
+ case util.keyCodes.HOME:
+ e.preventDefault();
+ firstTab.focus();
+ // updateUrlHash();
+
+ break;
+
+ case util.keyCodes.END:
+ e.preventDefault();
+ lastTab.focus();
+ // updateUrlHash();
+
+ break;
+ }
+ };
+
+ init.call(this);
+ return this;
+ }; // ARIAtabs()
+
+ w.ARIAtabs = ARIAtabs;
})(window, document);
-
var tabsInstance = "[data-tabs]";
var els = document.querySelectorAll(tabsInstance);
var allTabs = [];
// Generate all tabs instances
for (var i = 0; i < els.length; i++) {
- var nTabs = new ARIAtabs(els[i], { manual: true }); // if manual is set to false, the tabs open on focus without needing an ENTER or SPACE press
- allTabs.push(nTabs);
+ var nTabs = new ARIAtabs(els[i], { manual: true }); // if manual is set to false, the tabs open on focus without needing an ENTER or SPACE press
+ allTabs.push(nTabs);
}
diff --git a/docs/src/assets/js/themes.js b/docs/src/assets/js/themes.js
index e6071b21983a..d63c9d4b4b6b 100644
--- a/docs/src/assets/js/themes.js
+++ b/docs/src/assets/js/themes.js
@@ -1,48 +1,78 @@
-/* theme toggle buttons */
-(function() {
- var enableToggle = function(btn) {
- btn.setAttribute("aria-pressed", "true");
- }
-
- var disableToggle = function(btn) {
- btn.setAttribute("aria-pressed", "false");
- }
-
- document.addEventListener('DOMContentLoaded', function() {
- var switcher = document.getElementById('js-theme-switcher');
- switcher.removeAttribute('hidden');
-
- var light_theme_toggle = document.getElementById('light-theme-toggle'),
- dark_theme_toggle = document.getElementById('dark-theme-toggle');
-
- // get any previously-chosen themes
- var theme = document.documentElement.getAttribute('data-theme');
-
- if (theme == "light") {
- enableToggle(light_theme_toggle);
- disableToggle(dark_theme_toggle);
- } else if (theme == "dark") {
- enableToggle(dark_theme_toggle);
- disableToggle(light_theme_toggle);
- }
-
- light_theme_toggle.addEventListener("click", function() {
- enableToggle(light_theme_toggle);
- theme = this.getAttribute('data-theme');
- document.documentElement.setAttribute('data-theme', theme);
- window.localStorage.setItem("theme", theme);
-
- disableToggle(dark_theme_toggle);
- }, false);
-
- dark_theme_toggle.addEventListener("click", function() {
- enableToggle(dark_theme_toggle);
- theme = this.getAttribute('data-theme');
- document.documentElement.setAttribute('data-theme', theme);
- window.localStorage.setItem("theme", theme);
-
- disableToggle(light_theme_toggle);
- }, false);
- }, false);
+(function () {
+ var enableToggle = function (btn) {
+ btn.setAttribute("aria-pressed", "true");
+ };
+ var disableToggle = function (btns) {
+ btns.forEach(btn => btn.setAttribute("aria-pressed", "false"));
+ };
+
+ var setTheme = function (theme) {
+ if (theme === "system") {
+ var systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
+ .matches
+ ? "dark"
+ : "light";
+ document.documentElement.setAttribute("data-theme", systemTheme);
+ } else {
+ document.documentElement.setAttribute("data-theme", theme);
+ }
+ window.localStorage.setItem("theme", theme);
+ };
+
+ var initializeThemeSwitcher = function () {
+ var theme = window.localStorage.getItem("theme") || "system";
+ var switcher = document.getElementById("js-theme-switcher");
+ switcher.removeAttribute("hidden");
+
+ var lightThemeToggle = document.getElementById("light-theme-toggle");
+ var darkThemeToggle = document.getElementById("dark-theme-toggle");
+ var systemThemeToggle = document.getElementById("system-theme-toggle");
+
+ var toggleButtons = [
+ lightThemeToggle,
+ darkThemeToggle,
+ systemThemeToggle,
+ ];
+
+ toggleButtons.forEach(function (btn) {
+ btn.addEventListener("click", function () {
+ enableToggle(btn);
+ var theme = this.getAttribute("data-theme");
+ setTheme(theme);
+ if (btn === systemThemeToggle) {
+ disableToggle([lightThemeToggle, darkThemeToggle]);
+ } else if (btn === lightThemeToggle) {
+ disableToggle([systemThemeToggle, darkThemeToggle]);
+ } else if (btn === darkThemeToggle) {
+ disableToggle([systemThemeToggle, lightThemeToggle]);
+ }
+ });
+ });
+
+ if (theme === "system") {
+ enableToggle(systemThemeToggle);
+ disableToggle([lightThemeToggle, darkThemeToggle]);
+ } else if (theme === "light") {
+ enableToggle(lightThemeToggle);
+ disableToggle([systemThemeToggle, darkThemeToggle]);
+ } else if (theme === "dark") {
+ enableToggle(darkThemeToggle);
+ disableToggle([systemThemeToggle, lightThemeToggle]);
+ }
+
+ // Update theme on system preference change
+ window
+ .matchMedia("(prefers-color-scheme: dark)")
+ .addEventListener("change", function () {
+ var currentTheme = window.localStorage.getItem("theme");
+ if (currentTheme === "system" || !currentTheme) {
+ enableToggle(systemThemeToggle);
+ disableToggle([lightThemeToggle, darkThemeToggle]);
+ setTheme("system");
+ }
+ });
+ };
+
+ document.addEventListener("DOMContentLoaded", initializeThemeSwitcher);
})();
diff --git a/docs/src/assets/scss/ads.scss b/docs/src/assets/scss/ads.scss
new file mode 100644
index 000000000000..cc4ed68030c5
--- /dev/null
+++ b/docs/src/assets/scss/ads.scss
@@ -0,0 +1,154 @@
+.hero-ad {
+ @media all and (max-width: 800px) {
+ display: none;
+ }
+}
+
+.docs-ad {
+ height: 290px;
+}
+
+/*
+ * Carbon Ads
+ * https://www.carbonads.net/
+ */
+
+#carbonads * {
+ margin: initial;
+ padding: initial;
+}
+
+#carbonads {
+ display: inline-block;
+ margin: 2rem 0;
+ padding: 0.6em;
+ font-size: 1rem;
+ overflow: hidden;
+ background-color: var(--body-background-color);
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ border-radius: var(--border-radius);
+ box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, 0.1);
+
+ .docs-main & {
+ margin: 0 0 2rem;
+ }
+
+ @media all and (max-width: 800px) {
+ display: none !important;
+ }
+}
+
+.jumbotron #carbonads {
+ border: solid 1px hsla(250, 20%, 50%, 0.6);
+ background-color: hsla(0, 0%, 70%, 0.15);
+}
+
+#carbonads a {
+ font-weight: 500;
+ color: inherit;
+ text-decoration: none;
+}
+
+#carbonads a:hover {
+ text-decoration: none;
+ color: var(--link-color);
+}
+
+.jumbotron #carbonads a {
+ color: #eee;
+}
+
+.jumbotron #carbonads a:hover {
+ color: #ccc;
+}
+
+#carbonads span {
+ display: block;
+ position: relative;
+ overflow: hidden;
+}
+
+#carbonads .carbon-wrap {
+ display: flex;
+ flex-direction: column;
+ max-width: 130px;
+}
+
+#carbonads .carbon-img img {
+ display: block;
+}
+
+#carbonads .carbon-text {
+ margin-top: 10px;
+ line-height: 1rem;
+ font-size: 0.7em;
+ font-weight: 500;
+ text-align: left;
+}
+
+#carbonads .carbon-poweredby {
+ display: block;
+ margin-top: 10px;
+ font-size: 0.5rem;
+ font-weight: 500;
+ line-height: 1;
+ letter-spacing: 0.1ch;
+ text-transform: uppercase;
+}
+
+@media only screen and (min-width: 320px) and (max-width: 759px) {
+ #carbonads {
+ margin-top: 0;
+ font-size: 12px;
+ }
+
+ #carbonads .carbon-wrap {
+ display: flex;
+ flex-direction: row;
+ max-width: 330px;
+ }
+
+ #carbonads .carbon-text {
+ margin: 0 0 14px 10px;
+ font-size: 14px;
+ text-align: left;
+ }
+
+ #carbonads .carbon-poweredby {
+ position: absolute;
+ bottom: 0;
+ left: 142px;
+ font-size: 8px;
+ }
+}
+
+/*
+ * Ethical Ads
+ */
+
+[data-ea-publisher].loaded .ea-content,
+[data-ea-type].loaded .ea-content {
+ background-color: var(--body-background-color) !important;
+ border: 1px solid var(--border-color) !important;
+}
+
+[data-ea-publisher].loaded .ea-content a:link,
+[data-ea-type].loaded .ea-content a:link {
+ color: var(--body-text-color) !important;
+}
+
+[data-ea-publisher].loaded .ea-callout a:link,
+[data-ea-type].loaded .ea-callout a:link {
+ color: var(--body-text-color) !important;
+}
+
+.jumbotron [data-ea-publisher].loaded .ea-content a,
+.jumbotron [data-ea-type].loaded .ea-content a {
+ color: #eee;
+}
+
+.jumbotron [data-ea-publisher].loaded .ea-content a:hover,
+.jumbotron [data-ea-type].loaded .ea-content a:hover {
+ color: #ccc;
+}
diff --git a/docs/src/assets/scss/carbon-ads.scss b/docs/src/assets/scss/carbon-ads.scss
deleted file mode 100644
index bd7ea8e660cb..000000000000
--- a/docs/src/assets/scss/carbon-ads.scss
+++ /dev/null
@@ -1,115 +0,0 @@
-.hero-ad {
- @media all and (max-width: 800px) {
- display: none;
- }
-}
-
-#carbonads * {
- margin: initial;
- padding: initial;
-}
-
-#carbonads {
- display: inline-block;
- margin: 2rem 0;
- padding: .6em;
- font-size: 1rem;
- overflow: hidden;
- background-color: var(--body-background-color);
- border: 1px solid var(--border-color);
- border-radius: 4px;
- border-radius: var(--border-radius);
- box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, 0.1);
-
- .docs-main & {
- margin: 0 0 2rem;
- }
-
- @media all and (max-width: 800px) {
- display: none !important;
- }
-}
-
-.jumbotron #carbonads {
- border: solid 1px hsla(250, 20%, 50%, 0.6);
- background-color: hsla(0, 0%, 70%, 0.15);
-}
-
-#carbonads a {
- font-weight: 500;
- color: inherit;
- text-decoration: none;
-}
-
-#carbonads a:hover {
- text-decoration: none;
- color: var(--link-color);
-}
-
-.jumbotron #carbonads a {
- color: #eee;
-}
-
-.jumbotron #carbonads a:hover {
- color: #ccc;
-}
-
-#carbonads span {
- display: block;
- position: relative;
- overflow: hidden;
-}
-
-#carbonads .carbon-wrap {
- display: flex;
- flex-direction: column;
- max-width: 130px;
-}
-
-#carbonads .carbon-img img {
- display: block;
-}
-
-#carbonads .carbon-text {
- margin-top: 10px;
- line-height: 1rem;
- font-size: .7em;
- font-weight: 500;
- text-align: left;
-}
-
-#carbonads .carbon-poweredby {
- display: block;
- margin-top: 10px;
- font-size: 0.5rem;
- font-weight: 500;
- line-height: 1;
- letter-spacing: .1ch;
- text-transform: uppercase;
-}
-
-@media only screen and (min-width: 320px) and (max-width: 759px) {
- #carbonads {
- margin-top: 0;
- font-size: 12px;
- }
-
- #carbonads .carbon-wrap {
- display: flex;
- flex-direction: row;
- max-width: 330px;
- }
-
- #carbonads .carbon-text {
- margin: 0 0 14px 10px;
- font-size: 14px;
- text-align: left;
- }
-
- #carbonads .carbon-poweredby {
- position: absolute;
- bottom: 0;
- left: 142px;
- font-size: 8px;
- }
-}
diff --git a/docs/src/assets/scss/components/alert.scss b/docs/src/assets/scss/components/alert.scss
index 8235c1429c90..1f61eeec3d68 100644
--- a/docs/src/assets/scss/components/alert.scss
+++ b/docs/src/assets/scss/components/alert.scss
@@ -1,119 +1,89 @@
.alert {
- position: relative;
- display: grid;
- grid-template-columns: auto 1fr;
- padding: 1rem;
- gap: .75rem;
- margin-bottom: 1.5rem;
- margin-block-end: 1.5rem;
- align-items: start;
- font-size: .875rem;
- border: 1px solid currentColor;
- border-radius: var(--border-radius);
-
- &.alert--warning {
- background-color: var(--color-rose-25);
- color: var(--color-rose-600);
-
- [data-theme="dark"] & {
- border: 1px solid var(--color-rose-300);
- color: var(--color-rose-300);
- background-color: var(--color-rose-900);
- }
- }
-
- &.alert--important {
- background-color: var(--color-warning-25);
- color: var(--color-warning-600);
-
- [data-theme="dark"] & {
- color: var(--color-warning-300);
- border: 1px solid var(--color-warning-300);
- background-color: var(--color-warning-900);
- }
- }
-
- &.alert--tip {
- background-color: var(--color-success-25);
- color: var(--color-success-600);
-
- [data-theme="dark"] & {
- color: var(--color-success-300);
- border: 1px solid var(--color-success-300);
- background-color: var(--color-success-900);
- }
- }
+ position: relative;
+ display: grid;
+ grid-template-columns: auto 1fr;
+ padding: 1rem;
+ gap: 0.75rem;
+ margin-bottom: 1.5rem;
+ margin-block-end: 1.5rem;
+ align-items: start;
+ font-size: 0.875rem;
+ border: 1px solid currentColor;
+ border-radius: var(--border-radius);
+
+ &.alert--warning {
+ background-color: var(--alert-warning-background-color);
+ color: var(--alert-warning-color);
+ }
+
+ &.alert--important {
+ background-color: var(--alert-important-background-color);
+ color: var(--alert-important-color);
+ }
+
+ &.alert--tip {
+ background-color: var(--alert-tip-background-color);
+ color: var(--alert-tip-color);
+ }
}
.alert__icon {
- color: inherit;
- position: relative;
- top: 2px;
- offset-block-start: 2px;
+ color: inherit;
+ position: relative;
+ top: 2px;
+ offset-block-start: 2px;
}
.alert__text > p {
- margin: 0;
+ margin: 0;
}
.alert__type {
- display: block;
- font-weight: 500;
- margin-bottom: .25rem;
- margin-block-end: .25rem;
-
- .alert--warning & {
- color: var(--color-rose-700);
-
- [data-theme="dark"] & {
- color: var(--color-rose-200);
- }
- }
-
- .alert--important & {
- color: var(--color-warning-700);
-
- [data-theme="dark"] & {
- color: var(--color-warning-200);
- }
- }
-
- .alert--tip & {
- color: var(--color-success-700);
-
- [data-theme="dark"] & {
- color: var(--color-success-200);
- }
- }
+ display: block;
+ font-weight: 500;
+ margin-bottom: 0.25rem;
+ margin-block-end: 0.25rem;
+
+ .alert--warning & {
+ color: var(--alert-warning-heading-color);
+ }
+
+ .alert--important & {
+ color: var(--alert-important-heading-color);
+ }
+
+ .alert--tip & {
+ color: var(--alert-tip-heading-color);
+ }
}
.alert__learn-more {
- display: block;
- font-weight: 500;
- margin-top: .75rem;
- margin-block-start: .75rem;
-
- .alert--warning & {
- color: var(--color-rose-700);
-
- [data-theme="dark"] & {
- color: var(--color-rose-200);
- }
- }
-
- .alert--important & {
- color: var(--color-warning-700);
-
- [data-theme="dark"] & {
- color: var(--color-warning-200);
- }
- }
-
- .alert--tip & {
- color: var(--color-success-700);
-
- [data-theme="dark"] & {
- color: var(--color-success-200);
- }
- }
+ display: block;
+ font-weight: 500;
+ margin-top: 0.75rem;
+ margin-block-start: 0.75rem;
+
+ .alert--warning & {
+ color: var(--color-rose-700);
+
+ [data-theme="dark"] & {
+ color: var(--color-rose-200);
+ }
+ }
+
+ .alert--important & {
+ color: var(--color-warning-700);
+
+ [data-theme="dark"] & {
+ color: var(--color-warning-200);
+ }
+ }
+
+ .alert--tip & {
+ color: var(--color-success-700);
+
+ [data-theme="dark"] & {
+ color: var(--color-success-200);
+ }
+ }
}
diff --git a/docs/src/assets/scss/components/buttons.scss b/docs/src/assets/scss/components/buttons.scss
index ca0aa72a726c..f081cf87c632 100644
--- a/docs/src/assets/scss/components/buttons.scss
+++ b/docs/src/assets/scss/components/buttons.scss
@@ -1,77 +1,79 @@
button {
- border: none;
- background: none;
- font: inherit;
- cursor: pointer;
- line-height: inherit;
- display: inline-flex;
- align-items: center;
- justify-content: center;
+ border: none;
+ background: none;
+ font: inherit;
+ cursor: pointer;
+ line-height: inherit;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
}
.c-btn {
- background: none;
- border: none;
- font: inherit;
- font-family: var(--text-font);
- cursor: pointer;
- line-height: inherit;
- font-weight: 500;
- font-size: var(--step-0);
- display: inline-flex;
- padding: .75em 1.125em;
- align-items: center;
- justify-content: center;
- border-radius: var(--border-radius);
- transition: background-color .2s linear, border-color .2s linear;
+ background: none;
+ border: none;
+ font: inherit;
+ font-family: var(--text-font);
+ cursor: pointer;
+ line-height: inherit;
+ font-weight: 500;
+ font-size: var(--step-0);
+ display: inline-flex;
+ padding: 0.75em 1.125em;
+ align-items: center;
+ justify-content: center;
+ border-radius: var(--border-radius);
+ transition:
+ background-color 0.2s linear,
+ border-color 0.2s linear;
- svg {
- color: inherit;
- }
+ svg {
+ color: inherit;
+ }
}
.c-btn--large {
- font-size: 1.125rem;
- padding: .88em 1.5em;
+ font-size: 1.125rem;
+ padding: 0.88em 1.5em;
}
.c-btn--block {
- display: flex;
- width: 100%;
+ display: flex;
+ width: 100%;
}
a.c-btn {
- text-decoration: none;
- display: inline-flex;
- flex-wrap: wrap;
- gap: .5rem;
- align-items: center;
+ text-decoration: none;
+ display: inline-flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ align-items: center;
}
.c-btn--primary {
- background-color: var(--primary-button-background-color);
- color: var(--primary-button-text-color);
+ background-color: var(--primary-button-background-color);
+ color: var(--primary-button-text-color);
- &:hover {
- background-color: var(--primary-button-hover-color);
- }
+ &:hover {
+ background-color: var(--primary-button-hover-color);
+ }
}
.c-btn--secondary {
- background-color: var(--secondary-button-background-color);
- color: var(--secondary-button-text-color);
- box-shadow: 0 1px 2px rgba(16, 24, 40, 0.1);
+ background-color: var(--secondary-button-background-color);
+ color: var(--secondary-button-text-color);
+ box-shadow: 0 1px 2px rgba(16, 24, 40, 0.1);
- &:hover {
- background-color: var(--secondary-button-hover-color);
- }
+ &:hover {
+ background-color: var(--secondary-button-hover-color);
+ }
}
.c-btn--ghost {
- color: var(--body-text-color);
- border: 1px solid var(--border-color);
+ color: var(--body-text-color);
+ border: 1px solid var(--border-color);
- &:hover {
- border-color: var(--link-color);
- }
+ &:hover {
+ border-color: var(--link-color);
+ }
}
diff --git a/docs/src/assets/scss/components/docs-index.scss b/docs/src/assets/scss/components/docs-index.scss
index 22e156eb39d8..0e0abbe4d911 100644
--- a/docs/src/assets/scss/components/docs-index.scss
+++ b/docs/src/assets/scss/components/docs-index.scss
@@ -1,164 +1,164 @@
.docs-index .docs-index__list {
- a {
- border-radius: var(--border-radius);
- text-decoration: none;
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: .5rem .75rem;
- margin-left: -.75rem;
- margin-inline-start: -.75rem;
- color: var(--headings-color);
-
- &:hover,
- &[aria-current="true"] {
- background-color: var(--docs-lightest-background-color);
- color: var(--link-color);
- }
-
- @media all and (max-width: 1023px) {
- padding: .5rem 1rem;
- margin-left: 0;
- margin-inline-start: 0;
- }
- }
+ a {
+ border-radius: var(--border-radius);
+ text-decoration: none;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.5rem 0.75rem;
+ margin-left: -0.75rem;
+ margin-inline-start: -0.75rem;
+ color: var(--headings-color);
+
+ &:hover,
+ &[aria-current="true"] {
+ background-color: var(--docs-lightest-background-color);
+ color: var(--link-color);
+ }
+
+ @media all and (max-width: 1023px) {
+ padding: 0.5rem 1rem;
+ margin-left: 0;
+ margin-inline-start: 0;
+ }
+ }
}
.docs-index__item {
- margin: 0;
+ margin: 0;
- ul ul {
- padding-left: .75rem;
- }
+ ul ul {
+ padding-left: 0.75rem;
+ }
- &[data-has-children] {
- margin-bottom: .5rem;
- }
+ &[data-has-children] {
+ margin-bottom: 0.5rem;
+ }
}
.docs-index__list > .docs-index__item {
- margin-top: 1.5rem;
- margin-block-start: 1.5rem;
-
- > a {
- color: var(--icon-color);
- text-transform: uppercase;
- letter-spacing: 1px;
- font-size: .875rem;
- font-weight: 500;
- }
+ margin-top: 1.5rem;
+ margin-block-start: 1.5rem;
+
+ > a {
+ color: var(--icon-color);
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ font-size: 0.875rem;
+ font-weight: 500;
+ }
}
/* Styles for the accordion icon */
.index-js .index-icon {
- display: block !important;
- width: 0.75rem;
- height: 0.5rem;
- transform-origin: 50% 50%;
- transition: all 0.1s linear;
- color: inherit;
+ display: block !important;
+ width: 0.75rem;
+ height: 0.5rem;
+ transform-origin: 50% 50%;
+ transition: all 0.1s linear;
+ color: inherit;
}
.index-js [aria-expanded="true"] .index-icon {
- transform: rotate(180deg);
+ transform: rotate(180deg);
}
.index-js ul[aria-hidden="true"] {
- display: none;
+ display: none;
}
.index-js ul[aria-hidden="false"] {
- display: block;
+ display: block;
}
.docs__index__panel {
- &[data-open="false"] {
- display: none;
+ &[data-open="false"] {
+ display: none;
- @media all and (min-width: 1024px) {
- display: block;
- }
- }
+ @media all and (min-width: 1024px) {
+ display: block;
+ }
+ }
- &[data-open="true"] {
- display: block;
+ &[data-open="true"] {
+ display: block;
- @media all and (min-width: 1024px) {
- display: block;
- }
- }
+ @media all and (min-width: 1024px) {
+ display: block;
+ }
+ }
}
.docs-index-toggle {
- cursor: pointer;
- display: flex;
- width: 100%;
- padding: .75rem 1.125rem;
- align-items: center;
- justify-content: space-between;
- gap: .5rem;
- font-weight: 500;
- border: 1px solid var(--border-color);
- border-radius: var(--border-radius);
- background-color: var(--secondary-button-background-color);
- color: var(--secondary-button-text-color);
- box-shadow: 0 1px 2px rgba(16, 24, 40, 0.1);
-
- &:hover {
- background-color: var(--secondary-button-hover-color);
- }
-
- @media all and (min-width: 1024px) {
- display: none;
- }
-
- svg {
- width: 1.5em;
- height: 1.5em;
- color: inherit;
- fill: none;
- stroke-width: 4;
- stroke-linecap: round;
- stroke-linejoin: round;
- }
-
- #ham-top,
- #ham-middle,
- #ham-bottom {
- transition: all .2s linear;
- }
-
- #ham-top {
- transform-origin: 30px 37px;
- }
-
- #ham-bottom {
- transform-origin: 30px 63px;
- }
-
- &[aria-expanded="true"] {
- #ham-middle {
- opacity: 0;
- }
-
- #ham-top {
- transform: rotate(41deg);
- }
-
- #ham-bottom {
- transform: rotate(-41deg);
- }
- }
+ cursor: pointer;
+ display: flex;
+ width: 100%;
+ padding: 0.75rem 1.125rem;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.5rem;
+ font-weight: 500;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ background-color: var(--secondary-button-background-color);
+ color: var(--secondary-button-text-color);
+ box-shadow: 0 1px 2px rgba(16, 24, 40, 0.1);
+
+ &:hover {
+ background-color: var(--secondary-button-hover-color);
+ }
+
+ @media all and (min-width: 1024px) {
+ display: none;
+ }
+
+ svg {
+ width: 1.5em;
+ height: 1.5em;
+ color: inherit;
+ fill: none;
+ stroke-width: 4;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ }
+
+ #ham-top,
+ #ham-middle,
+ #ham-bottom {
+ transition: all 0.2s linear;
+ }
+
+ #ham-top {
+ transform-origin: 30px 37px;
+ }
+
+ #ham-bottom {
+ transform-origin: 30px 63px;
+ }
+
+ &[aria-expanded="true"] {
+ #ham-middle {
+ opacity: 0;
+ }
+
+ #ham-top {
+ transform: rotate(41deg);
+ }
+
+ #ham-bottom {
+ transform: rotate(-41deg);
+ }
+ }
}
.eslint-actions {
- display: inline-flex;
- flex-wrap: wrap;
- flex-direction: column;
- width: 100%;
- gap: 1rem;
-
- @media all and (min-width: 640px) {
- flex-direction: row;
- }
+ display: inline-flex;
+ flex-wrap: wrap;
+ flex-direction: column;
+ width: 100%;
+ gap: 1rem;
+
+ @media all and (min-width: 640px) {
+ flex-direction: row;
+ }
}
diff --git a/docs/src/assets/scss/components/docs-navigation.scss b/docs/src/assets/scss/components/docs-navigation.scss
index f47fce3a0a50..900fa1a3eee5 100644
--- a/docs/src/assets/scss/components/docs-navigation.scss
+++ b/docs/src/assets/scss/components/docs-navigation.scss
@@ -1,147 +1,147 @@
.docs-site-nav {
- display: flex;
- flex-direction: column;
- flex: 1;
- grid-column: 1 / -1;
- grid-row: 1;
-
- ul {
- list-style: none;
- font-size: var(--step-1);
- margin-top: 1rem;
- margin-block-start: 1rem;
- margin-bottom: 2rem;
- margin-block-end: 2rem;
-
- @media all and (min-width: 1024px) {
- font-size: var(--step-0);
- margin-top: 0;
- margin-block-start: 0;
- margin-bottom: 0;
- margin-block-end: 0;
- align-items: center;
- display: flex;
- }
- }
-
- .flexer {
- display: flex;
- justify-self: flex-end;
- align-self: flex-end;
- }
-
- a:not(.c-btn) {
- text-decoration: none;
- color: inherit;
- transition: color .2s linear;
- display: block;
-
- &:hover {
- color: var(--link-color);
- }
- }
-
- a:not(.c-btn)[aria-current="page"],
- a:not(.c-btn)[aria-current="true"] {
- color: var(--link-color);
- text-decoration: none;
- font-weight: 500;
- }
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ grid-column: 1 / -1;
+ grid-row: 1;
+
+ ul {
+ list-style: none;
+ font-size: var(--step-1);
+ margin-top: 1rem;
+ margin-block-start: 1rem;
+ margin-bottom: 2rem;
+ margin-block-end: 2rem;
+
+ @media all and (min-width: 1024px) {
+ font-size: var(--step-0);
+ margin-top: 0;
+ margin-block-start: 0;
+ margin-bottom: 0;
+ margin-block-end: 0;
+ align-items: center;
+ display: flex;
+ }
+ }
+
+ .flexer {
+ display: flex;
+ justify-self: flex-end;
+ align-self: flex-end;
+ }
+
+ a:not(.c-btn) {
+ text-decoration: none;
+ color: inherit;
+ transition: color 0.2s linear;
+ display: block;
+
+ &:hover {
+ color: var(--link-color);
+ }
+ }
+
+ a:not(.c-btn)[aria-current="page"],
+ a:not(.c-btn)[aria-current="true"] {
+ color: var(--link-color);
+ text-decoration: none;
+ font-weight: 500;
+ }
}
.docs-nav-panel {
- @media all and (min-width: 1024px) {
- display: flex;
- flex-direction: row;
- justify-content: center;
- }
-
- &[data-open="false"] {
- display: none;
- }
-
- &[data-open="true"] {
- @media all and (min-width: 1024px) {
- display: flex;
- flex-direction: row;
- justify-content: center;
- }
- }
+ @media all and (min-width: 1024px) {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ }
+
+ &[data-open="false"] {
+ display: none;
+ }
+
+ &[data-open="true"] {
+ @media all and (min-width: 1024px) {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ }
+ }
}
.docs-nav-panel .mobile-only {
- @media all and (min-width: 1024px) {
- display: none;
- }
+ @media all and (min-width: 1024px) {
+ display: none;
+ }
}
.docs-site-nav-toggle {
- cursor: pointer;
- display: inline-flex;
- align-items: center;
- margin-left: .5rem;
- margin-right: -10px;
- margin-inline-start: .5rem;
- margin-inline-end: -10px;
-
- svg {
- width: 40px;
- height: 40px;
- color: var(--headings-color);
- fill: none;
- stroke-width: 4;
- stroke-linecap: round;
- stroke-linejoin: round;
- }
-
- #ham-top,
- #ham-middle,
- #ham-bottom {
- transition: all .2s linear;
- }
-
- #ham-top {
- transform-origin: 30px 37px;
- }
-
- #ham-bottom {
- transform-origin: 30px 63px;
- }
-
- &[aria-expanded="true"] {
- #ham-middle {
- opacity: 0;
- }
-
- #ham-top {
- transform: rotate(41deg);
- }
-
- #ham-bottom {
- transform: rotate(-41deg);
- }
- }
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ margin-left: 0.5rem;
+ margin-right: -10px;
+ margin-inline-start: 0.5rem;
+ margin-inline-end: -10px;
+
+ svg {
+ width: 40px;
+ height: 40px;
+ color: var(--headings-color);
+ fill: none;
+ stroke-width: 4;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ }
+
+ #ham-top,
+ #ham-middle,
+ #ham-bottom {
+ transition: all 0.2s linear;
+ }
+
+ #ham-top {
+ transform-origin: 30px 37px;
+ }
+
+ #ham-bottom {
+ transform-origin: 30px 63px;
+ }
+
+ &[aria-expanded="true"] {
+ #ham-middle {
+ opacity: 0;
+ }
+
+ #ham-top {
+ transform: rotate(41deg);
+ }
+
+ #ham-bottom {
+ transform: rotate(-41deg);
+ }
+ }
}
@media all and (min-width: 1024px) {
- .docs-site-nav {
- flex-direction: row;
- grid-column: auto;
- gap: 2rem;
-
- ul {
- display: flex;
- gap: 2rem;
- font-size: var(--step-0);
-
- li {
- margin-bottom: 0;
- margin-block-end: 0;
- }
- }
-
- .flexer {
- order: 1;
- }
- }
+ .docs-site-nav {
+ flex-direction: row;
+ grid-column: auto;
+ gap: 2rem;
+
+ ul {
+ display: flex;
+ gap: 2rem;
+ font-size: var(--step-0);
+
+ li {
+ margin-bottom: 0;
+ margin-block-end: 0;
+ }
+ }
+
+ .flexer {
+ order: 1;
+ }
+ }
}
diff --git a/docs/src/assets/scss/components/hero.scss b/docs/src/assets/scss/components/hero.scss
index 44a7390e0270..5a24c7348573 100644
--- a/docs/src/assets/scss/components/hero.scss
+++ b/docs/src/assets/scss/components/hero.scss
@@ -1,64 +1,64 @@
.hero .grid {
- @media all and (min-width: 800px) {
- display: grid;
- grid-template-columns: 2fr 1fr;
- grid-gap: 2rem;
- align-items: center;
- }
+ @media all and (min-width: 800px) {
+ display: grid;
+ grid-template-columns: 2fr 1fr;
+ grid-gap: 2rem;
+ align-items: center;
+ }
- .span-1-7 {
- grid-column: 1 / 2;
- }
+ .span-1-7 {
+ grid-column: 1 / 2;
+ }
- .span-10-12 {
- grid-column: 2 / 3;
- justify-self: end;
- }
+ .span-10-12 {
+ grid-column: 2 / 3;
+ justify-self: end;
+ }
}
.hero {
- border-bottom: 1px solid var(--divider-color);
- border-block-end: 1px solid var(--divider-color);
- background-color: var(--hero-background-color);
+ border-bottom: 1px solid var(--divider-color);
+ border-block-end: 1px solid var(--divider-color);
+ background-color: var(--hero-background-color);
- @media all and (min-width: 800px) {
- // when the ad is displayed
- min-height: calc(285px + var(--space-xl-4xl));
- }
+ @media all and (min-width: 800px) {
+ // when the ad is displayed
+ min-height: calc(285px + var(--space-xl-4xl));
+ }
- .content-container {
- padding: var(--space-xl-4xl) 0;
- margin: 0;
- }
+ .content-container {
+ padding: var(--space-xl-4xl) 0;
+ margin: 0;
+ }
- >.content-container {
- margin: 0 auto;
- padding: 0 calc(1rem + 1vw);
- padding-bottom: 0;
- align-items: center;
- max-width: 1700px;
+ > .content-container {
+ margin: 0 auto;
+ padding: 0 calc(1rem + 1vw);
+ padding-bottom: 0;
+ align-items: center;
+ max-width: 1700px;
- @media all and (min-width: 1700px) {
- margin: auto;
- }
- }
+ @media all and (min-width: 1700px) {
+ margin: auto;
+ }
+ }
}
.hero--homepage {
- .section-title {
- margin-bottom: 1.5rem;
- margin-block-end: 1.5rem;
- }
+ .section-title {
+ margin-bottom: 1.5rem;
+ margin-block-end: 1.5rem;
+ }
- .section-supporting-text {
- margin: 0;
- font-size: var(--step-1);
- text-align: left;
- }
+ .section-supporting-text {
+ margin: 0;
+ font-size: var(--step-1);
+ text-align: left;
+ }
- .eslint-actions {
- font-size: var(--step-1);
- margin-top: 3rem;
- margin-block-start: 3rem;
- }
+ .eslint-actions {
+ font-size: var(--step-1);
+ margin-top: 3rem;
+ margin-block-start: 3rem;
+ }
}
diff --git a/docs/src/assets/scss/components/index.scss b/docs/src/assets/scss/components/index.scss
index 5989e1f48e7a..e009644ac587 100644
--- a/docs/src/assets/scss/components/index.scss
+++ b/docs/src/assets/scss/components/index.scss
@@ -1,109 +1,109 @@
.index {
- margin-bottom: 4rem;
- margin-block-end: 4rem;
+ margin-bottom: 4rem;
+ margin-block-end: 4rem;
}
.index__item {
- margin: 0;
-
- a {
- display: block;
- color: inherit;
- text-decoration: none;
- padding: .625rem .875rem;
- font-size: var(--step-0);
- border-radius: var(--border-radius);
-
- &:hover {
- color: var(--link-color);
- }
- }
-
- a[aria-current="page"] {
- color: var(--link-color);
- background-color: var(--lightest-background-color);
- font-weight: 500;
- }
+ margin: 0;
+
+ a {
+ display: block;
+ color: inherit;
+ text-decoration: none;
+ padding: 0.625rem 0.875rem;
+ font-size: var(--step-0);
+ border-radius: var(--border-radius);
+
+ &:hover {
+ color: var(--link-color);
+ }
+ }
+
+ a[aria-current="page"] {
+ color: var(--link-color);
+ background-color: var(--lightest-background-color);
+ font-weight: 500;
+ }
}
.index__toggle {
- cursor: pointer;
- display: flex;
- width: 100%;
- padding: .75rem 1.125rem;
- align-items: center;
- justify-content: space-between;
- gap: .5rem;
- font-weight: 500;
- border: 1px solid var(--border-color);
- border-radius: var(--border-radius);
- background-color: var(--secondary-button-background-color);
- color: var(--secondary-button-text-color);
- box-shadow: 0 1px 2px rgba(16, 24, 40, 0.1);
-
- &:hover {
- background-color: var(--secondary-button-hover-color);
- }
-
- @media all and (min-width: 1024px) {
- display: none;
- }
-
- svg {
- width: 1.5em;
- height: 1.5em;
- color: inherit;
- fill: none;
- stroke-width: 4;
- stroke-linecap: round;
- stroke-linejoin: round;
- }
-
- #ham-top,
- #ham-middle,
- #ham-bottom {
- transition: all .2s linear;
- }
-
- #ham-top {
- transform-origin: 30px 37px;
- }
-
- #ham-bottom {
- transform-origin: 30px 63px;
- }
-
- &[aria-expanded="true"] {
- #ham-middle {
- opacity: 0;
- }
-
- #ham-top {
- transform: rotate(41deg);
- }
-
- #ham-bottom {
- transform: rotate(-41deg);
- }
- }
+ cursor: pointer;
+ display: flex;
+ width: 100%;
+ padding: 0.75rem 1.125rem;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.5rem;
+ font-weight: 500;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ background-color: var(--secondary-button-background-color);
+ color: var(--secondary-button-text-color);
+ box-shadow: 0 1px 2px rgba(16, 24, 40, 0.1);
+
+ &:hover {
+ background-color: var(--secondary-button-hover-color);
+ }
+
+ @media all and (min-width: 1024px) {
+ display: none;
+ }
+
+ svg {
+ width: 1.5em;
+ height: 1.5em;
+ color: inherit;
+ fill: none;
+ stroke-width: 4;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ }
+
+ #ham-top,
+ #ham-middle,
+ #ham-bottom {
+ transition: all 0.2s linear;
+ }
+
+ #ham-top {
+ transform-origin: 30px 37px;
+ }
+
+ #ham-bottom {
+ transform-origin: 30px 63px;
+ }
+
+ &[aria-expanded="true"] {
+ #ham-middle {
+ opacity: 0;
+ }
+
+ #ham-top {
+ transform: rotate(41deg);
+ }
+
+ #ham-bottom {
+ transform: rotate(-41deg);
+ }
+ }
}
.index__list {
- display: block;
+ display: block;
- &[data-open="false"] {
- display: none;
+ &[data-open="false"] {
+ display: none;
- @media all and (min-width: 1024px) {
- display: block;
- }
- }
+ @media all and (min-width: 1024px) {
+ display: block;
+ }
+ }
- &[data-open="true"] {
- display: block;
+ &[data-open="true"] {
+ display: block;
- @media all and (min-width: 1024px) {
- display: block;
- }
- }
+ @media all and (min-width: 1024px) {
+ display: block;
+ }
+ }
}
diff --git a/docs/src/assets/scss/components/language-switcher.scss b/docs/src/assets/scss/components/language-switcher.scss
index 364f23fed6cc..92c05f1e04ea 100644
--- a/docs/src/assets/scss/components/language-switcher.scss
+++ b/docs/src/assets/scss/components/language-switcher.scss
@@ -1,31 +1,31 @@
.switcher--language {
- display: flex;
- align-items: center;
- justify-content: center;
- flex-wrap: wrap;
- gap: .25rem .5rem;
- position: relative;
- width: 100%;
- padding: 0;
- font-size: inherit;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 0.25rem 0.5rem;
+ position: relative;
+ width: 100%;
+ padding: 0;
+ font-size: inherit;
- @media all and (min-width: 800px) {
- justify-content: flex-start;
- }
+ @media all and (min-width: 800px) {
+ justify-content: flex-start;
+ }
}
.switcher--language .label__text {
- flex: 1 0 10ch;
+ flex: 1 0 10ch;
}
.switcher--language .switcher__select {
- flex: 1 0 12rem;
+ flex: 1 0 12rem;
- @media all and (max-width: 799px) {
- max-width: 250px;
- }
+ @media all and (max-width: 799px) {
+ max-width: 250px;
+ }
}
.language-switcher {
- display: inline-flex;
+ display: inline-flex;
}
diff --git a/docs/src/assets/scss/components/logo.scss b/docs/src/assets/scss/components/logo.scss
new file mode 100644
index 000000000000..351e6653dbe8
--- /dev/null
+++ b/docs/src/assets/scss/components/logo.scss
@@ -0,0 +1,12 @@
+.logo-component {
+ fill: var(--logo-color);
+}
+
+.logo-title {
+ fill: var(--headings-color);
+}
+
+#logo-center {
+ opacity: var(--logo-center-opacity);
+ fill: var(--logo-center-color);
+}
diff --git a/docs/src/assets/scss/components/resources.scss b/docs/src/assets/scss/components/resources.scss
index 4ee2616d8db7..3a59d162c6b1 100644
--- a/docs/src/assets/scss/components/resources.scss
+++ b/docs/src/assets/scss/components/resources.scss
@@ -1,67 +1,68 @@
.resource {
- display: flex;
- border-radius: var(--border-radius);
- border: 1px solid var(--divider-color);
- background-color: var(--lightest-background-color);
- align-items: stretch;
- overflow: hidden;
- margin-bottom: .5rem;
- margin-block-end: .5rem;
- position: relative;
- transition: all .2s linear;
+ display: flex;
+ border-radius: var(--border-radius);
+ border: 1px solid var(--divider-color);
+ background-color: var(--lightest-background-color);
+ align-items: stretch;
+ overflow: hidden;
+ margin-bottom: 0.5rem;
+ margin-block-end: 0.5rem;
+ position: relative;
+ transition: all 0.2s linear;
- &:hover {
- background-color: var(--lighter-background-color);
- }
+ &:hover {
+ background-color: var(--lighter-background-color);
+ }
}
.resource__image {
- flex: 1 0 5.5rem;
- max-width: 5.5rem;
- overflow: hidden;
- padding: .25rem;
+ flex: 1 0 5.5rem;
+ max-width: 5.5rem;
+ overflow: hidden;
+ padding: 0.25rem;
- img {
- display: block;
- height: 100%;
- width: 100%;
- object-fit: contain;
- }
+ img {
+ display: block;
+ height: 100%;
+ width: 100%;
+ object-fit: contain;
+ }
}
.resource__content {
- flex: 4;
- padding: .75rem;
- align-self: center;
+ flex: 4;
+ padding: 0.75rem;
+ align-self: center;
}
-.resource__title { // a
- text-decoration: none;
- color: var(--headings-color);
- font-weight: 500;
- margin-bottom: .125rem;
+.resource__title {
+ // a
+ text-decoration: none;
+ color: var(--headings-color);
+ font-weight: 500;
+ margin-bottom: 0.125rem;
- &::after {
- content: "";
- position: absolute;
- left: 0;
- offset-inline-start: 0;
- top: 0;
- offset-block-start: 0;
- width: 100%;
- height: 100%;
- }
+ &::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ offset-inline-start: 0;
+ top: 0;
+ offset-block-start: 0;
+ width: 100%;
+ height: 100%;
+ }
}
.resource__domain,
.resource__domain a {
- text-decoration: none;
- color: var(--body-text-color);
- font-size: .875rem;
+ text-decoration: none;
+ color: var(--body-text-color);
+ font-size: 0.875rem;
}
.resource__icon {
- color: var(--headings-color);
- margin: 1rem;
- align-self: center;
+ color: var(--headings-color);
+ margin: 1rem;
+ align-self: center;
}
diff --git a/docs/src/assets/scss/components/rules.scss b/docs/src/assets/scss/components/rules.scss
index 4e0f4619c211..6754851337fc 100644
--- a/docs/src/assets/scss/components/rules.scss
+++ b/docs/src/assets/scss/components/rules.scss
@@ -1,201 +1,207 @@
.rule-categories {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 0;
- margin-bottom: 3rem;
- background-color: var(--lightest-background-color);
- border: 1px solid var(--divider-color);
- border-radius: var(--border-radius);
-
- .rule-category {
- margin: 0;
- padding: 1rem;
- background: none;
- border: none;
-
- @media screen and (min-width: 768px) {
- &:not(:first-child)::after {
- content: "";
- display: block;
- padding: 1px;
- border-left: 1px solid var(--divider-color);
- left: 0;
- }
- }
-
- @media screen and (min-width: 768px) and (max-width: 1023px), screen and (min-width: 1440px) {
- &:not(:first-child)::after {
- height: 70%;
- position: absolute;
- }
- }
-
- @media screen and (min-width: 1024px) and (max-width: 1439px) {
- &:nth-child(2)::after {
- height: 70%;
- position: absolute;
- }
- }
- }
-
- .rule-category__description {
- flex: 1 1 45ch;
- }
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 0;
+ margin-bottom: 3rem;
+ background-color: var(--lightest-background-color);
+ border: 1px solid var(--divider-color);
+ border-radius: var(--border-radius);
+
+ .rule-category {
+ margin: 0;
+ padding: 1rem;
+ background: none;
+ border: none;
+
+ @media screen and (min-width: 768px) {
+ &:not(:first-child)::after {
+ content: "";
+ display: block;
+ border-left: 1px solid var(--divider-color);
+ left: 0;
+ }
+
+ &:nth-child(2)::after {
+ height: 70%;
+ position: absolute;
+ }
+ }
+
+ @media screen and (min-width: 768px) and (max-width: 799px),
+ screen and (min-width: 854px) and (max-width: 1023px),
+ screen and (min-width: 1256px) {
+ &:nth-child(3)::after {
+ height: 70%;
+ position: absolute;
+ }
+ }
+
+ @media screen and (min-width: 800px) and (max-width: 853px),
+ screen and (min-width: 1024px) and (max-width: 1255px),
+ screen and (min-width: 1654px) and (max-width: 1982px) {
+ &:nth-child(4)::after {
+ height: 70%;
+ position: absolute;
+ }
+ }
+ }
+
+ .rule-category__description {
+ height: 100%;
+ }
}
.rule-category {
- font-size: var(--step--1);
- display: flex;
- position: relative;
- flex-wrap: wrap;
- align-items: flex-start;
- gap: 1rem;
- padding: 1rem;
- margin: 1.5rem 0;
- border-radius: var(--border-radius);
- border: 1px solid var(--divider-color);
- background-color: var(--lightest-background-color);
-
- p {
- margin: 0;
- }
-
- .rule-category__description {
- flex: 1 1 30ch;
- }
+ font-size: var(--step--1);
+ display: flex;
+ position: relative;
+ flex-wrap: wrap;
+ align-items: flex-start;
+ gap: 1rem;
+ padding: 1rem;
+ margin: 1.5rem 0;
+ border-radius: var(--border-radius);
+ border: 1px solid var(--divider-color);
+ background-color: var(--lightest-background-color);
+
+ p {
+ margin: 0;
+ }
}
.rule:not(.token) {
- border-radius: var(--border-radius);
- background-color: var(--lightest-background-color);
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 1rem;
- padding: 1rem;
- margin: .5rem 0;
- position: relative;
-
- p:last-of-type {
- margin: 0;
- }
+ border-radius: var(--border-radius);
+ background-color: var(--lightest-background-color);
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 1rem;
+ padding: 1rem;
+ margin: 0.5rem 0;
+ position: relative;
+
+ p:last-of-type {
+ margin: 0;
+ }
}
-.rule--deprecated,
-.rule--removed {
- // opacity: .5;
+.rule__content {
+ flex: 1 1 35ch;
+ overflow-x: auto;
}
-.rule__content {
- flex: 1 1 35ch;
+.rule__name_wrapper {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+
+ .frozen {
+ font-size: 0.875rem;
+ }
}
.rule__name {
- font-weight: 500;
- font-size: .875rem;
- margin-bottom: .25rem;
- margin-block-end: .25rem;
+ font-weight: 500;
+ font-size: 0.875rem;
+ margin-bottom: 0.25rem;
+ margin-block-end: 0.25rem;
}
a.rule__name {
- text-decoration: none;
-
- &:hover {
- text-decoration: underline;
- }
-
- &::after {
- position: absolute;
- content: "";
- width: 100%;
- height: 100%;
- top: 0;
- offset-block-start: 0;
- left: 0;
- offset-inline-start: 0;
- }
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+
+ &::after {
+ position: absolute;
+ content: "";
+ width: 100%;
+ height: 100%;
+ top: 0;
+ offset-block-start: 0;
+ left: 0;
+ offset-inline-start: 0;
+ }
}
.rule__description {
- font-size: var(--step--1);
+ font-size: var(--step--1);
}
.rule__categories {
- font-size: .875rem;
- display: flex;
- align-items: center;
- gap: 1rem;
- border-radius: var(--border-radius);
- padding: 2px 4px;
-
- p {
- display: inline-flex;
- margin: 0;
- align-items: center;
- }
-
- [data-theme="dark"] & {
- background: var(--body-background-color);
- }
+ font-size: 0.875rem;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ border-radius: var(--border-radius);
+ padding: 2px 4px;
+
+ p {
+ display: inline-flex;
+ margin: 0;
+ align-items: center;
+ }
+
+ [data-theme="dark"] & {
+ background: var(--body-background-color);
+ }
}
.rule__status {
- color: var(--color-rose-500);
- background: var(--color-rose-50);
- border-radius: var(--border-radius);
- display: inline-block;
- font-weight: normal;
- margin-left: .5rem;
- margin-inline-start: .5rem;
- font-size: var(--step--1);
- padding: 0 .5rem;
-
- [data-theme="dark"] & {
- background: var(--body-background-color);
- }
+ color: var(--color-rose-500);
+ background: var(--rule-status-background-color);
+ border-radius: var(--border-radius);
+ display: inline-block;
+ font-weight: normal;
+ margin-left: 0.5rem;
+ margin-inline-start: 0.5rem;
+ font-size: var(--step--1);
+ padding: 0 0.5rem;
}
.rule__categories__type {
- &[aria-hidden="true"] {
- opacity: .25;
- }
+ &[aria-hidden="true"] {
+ opacity: 0.25;
+ }
}
/* related rules */
.related-rules__list {
- display: flex;
- gap: .5rem;
- flex-wrap: wrap;
- justify-content: start;
+ display: flex;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+ justify-content: start;
}
.related-rules__list__item {
- svg {
- color: inherit;
- }
-
- a {
- text-decoration: none;
- color: var(--headings-color);
- padding: .625rem;
- display: inline-flex;
- gap: .5rem;
- align-items: center;
- border: 1px solid var(--divider-color);
- border-radius: var(--border-radius);
- background-color: var(--lightest-background-color);
-
- &:hover {
- color: var(--link-color);
- background-color: var(--lighter-background-color);
- }
- }
+ svg {
+ color: inherit;
+ }
+
+ a {
+ text-decoration: none;
+ color: var(--headings-color);
+ padding: 0.625rem;
+ display: inline-flex;
+ gap: 0.5rem;
+ align-items: center;
+ border: 1px solid var(--divider-color);
+ border-radius: var(--border-radius);
+ background-color: var(--lightest-background-color);
+
+ &:hover {
+ color: var(--link-color);
+ background-color: var(--lighter-background-color);
+ }
+ }
}
a.rule-list-item + a.rule-list-item::before {
- content: ",";
- display: inline-block;
- margin-left: 5px;
- margin-right: 5px;
+ content: ",";
+ display: inline-block;
+ margin-left: 5px;
+ margin-right: 5px;
}
diff --git a/docs/src/assets/scss/components/search.scss b/docs/src/assets/scss/components/search.scss
index 4b90582c4c6f..9f942657e70f 100644
--- a/docs/src/assets/scss/components/search.scss
+++ b/docs/src/assets/scss/components/search.scss
@@ -1,163 +1,184 @@
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
- appearance: none;
+ appearance: none;
}
[type="search"]::-ms-clear,
[type="search"]::-ms-reveal {
- display: none;
- width: 0;
- height: 0;
+ display: none;
+ width: 0;
+ height: 0;
}
.search {
- margin: 1rem 0;
- position: relative;
+ margin: 1rem 0;
+ position: relative;
}
.search__input-wrapper,
.search__inner-input-wrapper {
- position: relative;
+ position: relative;
}
.search__clear-btn {
- color: var(--body-text-color);
- position: absolute;
- display: flex;
- top: 50%;
- offset-block-start: 50%;
- transform: translateY(-50%);
- right: 1.5rem;
- offset-inline-end: 1.5rem;
- z-index: 3;
- padding: 0;
-
- svg {
- color: inherit;
- width: 1rem;
- height: 1rem;
- border: 1px solid;
- border-radius: 50%;
- }
+ color: var(--body-text-color);
+ position: absolute;
+ display: flex;
+ top: 25%;
+ offset-block-start: 25%;
+ transform: translateY(-25%);
+ right: 1rem;
+ offset-inline-end: 1.5rem;
+ z-index: 3;
+ padding: 0;
+
+ svg {
+ color: inherit;
+ width: 1rem;
+ height: 1rem;
+ border: 1px solid;
+ border-radius: 50%;
+ }
}
.search__input {
- padding-left: 2.5rem;
- padding-inline-start: 2.5rem;
- outline-offset: 1px;
- width: 100%;
+ padding-left: 2.5rem;
+ padding-inline-start: 2.5rem;
+ outline-offset: 1px;
+ width: 100%;
+ padding-right: 2.5rem;
+ padding-inline-end: 2.5rem;
}
.search__icon {
- color: var(--body-text-color);
- position: absolute;
- display: block;
- top: 50%;
- offset-block-start: 50%;
- transform: translateY(-50%);
- left: .75rem;
- offset-inline-start: .75rem;
- z-index: 3;
+ color: var(--body-text-color);
+ position: absolute;
+ display: block;
+ top: 0.75rem;
+ left: 0.75rem;
+ offset-inline-start: 0.75rem;
+ z-index: 3;
+}
+
+.search__inner-input-wrapper {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+}
+
+.search_powered-by-wrapper {
+ text-decoration: none;
+
+ .search__powered-by {
+ display: flex;
+ padding: 10px 6px 0 0;
+ align-items: center;
+
+ .powered_by-text {
+ color: var(--body-text-color);
+ margin-right: 5px;
+ margin-top: -2px;
+ }
+
+ .algolia-logo {
+ fill: var(--body-text-color);
+ }
+ }
}
/* search results */
.search .search-results {
- font-size: .875rem;
- background-color: var(--body-background-color);
- z-index: 10;
- width: 100%;
- border-radius: 0 0 var(--border-radius) var(--border-radius);
- border: 1px solid var(--divider-color);
- position: relative;
- top: .25rem;
- max-height: 400px;
- overflow-y: auto;
-
- @media all and (min-width: 1024px) {
- box-shadow: var(--shadow-lg);
- position: absolute;
- top: calc(100% + .25rem);
- }
-
- &[data-results="true"] {
- padding: 0;
- }
-
- &[data-results="false"] {
- padding: 1rem;
- }
-
- &:empty {
- display: none;
- }
+ font-size: 0.875rem;
+ background-color: var(--body-background-color);
+ z-index: 10;
+ width: 100%;
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
+ border: 1px solid var(--divider-color);
+ position: absolute;
+ top: calc(100% - 1.5rem);
+ max-height: 400px;
+ overflow-y: auto;
+ box-shadow: var(--shadow-lg);
+
+ &[data-results="true"] {
+ padding: 0;
+ }
+
+ &[data-results="false"] {
+ padding: 1rem;
+ }
+
+ &:empty {
+ display: none;
+ }
}
.search-results__list {
- list-style: none;
- margin: 0;
- padding: 0;
+ list-style: none;
+ margin: 0;
+ padding: 0;
}
.search .search-results__item {
- margin: 0;
- padding: .875rem;
- border-bottom: 1px solid var(--lightest-background-color);
- border-block-end: 1px solid var(--lightest-background-color);
- position: relative;
-
- &:hover {
- background-color: var(--lightest-background-color);
- }
-
- &:focus-within {
- background-color: var(--lightest-background-color);
- }
+ margin: 0;
+ padding: 0.875rem;
+ border-bottom: 1px solid var(--lightest-background-color);
+ border-block-end: 1px solid var(--lightest-background-color);
+ position: relative;
+
+ &:hover {
+ background-color: var(--lightest-background-color);
+ }
+
+ &:focus-within {
+ background-color: var(--lightest-background-color);
+ }
}
.search .search-results__item__title {
- font-size: var(--step-0);
- font-size: .875rem;
- margin-bottom: 0;
- font-family: var(--text-font);
-
- a {
- display: block;
- text-decoration: none;
- color: var(--link-color);
- font: inherit;
- padding: .25rem .75rem;
-
- &:hover {
- background-color: inherit;
- color: var(--link-color);
- }
-
- &::after {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- content: "";
- }
- }
+ font-size: var(--step-0);
+ font-size: 0.875rem;
+ margin-bottom: 0;
+ font-family: var(--text-font);
+
+ a {
+ display: block;
+ text-decoration: none;
+ color: var(--link-color);
+ font: inherit;
+ padding: 0.25rem 0.75rem;
+
+ &:hover {
+ background-color: inherit;
+ color: var(--link-color);
+ }
+
+ &::after {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ content: "";
+ }
+ }
}
.search-results__item__context {
- margin: 0;
- font-size: .875rem;
- padding-left: 1rem;
+ margin: 0;
+ font-size: 0.875rem;
+ padding-left: 1rem;
}
.algolia-docsearch-suggestion--highlight {
- background-color: var(--color-brand);
- color: #fff;
- display: inline-block;
- padding: 0 2px;
- border-radius: 2px;
-
- [data-theme="dark"] & {
- background-color: var(--link-color);
- color: var(--color-neutral-900);
- }
+ background-color: var(--color-brand);
+ color: #fff;
+ display: inline-block;
+ padding: 0 2px;
+ border-radius: 2px;
+
+ [data-theme="dark"] & {
+ background-color: var(--link-color);
+ color: var(--color-neutral-900);
+ }
}
diff --git a/docs/src/assets/scss/components/social-icons.scss b/docs/src/assets/scss/components/social-icons.scss
index eddd47f3ec77..24048d5ce7b9 100644
--- a/docs/src/assets/scss/components/social-icons.scss
+++ b/docs/src/assets/scss/components/social-icons.scss
@@ -1,22 +1,26 @@
.eslint-social-icons {
- margin-bottom: -1rem;
- margin-block-end: -1rem;
+ margin-bottom: -1rem;
+ margin-block-end: -1rem;
- ul {
- margin: 0;
- padding: 0;
- margin-left: -1rem;
- margin-inline-start: -1rem;
- display: inline-flex;
+ ul {
+ margin: 0;
+ padding: 0;
+ margin-left: -1rem;
+ margin-inline-start: -1rem;
+ display: inline-flex;
- li {
- margin: 0;
- align-items: center;
+ li {
+ margin: 0;
+ align-items: center;
- a {
- display: flex;
- padding: 1rem .75rem;
- }
- }
- }
+ a {
+ display: flex;
+ padding: 1rem 0.75rem;
+ }
+ }
+
+ @media screen and (min-width: 800px) and (max-width: 1500px) {
+ flex-wrap: wrap;
+ }
+ }
}
diff --git a/docs/src/assets/scss/components/tabs.scss b/docs/src/assets/scss/components/tabs.scss
index 8a7d866c514c..67cdfc89be0c 100644
--- a/docs/src/assets/scss/components/tabs.scss
+++ b/docs/src/assets/scss/components/tabs.scss
@@ -1,63 +1,66 @@
.c-tabs {
- pre {
- margin-top: 0;
- margin-block-start: 0;
- }
+ pre {
+ margin-top: 0;
+ margin-block-start: 0;
+ }
}
.c-tabs__tablist {
- .js-tabs & {
- display: flex;
- justify-content: start;
- }
+ .js-tabs & {
+ display: flex;
+ justify-content: start;
+ }
}
.c-tabs__tab {
- background: none;
- border: none;
- margin: 0;
- color: inherit;
- font: inherit;
- cursor: pointer;
- line-height: inherit;
- font-weight: 500;
- font-size: var(--step-0);
- display: inline-flex;
- padding: .75rem 1.125rem;
- align-items: center;
- justify-content: center;
- border-radius: var(--border-radius) var(--border-radius) 0 0;
- transition: background-color .2s linear, border-color .2s linear;
-
- &:hover {
- color: var(--link-color);
- }
-
- &[aria-selected="true"] {
- color: var(--link-color);
- background-color: var(--lightest-background-color);
- }
+ background: none;
+ border: none;
+ margin: 0;
+ color: inherit;
+ font: inherit;
+ cursor: pointer;
+ line-height: inherit;
+ font-weight: 500;
+ font-size: var(--step-0);
+ display: inline-flex;
+ padding: 0.75rem 1.125rem;
+ align-items: center;
+ justify-content: center;
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
+ transition:
+ background-color 0.2s linear,
+ border-color 0.2s linear;
+
+ &:hover {
+ color: var(--link-color);
+ }
+
+ &[aria-selected="true"] {
+ color: var(--link-color);
+ background-color: var(--lightest-background-color);
+ }
}
.c-tabs__tabpanel {
- margin-bottom: 2rem;
- margin-block-end: 2rem;
- background-color: var(--lightest-background-color);
- border-radius: 0 var(--border-radius) var(--border-radius) var(--border-radius);
-
- .js-tabs & {
- margin-bottom: 0;
- margin-block-end: 0;
- }
+ margin-bottom: 2rem;
+ margin-block-end: 2rem;
+ background-color: var(--lightest-background-color);
+ border-radius: 0 var(--border-radius) var(--border-radius)
+ var(--border-radius);
+
+ .js-tabs & {
+ margin-bottom: 0;
+ margin-block-end: 0;
+ }
}
.c-tabs__tabpanel__title {
- margin-bottom: 1.5rem;
- margin-block-end: 1.5rem;
+ margin-bottom: 1.5rem;
+ margin-block-end: 1.5rem;
}
// when the js is enabled, the tabpanels are labelled by their tabs
// you may choose to hide or keep the headings inside of them visible
.js-tabs .c-tabs__tabpanel__title {
- display: none;
+ display: none;
}
diff --git a/docs/src/assets/scss/components/theme-switcher.scss b/docs/src/assets/scss/components/theme-switcher.scss
index d44aa9009d94..69277fa2c1f6 100644
--- a/docs/src/assets/scss/components/theme-switcher.scss
+++ b/docs/src/assets/scss/components/theme-switcher.scss
@@ -1,77 +1,77 @@
.theme-switcher {
- display: inline-flex;
- align-items: center;
- gap: .5rem;
- position: relative;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ position: relative;
}
.theme-switcher-label.theme-switcher-label {
- color: inherit;
- font: inherit;
- font-family: var(--text-font);
- margin: 0;
+ color: inherit;
+ font: inherit;
+ font-family: var(--text-font);
+ margin: 0;
}
.theme-switcher__buttons {
- display: flex;
- border: 1px solid var(--border-color);
- border-radius: var(--border-radius);
- background-color: var(--body-background-color);
+ display: flex;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ background-color: var(--body-background-color);
}
.theme-switcher__button {
- flex-wrap: wrap;
- box-shadow: var(--shadow-xs);
- padding: .625rem .875rem;
- display: inline-flex;
- align-items: center;
- margin: 0;
- gap: .25rem;
- color: inherit;
+ flex-wrap: wrap;
+ box-shadow: var(--shadow-xs);
+ padding: 0.625rem 0.675rem;
+ display: inline-flex;
+ align-items: center;
+ margin: 0;
+ gap: 0.25rem;
+ color: inherit;
- &:first-of-type {
- border-right: .5px solid var(--border-color);
- border-inline-end: .5px solid var(--border-color);
- }
+ &:first-of-type {
+ border-right: 0.5px solid var(--border-color);
+ border-inline-end: 0.5px solid var(--border-color);
+ }
- &:last-of-type {
- border-left: .5px solid var(--border-color);
- border-inline-start: .5px solid var(--border-color);
- }
+ &:last-of-type {
+ border-left: 0.5px solid var(--border-color);
+ border-inline-start: 0.5px solid var(--border-color);
+ }
- .theme-switcher__icon {
- color: var(--icon-color);
- }
+ .theme-switcher__icon {
+ color: var(--icon-color);
+ }
- &:hover {
- .theme-switcher__icon {
- color: var(--link-color);
- }
- }
+ &:hover {
+ .theme-switcher__icon {
+ color: var(--link-color);
+ }
+ }
}
.theme-switcher__button[aria-pressed="true"] {
- color: var(--link-color);
+ color: var(--link-color);
- .theme-switcher__icon {
- color: var(--link-color);
- }
+ .theme-switcher__icon {
+ color: var(--link-color);
+ }
- &:hover {
- .theme-switcher__icon {
- color: var(--link-color);
- }
- }
+ &:hover {
+ .theme-switcher__icon {
+ color: var(--link-color);
+ }
+ }
}
.theme-switcher__button[aria-pressed="false"] {
- .theme-switcher__icon {
- color: var(--icon-color);
- }
+ .theme-switcher__icon {
+ color: var(--icon-color);
+ }
- &:hover {
- .theme-switcher__icon {
- color: var(--link-color);
- }
- }
+ &:hover {
+ .theme-switcher__icon {
+ color: var(--link-color);
+ }
+ }
}
diff --git a/docs/src/assets/scss/components/toc.scss b/docs/src/assets/scss/components/toc.scss
index 96647b4c70f0..10440effd068 100644
--- a/docs/src/assets/scss/components/toc.scss
+++ b/docs/src/assets/scss/components/toc.scss
@@ -1,135 +1,135 @@
.docs-toc {
- margin: 2rem 0;
+ margin: 2rem 0;
- @media all and (min-width: 1400px) {
- display: none;
- }
+ @media all and (min-width: 1400px) {
+ display: none;
+ }
- .docs-aside & {
- display: none;
+ .docs-aside & {
+ display: none;
- @media all and (min-width: 1400px) {
- display: block;
- }
- }
+ @media all and (min-width: 1400px) {
+ display: block;
+ }
+ }
}
.docs-aside {
- // for sticky table of contents in sidebar
- .docs-toc.c-toc {
- background-color: var(--body-background-color);
- @media all and (min-width: 1400px) {
- position: sticky;
- top: 20px;
- overflow-y: auto; // show scrollbar when toc is higher than viewport
- padding-right: 5px; // push scrollbar away from content
- max-height: calc(100vh - 32px); // minus element's margin-top
- a.active {
- color: var(--link-color);
- font-weight: 500;
- }
- }
- }
-
- .c-toc ol li.active::before {
- @media all and (min-width: 1400px) {
- color: var(--link-color);
- }
- }
+ // for sticky table of contents in sidebar
+ .docs-toc.c-toc {
+ background-color: var(--body-background-color);
+ @media all and (min-width: 1400px) {
+ position: sticky;
+ top: 20px;
+ overflow-y: auto; // show scrollbar when toc is higher than viewport
+ padding-right: 5px; // push scrollbar away from content
+ max-height: calc(100vh - 32px); // minus element's margin-top
+ a.active {
+ color: var(--link-color);
+ font-weight: 500;
+ }
+ }
+ }
+
+ .c-toc ol li.active::before {
+ @media all and (min-width: 1400px) {
+ color: var(--link-color);
+ }
+ }
}
.c-toc {
- ol {
- margin: 0;
-
- li {
- position: relative;
- margin-bottom: .25rem;
- margin-block-end: .25rem;
- padding-left: 1rem;
- padding-inline-start: 1rem;
-
- >ol {
- margin-top: .25rem;
- }
- }
-
- li::before {
- content: "â";
- color: var(--icon-color);
- position: absolute;
- left: -.4rem;
- offset-inline-start: -.4rem;
- }
- }
-
- a {
- text-decoration: none;
- color: var(--headings-color);
-
- &:hover {
- color: var(--link-color);
- }
- }
+ ol {
+ margin: 0;
+
+ li {
+ position: relative;
+ margin-bottom: 0.25rem;
+ margin-block-end: 0.25rem;
+ padding-left: 1rem;
+ padding-inline-start: 1rem;
+
+ > ol {
+ margin-top: 0.25rem;
+ }
+ }
+
+ li::before {
+ content: "â";
+ color: var(--icon-color);
+ position: absolute;
+ left: -0.4rem;
+ offset-inline-start: -0.4rem;
+ }
+ }
+
+ a {
+ text-decoration: none;
+ color: var(--headings-color);
+
+ &:hover {
+ color: var(--link-color);
+ }
+ }
}
.c-toc__label.c-toc__label {
- font-size: var(--step-0);
- color: var(--body-text-color);
- font-family: var(--text-font);
- margin-bottom: .5rem;
- margin-block-end: .5rem;
+ font-size: var(--step-0);
+ color: var(--body-text-color);
+ font-family: var(--text-font);
+ margin-bottom: 0.5rem;
+ margin-block-end: 0.5rem;
}
.c-toc__label {
- width: fit-content;
-
- button {
- color: var(--link-color);
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: space-between;
- font: inherit;
- font-size: inherit;
- font-weight: 500;
- width: 100%;
- height: 100%;
- text-align: left;
- line-height: 1.5;
- padding: 0;
- border-radius: 0;
- position: relative;
- transition: outline 0.1s linear;
-
- svg {
- flex: none;
- }
- }
+ width: fit-content;
+
+ button {
+ color: var(--link-color);
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font: inherit;
+ font-size: inherit;
+ font-weight: 500;
+ width: 100%;
+ height: 100%;
+ text-align: left;
+ line-height: 1.5;
+ padding: 0;
+ border-radius: 0;
+ position: relative;
+ transition: outline 0.1s linear;
+
+ svg {
+ flex: none;
+ }
+ }
}
/* Styles for the accordion icon */
.toc-trigger-icon {
- display: block !important; // to override aria-hidden
- width: 0.75rem;
- height: 0.5rem;
- transform-origin: 50% 50%;
- margin-left: 2rem;
- margin-inline-start: 2rem;
- transition: all 0.1s linear;
- color: var(--color-neutral-400);
-
- [aria-expanded="true"] & {
- transform: rotate(180deg);
- }
+ display: block !important; // to override aria-hidden
+ width: 0.75rem;
+ height: 0.5rem;
+ transform-origin: 50% 50%;
+ margin-left: 2rem;
+ margin-inline-start: 2rem;
+ transition: all 0.1s linear;
+ color: var(--color-neutral-400);
+
+ [aria-expanded="true"] & {
+ transform: rotate(180deg);
+ }
}
.c-toc__panel {
- &[data-open="false"] {
- display: none;
- }
+ &[data-open="false"] {
+ display: none;
+ }
- &[data-open="true"] {
- display: block;
- }
+ &[data-open="true"] {
+ display: block;
+ }
}
diff --git a/docs/src/assets/scss/components/version-switcher.scss b/docs/src/assets/scss/components/version-switcher.scss
index 606b802395cb..95079922f5d0 100644
--- a/docs/src/assets/scss/components/version-switcher.scss
+++ b/docs/src/assets/scss/components/version-switcher.scss
@@ -1,4 +1,4 @@
.version-switcher {
- margin-bottom: .5rem;
- margin-block-end: .5rem;
+ margin-bottom: 0.5rem;
+ margin-block-end: 0.5rem;
}
diff --git a/docs/src/assets/scss/docs-footer.scss b/docs/src/assets/scss/docs-footer.scss
index 347afd3978e6..994004fb3e3a 100644
--- a/docs/src/assets/scss/docs-footer.scss
+++ b/docs/src/assets/scss/docs-footer.scss
@@ -1,50 +1,50 @@
.docs-footer {
- display: flex;
- flex-direction: column;
- gap: 2rem;
- justify-content: space-between;
- align-items: baseline;
- font-size: .875rem;
-
- @media all and (max-width: 800px) {
- padding: 1.5rem 0 4rem;
- align-items: center;
- }
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+ justify-content: space-between;
+ align-items: baseline;
+ font-size: 0.875rem;
+
+ @media all and (max-width: 800px) {
+ padding: 1.5rem 0 4rem;
+ align-items: center;
+ }
}
.copyright p {
- margin: 0;
+ margin: 0;
}
.docs-socials-and-legal {
- display: flex;
- flex-direction: column;
- gap: 1rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
- @media all and (max-width: 800px) {
- text-align: center;
- }
+ @media all and (max-width: 800px) {
+ text-align: center;
+ }
}
.docs-switchers {
- display: flex;
- flex-wrap: wrap;
- gap: 1.5rem;
-
- .theme-switcher,
- .language-switcher {
- flex: 1 1 240px;
- }
-
- .theme-switcher {
- @media all and (max-width: 800px) {
- justify-content: center;
- }
- }
-
- .language-switcher {
- @media all and (max-width: 800px) {
- justify-content: center;
- }
- }
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1.5rem;
+
+ .theme-switcher,
+ .language-switcher {
+ flex: 1 1 240px;
+ }
+
+ .theme-switcher {
+ @media all and (max-width: 800px) {
+ justify-content: center;
+ }
+ }
+
+ .language-switcher {
+ @media all and (max-width: 800px) {
+ justify-content: center;
+ }
+ }
}
diff --git a/docs/src/assets/scss/docs-header.scss b/docs/src/assets/scss/docs-header.scss
index 15f21cf47eef..50cc4ad7bc3b 100644
--- a/docs/src/assets/scss/docs-header.scss
+++ b/docs/src/assets/scss/docs-header.scss
@@ -1,43 +1,43 @@
.site-header {
- padding: .75rem 0;
- border-top: 4px solid var(--link-color);
- border-bottom: 1px solid var(--divider-color);
- border-block-start: 4px solid var(--link-color);
- border-block-end: 1px solid var(--divider-color);
+ padding: 0.75rem 0;
+ border-top: 4px solid var(--link-color);
+ border-bottom: 1px solid var(--divider-color);
+ border-block-start: 4px solid var(--link-color);
+ border-block-end: 1px solid var(--divider-color);
- .docs-wrapper {
- display: grid;
- align-items: start;
- padding-top: 0;
- padding-bottom: 0;
- padding-block-start: 0;
- padding-block-end: 0;
- max-width: 1700px;
+ .docs-wrapper {
+ display: grid;
+ align-items: start;
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-block-start: 0;
+ padding-block-end: 0;
+ max-width: 1700px;
- @media all and (min-width: 1024px) {
- justify-content: space-between;
- }
- @media all and (min-width: 1700px) {
- margin: auto;
- }
- }
+ @media all and (min-width: 1024px) {
+ justify-content: space-between;
+ }
+ @media all and (min-width: 1700px) {
+ margin: auto;
+ }
+ }
}
.logo-link {
- display: inline-flex;
- justify-self: start;
- flex: none;
- place-content: center;
- grid-column: 1 / -1;
- grid-row: 1;
- padding: .5rem 0;
+ display: inline-flex;
+ justify-self: start;
+ flex: none;
+ place-content: center;
+ grid-column: 1 / -1;
+ grid-row: 1;
+ padding: 0.5rem 0;
}
.logo svg {
- display: inline-block;
- margin-bottom: -4px;
- margin-block-end: -4px;
- width: 100%;
- max-width: 100px;
- height: auto;
+ display: inline-block;
+ margin-bottom: -4px;
+ margin-block-end: -4px;
+ width: 100%;
+ max-width: 100px;
+ height: auto;
}
diff --git a/docs/src/assets/scss/docs.scss b/docs/src/assets/scss/docs.scss
index e981d8cc6af3..5068172a4899 100644
--- a/docs/src/assets/scss/docs.scss
+++ b/docs/src/assets/scss/docs.scss
@@ -1,216 +1,274 @@
/* docs layout styles */
html {
- scroll-behavior: smooth;
+ scroll-behavior: smooth;
}
.docs-aside__content {
- flex: 1;
+ flex: 1;
}
.docs-wrapper {
- padding: 0 var(--space-s-l);
- flex: 1;
- display: flex;
- flex-direction: column;
- max-width: 1700px;
-
- @media all and (min-width: 1024px) {
- display: grid;
- grid-template-columns: minmax(250px, 1fr) minmax(0, 3.5fr);
- align-items: stretch;
- }
- @media all and (min-width: 1700px) {
- margin: auto;
- }
+ padding: 0 var(--space-s-l);
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ max-width: 1700px;
+
+ @media all and (min-width: 1024px) {
+ display: grid;
+ grid-template-columns: minmax(250px, 1fr) minmax(0, 3.5fr);
+ align-items: stretch;
+ }
+ @media all and (min-width: 1700px) {
+ margin: auto;
+ }
}
.docs-nav {
- grid-column: 1 / 2;
- grid-row: 1 / 2;
- padding-top: var(--space-l-xl);
- padding-block-start: var(--space-l-xl);
- font-size: 0.875rem;
- display: grid;
- grid-auto-rows: max-content;
- align-items: start;
-
- @media all and (min-width: 1024px) {
- padding: var(--space-l-xl) 0;
- padding-right: var(--space-s-l);
- padding-inline-end: var(--space-s-l);
- border-right: 1px solid var(--divider-color);
- border-inline-end: 1px solid var(--divider-color);
- }
+ grid-column: 1 / 2;
+ grid-row: 1 / 2;
+ padding-top: var(--space-l-xl);
+ padding-block-start: var(--space-l-xl);
+ font-size: 0.875rem;
+ display: grid;
+ grid-auto-rows: max-content;
+ align-items: start;
+
+ @media all and (min-width: 1024px) {
+ padding: var(--space-l-xl) 0;
+ padding-right: var(--space-s-l);
+ padding-inline-end: var(--space-s-l);
+ border-right: 1px solid var(--divider-color);
+ border-inline-end: 1px solid var(--divider-color);
+ }
}
.docs-content {
- grid-column: 2 / 3;
- padding: var(--space-l-xl) 0;
- flex: 1;
-
- @media all and (min-width: 800px) {
- display: grid;
- grid-template-columns: minmax(0, 4fr) minmax(160px, 1fr);
- grid-gap: 1rem;
- }
-
- @media all and (min-width: 1024px) {
- padding: 0;
- }
-
- @media all and (min-width: 1300px) {
- grid-gap: 2rem;
- }
+ grid-column: 2 / 3;
+ padding: var(--space-l-xl) 0;
+ flex: 1;
+
+ @media all and (min-width: 800px) {
+ display: grid;
+ grid-template-columns: minmax(0, 4fr) minmax(160px, 1fr);
+ grid-gap: 1rem;
+ }
+
+ @media all and (min-width: 1024px) {
+ padding: 0;
+ }
+
+ @media all and (min-width: 1300px) {
+ grid-gap: 2rem;
+ }
}
.docs-main {
- flex: 1 1 68ch;
-
- @media all and (min-width: 800px) {
- padding-right: var(--space-s-l);
- padding-inline-end: var(--space-s-l);
- border-right: 1px solid var(--divider-color);
- border-inline-end: 1px solid var(--divider-color);
- }
-
- @media all and (min-width: 1024px) {
- padding: var(--space-l-xl) var(--space-l-2xl);
- }
+ flex: 1 1 68ch;
+
+ @media all and (min-width: 800px) {
+ padding-right: var(--space-s-l);
+ padding-inline-end: var(--space-s-l);
+ border-right: 1px solid var(--divider-color);
+ border-inline-end: 1px solid var(--divider-color);
+ }
+
+ @media all and (min-width: 1024px) {
+ padding: var(--space-l-xl) var(--space-l-2xl);
+ }
}
.docs-aside {
- grid-column: 2 / 3;
- display: flex;
- flex-direction: column;
+ grid-column: 2 / 3;
+ display: flex;
+ flex-direction: column;
- @media all and (min-width: 800px) {
- padding: var(--space-l-xl) 0;
- }
+ @media all and (min-width: 800px) {
+ padding: var(--space-l-xl) 0;
+ }
}
.docs-toc {
- flex: 1;
- align-self: center;
+ flex: 1;
+ align-self: center;
}
.docs-edit-link {
- border-top: 1px solid var(--divider-color);
- padding-top: 1.5rem;
- padding-block-start: 1.5rem;
- margin: 3rem 0;
+ border-top: 1px solid var(--divider-color);
+ padding-top: 1.5rem;
+ padding-block-start: 1.5rem;
+ margin: 3rem 0;
}
div.correct,
div.incorrect {
- position: relative;
-
- &::after {
- position: absolute;
- top: -22px;
- right: -22px;
- offset-inline-end: -22px;
- offset-block-start: -22px;
- }
-
- // Add space to the bottom if there is a Playground button.
- .c-btn.c-btn--playground ~ pre.line-numbers-mode {
- padding-bottom: 4.5rem;
- }
+ position: relative;
+
+ &::after {
+ position: absolute;
+ top: -22px;
+ right: -22px;
+ offset-inline-end: -22px;
+ offset-block-start: -22px;
+ }
+
+ // Add space to the bottom if there is a Playground button.
+ .c-btn.c-btn--playground ~ pre.line-numbers-mode {
+ padding-bottom: 4.5rem;
+ }
}
div.correct {
- &::after {
- content: url("data:image/svg+xml,%3Csvg width='45' height='44' viewBox='0 0 45 44' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.5' y='1' width='42' height='42' rx='21' fill='%23ECFDF3'/%3E%3Cpath d='M30.5 16L19.5 27L14.5 22' stroke='%2312B76A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Crect x='1.5' y='1' width='42' height='42' rx='21' stroke='white' stroke-width='2'/%3E%3C/svg%3E%0A");
- }
+ &::after {
+ content: url("data:image/svg+xml,%3Csvg width='45' height='44' viewBox='0 0 45 44' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.5' y='1' width='42' height='42' rx='21' fill='%23ECFDF3'/%3E%3Cpath d='M30.5 16L19.5 27L14.5 22' stroke='%2312B76A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Crect x='1.5' y='1' width='42' height='42' rx='21' stroke='white' stroke-width='2'/%3E%3C/svg%3E%0A");
+ }
}
div.incorrect {
- &::after {
- content: url("data:image/svg+xml,%3Csvg width='45' height='44' viewBox='0 0 45 44' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.5' y='1' width='42' height='42' rx='21' fill='%23FFF1F3'/%3E%3Cpath d='M28.5 16L16.5 28M16.5 16L28.5 28' stroke='%23F63D68' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Crect x='1.5' y='1' width='42' height='42' rx='21' stroke='white' stroke-width='2'/%3E%3C/svg%3E%0A");
- }
+ &::after {
+ content: url("data:image/svg+xml,%3Csvg width='45' height='44' viewBox='0 0 45 44' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.5' y='1' width='42' height='42' rx='21' fill='%23FFF1F3'/%3E%3Cpath d='M28.5 16L16.5 28M16.5 16L28.5 28' stroke='%23F63D68' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Crect x='1.5' y='1' width='42' height='42' rx='21' stroke='white' stroke-width='2'/%3E%3C/svg%3E%0A");
+ }
}
div.img-container {
- background-color: var(--img-background-color);
- border-radius: var(--border-radius);
+ background-color: var(--img-background-color);
+ border-radius: var(--border-radius);
- img {
- margin: 0 auto;
- }
+ img {
+ margin: 0 auto;
+ }
}
pre[class*="language-"] {
- position: relative;
+ position: relative;
}
.c-btn.c-btn--playground {
- position: absolute;
- font-size: var(--step--1);
- bottom: 1rem;
- right: 1rem;
- z-index: 1;
-
- @media all and (min-width: 768px) {
- bottom: 1.5rem;
- }
+ position: absolute;
+ font-size: var(--step--1);
+ bottom: 1rem;
+ right: 1rem;
+ z-index: 1;
+
+ @media all and (min-width: 768px) {
+ bottom: 1.5rem;
+ }
}
@media (hover: none) {
- .anchorjs-link {
- opacity: 1;
- }
+ .anchorjs-link {
+ opacity: 1;
+ }
}
#scroll-up-btn {
- width: 50px;
- height: 50px;
- display: none;
- position: fixed;
- right: 19.8vw;
- bottom: 35px;
- z-index: 1;
- font-size: 1.5rem;
- border-radius: 50%;
- color: var(--body-background-color);
- text-decoration: none;
- justify-content: center;
- align-items: center;
- background-color: var(--link-color);
-
- @media (max-width: 1299px) {
- right: 18.99vw;
- }
-
- @media (max-width: 1100px) {
- right: 19.4vw;
- }
-
- @media (max-width: 1060px) {
- right: 19.9vw;
- }
-
- @media (max-width: 1024px) {
- right: 22vw;
- }
-
- @media (max-width: 860px) {
- right: 22.2vw;
- }
-
- @media (max-width: 850px) {
- right: 22.6vw;
- }
-
- @media (max-width: 820px) {
- right: 23.4vw;
- }
-
- @media (max-width: 799px) {
- right: 35px;
- }
-
- @media (max-width: 600px) {
- right: 25px;
- }
+ width: 50px;
+ height: 50px;
+ display: none;
+ position: fixed;
+ right: 19.8vw;
+ bottom: 35px;
+ z-index: 1;
+ font-size: 1.5rem;
+ border-radius: 50%;
+ color: var(--body-background-color);
+ text-decoration: none;
+ justify-content: center;
+ align-items: center;
+ background-color: var(--link-color);
+
+ @media (max-width: 1299px) {
+ right: 18.99vw;
+ }
+
+ @media (max-width: 1100px) {
+ right: 19.4vw;
+ }
+
+ @media (max-width: 1060px) {
+ right: 19.9vw;
+ }
+
+ @media (max-width: 1024px) {
+ right: 22vw;
+ }
+
+ @media (max-width: 860px) {
+ right: 22.2vw;
+ }
+
+ @media (max-width: 850px) {
+ right: 22.6vw;
+ }
+
+ @media (max-width: 820px) {
+ right: 23.4vw;
+ }
+
+ @media (max-width: 799px) {
+ right: 35px;
+ }
+
+ @media (max-width: 600px) {
+ right: 25px;
+ }
+}
+
+.code-wrapper {
+ position: relative;
+
+ > pre {
+ padding-right: 3.5rem;
+ }
+}
+
+.copy-btn {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ padding: 0.5rem;
+ border-radius: var(--border-radius);
+ color: var(--body-text-color);
+ background-color: var(--color-neutral-100);
+ border: 1px solid var(--border-color);
+ transition: background-color 0.1s linear;
+
+ &:hover {
+ background-color: var(--color-neutral-200);
+ }
+
+ [data-theme="dark"] & {
+ background-color: var(--color-neutral-700);
+
+ &:hover {
+ background-color: var(--color-neutral-600);
+ }
+ }
+
+ &::after {
+ content: "Copied!";
+ padding: 0.5rem 0.75rem;
+ font-size: var(--step--1);
+ border-radius: var(--border-radius);
+ position: absolute;
+ top: 0;
+ right: 2.5rem;
+ height: 2.25rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: var(--color-neutral-100);
+ border: 1px solid var(--border-color);
+ opacity: 0;
+ pointer-events: none;
+
+ [data-theme="dark"] & {
+ background-color: var(--color-neutral-700);
+ }
+ }
+
+ &[data-copied="true"]::after {
+ opacity: 1;
+ }
}
diff --git a/docs/src/assets/scss/eslint-site-footer.scss b/docs/src/assets/scss/eslint-site-footer.scss
index 6ecb430c7038..e9b25d685b9d 100644
--- a/docs/src/assets/scss/eslint-site-footer.scss
+++ b/docs/src/assets/scss/eslint-site-footer.scss
@@ -1,64 +1,64 @@
.site-footer {
- text-align: center;
- background-color: var(--footer-background-color);
- border-top: 1px solid var(--divider-color);
- border-block-start: 1px solid var(--divider-color);
+ text-align: center;
+ background-color: var(--footer-background-color);
+ border-top: 1px solid var(--divider-color);
+ border-block-start: 1px solid var(--divider-color);
}
.footer-cta {
- .logo {
- margin-bottom: 2.5rem;
- margin-block-end: 2.5rem;
- }
+ .logo {
+ margin-bottom: 2.5rem;
+ margin-block-end: 2.5rem;
+ }
- .section-supporting-text {
- margin-bottom: 2.5rem;
- margin-block-end: 2.5rem;
- }
+ .section-supporting-text {
+ margin-bottom: 2.5rem;
+ margin-block-end: 2.5rem;
+ }
- .eslint-actions {
- justify-content: center;
- }
+ .eslint-actions {
+ justify-content: center;
+ }
}
.footer-legal-links {
- ul {
- li {
- display: inline-block;
- margin-right: .5rem;
- margin-inline-end: .5rem;
+ ul {
+ li {
+ display: inline-block;
+ margin-right: 0.5rem;
+ margin-inline-end: 0.5rem;
- &:not(:last-of-type)::after {
- content: "|";
- margin-left: .5rem;
- margin-inline-start: .5rem;
- }
- }
- }
+ &:not(:last-of-type)::after {
+ content: "|";
+ margin-left: 0.5rem;
+ margin-inline-start: 0.5rem;
+ }
+ }
+ }
}
.footer-legal-section {
- font-size: var(--step--1);
- padding: 2rem 1rem;
+ font-size: var(--step--1);
+ padding: 2rem 1rem;
}
.copyright {
- max-width: 1100px;
- margin: 0 auto;
+ max-width: 1100px;
+ margin: 0 auto;
}
.footer-middle {
- padding-top: 2rem;
- padding-bottom: 2rem;
- padding-block-start: 2rem;
- padding-block-end: 2rem;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 2rem;
+ padding-top: 2rem;
+ padding-bottom: 2rem;
+ padding-block-start: 2rem;
+ padding-block-end: 2rem;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2rem;
- @media all and (min-width: 768px) {
- flex-direction: row;
- justify-content: space-between;
- }
+ @media all and (min-width: 768px) {
+ flex-direction: row;
+ justify-content: space-between;
+ }
}
diff --git a/docs/src/assets/scss/eslint-site-header.scss b/docs/src/assets/scss/eslint-site-header.scss
index 892ebc7e6250..b970e673efea 100644
--- a/docs/src/assets/scss/eslint-site-header.scss
+++ b/docs/src/assets/scss/eslint-site-header.scss
@@ -1,40 +1,40 @@
.site-header {
- padding: .75rem 0;
- border-top: 4px solid var(--link-color);
- border-block-start: 4px solid var(--link-color);
- border-bottom: 1px solid var(--divider-color);
- border-block-end: 1px solid var(--divider-color);
+ padding: 0.75rem 0;
+ border-top: 4px solid var(--link-color);
+ border-block-start: 4px solid var(--link-color);
+ border-bottom: 1px solid var(--divider-color);
+ border-block-end: 1px solid var(--divider-color);
- .content-container {
- display: grid;
- align-items: start;
- padding-top: 0;
- padding-bottom: 0;
- padding-block-start: 0;
- padding-block-end: 0;
+ .content-container {
+ display: grid;
+ align-items: start;
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-block-start: 0;
+ padding-block-end: 0;
- @media all and (min-width: 680px) {
- justify-content: space-between;
- }
- }
+ @media all and (min-width: 680px) {
+ justify-content: space-between;
+ }
+ }
}
.logo-link {
- display: inline-flex;
- justify-self: start;
- flex: none;
- place-content: center;
- grid-column: 1 / -1;
- grid-row: 1;
- padding: .5rem 0;
- z-index: 2;
+ display: inline-flex;
+ justify-self: start;
+ flex: none;
+ place-content: center;
+ grid-column: 1 / -1;
+ grid-row: 1;
+ padding: 0.5rem 0;
+ z-index: 2;
}
.logo svg {
- display: inline-block;
- margin-bottom: -4px;
- margin-block-end: -4px;
- width: 100%;
- max-width: 100px;
- height: auto;
+ display: inline-block;
+ margin-bottom: -4px;
+ margin-block-end: -4px;
+ width: 100%;
+ max-width: 100px;
+ height: auto;
}
diff --git a/docs/src/assets/scss/forms.scss b/docs/src/assets/scss/forms.scss
index 3ca145257342..28dfde5750b0 100644
--- a/docs/src/assets/scss/forms.scss
+++ b/docs/src/assets/scss/forms.scss
@@ -1,49 +1,58 @@
.c-custom-select {
- appearance: none;
- box-sizing: border-box;
- display: block;
- width: 100%;
- max-width: 100%;
- min-width: 0;
- padding: .625rem .875rem;
- padding-right: calc(.875rem * 2.5);
- padding-inline-end: calc(.875rem * 2.5);
- font: inherit;
- color: var(--body-text-color);
- line-height: 1.3;
- border: 1px solid var(--border-color);
- border-radius: var(--border-radius);
- box-shadow: var(--shadow-xs);
- background-color: var(--body-background-color);
- background-image: url("data:image/svg+xml,%3Csvg width='20' height='21' viewBox='0 0 20 21' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 7.60938L10 12.6094L15 7.60938' stroke='%23667085' stroke-width='1.66667' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"), linear-gradient(to bottom, var(--body-background-color) 0%, var(--body-background-color) 100%);
- background-repeat: no-repeat, repeat;
- background-position: right .875rem top 50%, 0 0;
- background-size: 1em auto, 100%;
+ appearance: none;
+ box-sizing: border-box;
+ display: block;
+ width: 100%;
+ max-width: 100%;
+ min-width: 0;
+ padding: 0.625rem 0.875rem;
+ padding-right: calc(0.875rem * 2.5);
+ padding-inline-end: calc(0.875rem * 2.5);
+ font: inherit;
+ color: var(--body-text-color);
+ line-height: 1.3;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ box-shadow: var(--shadow-xs);
+ background-color: var(--body-background-color);
+ background-image: url("data:image/svg+xml,%3Csvg width='20' height='21' viewBox='0 0 20 21' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 7.60938L10 12.6094L15 7.60938' stroke='%23667085' stroke-width='1.66667' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"),
+ linear-gradient(
+ to bottom,
+ var(--body-background-color) 0%,
+ var(--body-background-color) 100%
+ );
+ background-repeat: no-repeat, repeat;
+ background-position:
+ right 0.875rem top 50%,
+ 0 0;
+ background-size:
+ 1em auto,
+ 100%;
}
.label__text.label__text {
- display: flex;
- align-items: center;
- gap: .5rem;
- font-size: .875rem;
- font-family: var(--text-font);
- color: inherit;
- font-weight: 400;
- line-height: 1.5;
- margin-bottom: .25rem;
- margin-block-end: .25rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ font-size: 0.875rem;
+ font-family: var(--text-font);
+ color: inherit;
+ font-weight: 400;
+ line-height: 1.5;
+ margin-bottom: 0.25rem;
+ margin-block-end: 0.25rem;
}
input {
- border: 1px solid var(--border-color);
- border-radius: var(--border-radius);
- padding: .625rem .875rem;
- font: inherit;
- font-size: 1rem;
- display: block;
- min-width: 0;
- line-height: 1.3;
- max-width: 100%;
- background-color: var(--body-background-color);
- color: inherit;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 0.625rem 0.875rem;
+ font: inherit;
+ font-size: 1rem;
+ display: block;
+ min-width: 0;
+ line-height: 1.3;
+ max-width: 100%;
+ background-color: var(--body-background-color);
+ color: inherit;
}
diff --git a/docs/src/assets/scss/foundations.scss b/docs/src/assets/scss/foundations.scss
index 68e44651f4fb..6237fef1d022 100644
--- a/docs/src/assets/scss/foundations.scss
+++ b/docs/src/assets/scss/foundations.scss
@@ -1,6 +1,6 @@
::selection {
- background-color: var(--color-brand);
- color: #fff;
+ background-color: var(--color-brand);
+ color: #fff;
}
h1:target,
@@ -9,230 +9,231 @@ h3:target,
h4:target,
h5:target,
h6:target {
- background-color: var(--lighter-background-color);
+ background-color: var(--lighter-background-color);
+ border-radius: var(--border-radius);
}
*:focus {
- outline: none;
+ outline: none;
}
*:focus-visible {
- outline: 2px solid var(--outline-color);
- outline-offset: 3px;
+ outline: 2px solid var(--outline-color);
+ outline-offset: 3px;
}
*.focus-visible {
- outline: 2px solid var(--outline-color);
- outline-offset: 3px;
+ outline: 2px solid var(--outline-color);
+ outline-offset: 3px;
}
*:focus:not(:focus-visible) {
- outline: 1px solid transparent;
- box-shadow: none;
+ outline: 1px solid transparent;
+ box-shadow: none;
}
.js-focus-visible *:focus:not(.focus-visible) {
- outline: 1px solid transparent;
- box-shadow: none;
+ outline: 1px solid transparent;
+ box-shadow: none;
}
input:focus-visible {
- outline: 2px solid var(--link-color);
- border-color: var(--border-color);
+ outline: 2px solid var(--link-color);
+ border-color: var(--border-color);
}
input:focus {
- outline: 2px solid transparent;
- box-shadow: 0 0 0 2px var(--link-color);
+ outline: 2px solid transparent;
+ box-shadow: 0 0 0 2px var(--link-color);
}
*,
*::before,
*::after {
- box-sizing: border-box;
+ box-sizing: border-box;
}
html {
- accent-color: var(--link-color);
- background-color: var(--body-background-color);
- height: 100%;
- font-family: var(--text-font);
- overflow-x: hidden;
- caret-color: var(--link-color);
+ accent-color: var(--link-color);
+ background-color: var(--body-background-color);
+ height: 100%;
+ font-family: var(--text-font);
+ overflow-x: hidden;
+ caret-color: var(--link-color);
}
body {
- font-size: var(--step-0);
- position: relative;
- margin: 0 auto;
- line-height: 1.5;
- display: flex;
- flex-direction: column;
- min-height: 100%;
- background-color: var(--body-background-color);
- color: var(--body-text-color);
+ font-size: var(--step-0);
+ position: relative;
+ margin: 0 auto;
+ line-height: 1.5;
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+ background-color: var(--body-background-color);
+ color: var(--body-text-color);
}
#skip-link {
- position: fixed;
- top: -30em;
- left: 0;
- right: auto;
- offset-block-start: -30em;
- offset-inline-start: 0;
- offset-inline-end: auto;
- z-index: 999;
- transition: top .1s linear;
-
- &:focus {
- outline: 2px solid transparent;
- top: 2px;
- offset-block-start: 2px;
- }
-
- &:focus-visible {
- outline: 2px solid transparent;
- top: 2px;
- offset-block-start: 2px;
- }
+ position: fixed;
+ top: -30em;
+ left: 0;
+ right: auto;
+ offset-block-start: -30em;
+ offset-inline-start: 0;
+ offset-inline-end: auto;
+ z-index: 999;
+ transition: top 0.1s linear;
+
+ &:focus {
+ outline: 2px solid transparent;
+ top: 2px;
+ offset-block-start: 2px;
+ }
+
+ &:focus-visible {
+ outline: 2px solid transparent;
+ top: 2px;
+ offset-block-start: 2px;
+ }
}
main {
- flex: 1;
+ flex: 1;
- &:focus {
- outline: none;
- }
+ &:focus {
+ outline: none;
+ }
- &:target {
- outline: none;
- }
+ &:target {
+ outline: none;
+ }
}
hr {
- border: none;
- border-top: 1px solid var(--divider-color);
- border-block-start: 1px solid var(--divider-color);
- background: none;
- height: 0;
- margin: 2rem 0;
+ border: none;
+ border-top: 1px solid var(--divider-color);
+ border-block-start: 1px solid var(--divider-color);
+ background: none;
+ height: 0;
+ margin: 2rem 0;
}
.content-container {
- width: 100%;
- margin: 0 auto;
- padding: var(--space-xl-3xl) calc(1rem + 1vw);
- max-width: 1700px;
+ width: 100%;
+ margin: 0 auto;
+ padding: var(--space-xl-3xl) calc(1rem + 1vw);
+ max-width: 1700px;
- @media all and (min-width: 1700px) {
- margin: auto;
- }
+ @media all and (min-width: 1700px) {
+ margin: auto;
+ }
}
.section-head {
- .section-supporting-text {
- text-align: center;
- max-width: 768px;
- margin: 0 auto var(--space-l-2xl);
- }
+ .section-supporting-text {
+ text-align: center;
+ max-width: 768px;
+ margin: 0 auto var(--space-l-2xl);
+ }
}
.section-foot {
- margin-top: var(--space-l-2xl);
- margin-block-start: var(--space-l-2xl);
+ margin-top: var(--space-l-2xl);
+ margin-block-start: var(--space-l-2xl);
- .section-supporting-text {
- text-align: center;
- font-size: var(--step--1);
- max-width: 768px;
- margin: 0 auto;
- }
+ .section-supporting-text {
+ text-align: center;
+ font-size: var(--step--1);
+ max-width: 768px;
+ margin: 0 auto;
+ }
}
.section-title {
- margin-bottom: 1rem;
- margin-block-end: 1rem;
+ margin-bottom: 1rem;
+ margin-block-end: 1rem;
}
.section-supporting-text {
- font-size: var(--step-1);
+ font-size: var(--step-1);
}
code,
pre {
- font-family: var(--mono-font);
- font-variant-ligatures: none;
+ font-family: var(--mono-font);
+ font-variant-ligatures: none;
}
code {
- color: var(--link-color);
+ color: var(--link-color);
- pre & {
- color: unset;
- }
+ pre & {
+ color: unset;
+ }
}
.c-icon {
- color: var(--icon-color);
- flex: none;
- transition: all .2s linear;
+ color: var(--icon-color);
+ flex: none;
+ transition: all 0.2s linear;
- @media (-ms-high-contrast: active) {
- color: windowText;
- }
+ @media (-ms-high-contrast: active) {
+ color: windowText;
+ }
- @media (forced-colors: active) {
- color: canvasText;
- }
+ @media (forced-colors: active) {
+ color: canvasText;
+ }
}
table {
- width: 100%;
- margin: 2.5rem 0;
- border-collapse: collapse;
- border: 1px solid var(--divider-color);
+ width: 100%;
+ margin: 2.5rem 0;
+ border-collapse: collapse;
+ border: 1px solid var(--divider-color);
- td {
- padding: .25rem .5rem;
- border: 1px solid var(--divider-color);
- }
+ td {
+ padding: 0.25rem 0.5rem;
+ border: 1px solid var(--divider-color);
+ }
- th {
- background-color: var(--lightest-background-color);
- padding: .25rem .5rem;
- }
+ th {
+ background-color: var(--lightest-background-color);
+ padding: 0.25rem 0.5rem;
+ }
}
.c-btn,
button,
a {
- .c-icon:hover {
- color: var(--link-color);
- }
+ .c-icon:hover {
+ color: var(--link-color);
+ }
}
a {
- color: var(--link-color);
- transition: color .1s linear;
+ color: var(--link-color);
+ transition: color 0.1s linear;
- .side-header & {
- color: inherit;
- text-decoration: none;
- }
+ .side-header & {
+ color: inherit;
+ text-decoration: none;
+ }
}
svg {
- flex: none;
- transition: color .1s linear;
+ flex: none;
+ transition: color 0.1s linear;
}
p {
- margin: 0 0 1.5em;
+ margin: 0 0 1.5em;
- :matches(nav, .posts-collection) & {
- margin-bottom: .75em;
- margin-block-end: .75em;
- }
+ :matches(nav, .posts-collection) & {
+ margin-bottom: 0.75em;
+ margin-block-end: 0.75em;
+ }
}
p,
@@ -242,114 +243,114 @@ h3,
h4,
h5,
h6 {
- overflow-wrap: break-word;
+ overflow-wrap: break-word;
}
ul,
ol {
- margin-top: 0;
- margin-block-start: 0;
+ margin-top: 0;
+ margin-block-start: 0;
- li {
- margin: 0 0 .75em;
- }
+ li {
+ margin: 0 0 0.75em;
+ }
- .person__bio & {
- padding-left: 1.5rem;
- padding-inline-start: 1.5rem;
- }
+ .person__bio & {
+ padding-left: 1.5rem;
+ padding-inline-start: 1.5rem;
+ }
}
.docs-main ul,
.post-main ul,
.docs-main ol,
.post-main ol {
- margin: 1rem 0;
+ margin: 1rem 0;
}
ul[role="list"] {
- list-style: none;
- margin: 0;
- padding: 0;
+ list-style: none;
+ margin: 0;
+ padding: 0;
- li {
- margin: 0;
- }
+ li {
+ margin: 0;
+ }
}
ol {
- list-style: decimal;
+ list-style: decimal;
- li::marker {
- color: var(--link-color);
- }
+ li::marker {
+ color: var(--link-color);
+ }
}
p:empty {
- margin: 0;
- display: none;
+ margin: 0;
+ display: none;
}
figure {
- margin-bottom: 4rem;
- margin-block-end: 4rem;
+ margin-bottom: 4rem;
+ margin-block-end: 4rem;
- img {
- margin-bottom: 1rem;
- margin-block-end: 1rem;
- }
+ img {
+ margin-bottom: 1rem;
+ margin-block-end: 1rem;
+ }
- figcaption {
- color: var(--grey);
- }
+ figcaption {
+ color: var(--grey);
+ }
}
img {
- display: block;
- position: relative;
- max-width: 100%;
- height: auto;
+ display: block;
+ position: relative;
+ max-width: 100%;
+ height: auto;
}
nav {
- /* rarely do we display bullets for lists in navigation */
- ol,
- ul {
- list-style: none;
- margin: 0;
- padding: 0;
- }
+ /* rarely do we display bullets for lists in navigation */
+ ol,
+ ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
}
.video {
- width: 90%;
- max-width: 1400px;
- margin: 2em auto;
+ width: 90%;
+ max-width: 1400px;
+ margin: 2em auto;
- iframe {
- aspect-ratio: 16 / 9;
- width: 100%;
- height: auto;
- }
+ iframe {
+ aspect-ratio: 16 / 9;
+ width: 100%;
+ height: auto;
+ }
}
@media (prefers-reduced-motion: no-preference) {
- *:focus-visible,
- *.focus-visible {
- transition: outline-offset .15s linear;
- outline-offset: 3px;
- }
+ *:focus-visible,
+ *.focus-visible {
+ transition: outline-offset 0.15s linear;
+ outline-offset: 3px;
+ }
}
/* typography */
.eyebrow {
- color: var(--link-color);
- font-size: 1rem;
- font-weight: 500;
- display: block;
- margin-bottom: 1.5rem;
- margin-block-end: 1.5rem;
+ color: var(--link-color);
+ font-size: 1rem;
+ font-weight: 500;
+ display: block;
+ margin-bottom: 1.5rem;
+ margin-block-end: 1.5rem;
}
h1,
@@ -358,11 +359,12 @@ h3,
h4,
h5,
h6 {
- font-family: var(--display-font);
- color: var(--headings-color);
- font-weight: 500;
- margin-top: 0;
- margin-block-start: 0;
+ font-family: var(--display-font);
+ color: var(--headings-color);
+ font-weight: 500;
+ margin-top: 0;
+ margin-block-start: 0;
+ padding: 0.25rem 0;
}
h2,
@@ -370,61 +372,61 @@ h3,
h4,
h5,
h6 {
- .docs-main &,
- .components-main & {
- margin-top: 3rem;
- margin-bottom: 1.5rem;
- margin-block-start: 3rem;
- margin-block-end: 1.5rem;
+ .docs-main &,
+ .components-main & {
+ margin-top: 3rem;
+ margin-bottom: 1.5rem;
+ margin-block-start: 3rem;
+ margin-block-end: 1.5rem;
- &:first-child {
- margin-top: 0;
- margin-block-start: 0;
- }
- }
+ &:first-child {
+ margin-top: 0;
+ margin-block-start: 0;
+ }
+ }
}
small,
caption,
cite,
figcaption {
- font-size: var(--step--1);
+ font-size: var(--step--1);
}
h6,
.h6 {
- font-size: var(--step-0);
+ font-size: var(--step-0);
}
h5,
.h5 {
- font-size: var(--step-0); // 20
+ font-size: var(--step-0); // 20
}
h4,
.h4 {
- font-size: var(--step-1); // 24
+ font-size: var(--step-1); // 24
}
h3,
.h3 {
- font-size: var(--step-2);
- line-height: 1.2;
+ font-size: var(--step-2);
+ line-height: 1.2;
}
h2,
.h2 {
- font-size: var(--step-3);
- line-height: 1.2;
+ font-size: var(--step-3);
+ line-height: 1.2;
}
h1,
.h1 {
- font-size: var(--step-4);
- line-height: 1.2;
+ font-size: var(--step-4);
+ line-height: 1.2;
}
.h0 {
- font-size: var(--step-6);
- line-height: 1.2;
+ font-size: var(--step-6);
+ line-height: 1.2;
}
diff --git a/docs/src/assets/scss/languages.scss b/docs/src/assets/scss/languages.scss
index 9b29097f0d49..468d55b319b4 100644
--- a/docs/src/assets/scss/languages.scss
+++ b/docs/src/assets/scss/languages.scss
@@ -1,55 +1,55 @@
.languages-list {
- margin: 0;
- padding: 0;
- font-size: var(--step-0);
-
- li {
- margin: 0;
-
- &:last-of-type a {
- border-bottom: 0;
- }
- }
-
- a {
- color: inherit;
- width: 100%;
- padding: .75rem .1rem;
- text-decoration: none;
- display: block;
- display: flex;
- align-items: center;
- border-bottom: 1px solid var(--divider-color);
- border-block-end: 1px solid var(--divider-color);
-
- &[aria-current="true"] {
- font-weight: 500;
- color: var(--link-color);
-
- &::after {
- content: " âī¸";
- white-space: pre;
- color: rgba(100%, 0%, 0%, 0);
- text-shadow: 0 0 0 var(--headings-color);
- }
- }
-
- &:hover {
- color: var(--link-color);
- }
- }
+ margin: 0;
+ padding: 0;
+ font-size: var(--step-0);
+
+ li {
+ margin: 0;
+
+ &:last-of-type a {
+ border-bottom: 0;
+ }
+ }
+
+ a {
+ color: inherit;
+ width: 100%;
+ padding: 0.75rem 0.1rem;
+ text-decoration: none;
+ display: block;
+ display: flex;
+ align-items: center;
+ border-bottom: 1px solid var(--divider-color);
+ border-block-end: 1px solid var(--divider-color);
+
+ &[aria-current="true"] {
+ font-weight: 500;
+ color: var(--link-color);
+
+ &::after {
+ content: " âī¸";
+ white-space: pre;
+ color: rgba(100%, 0%, 0%, 0);
+ text-shadow: 0 0 0 var(--headings-color);
+ }
+ }
+
+ &:hover {
+ color: var(--link-color);
+ }
+ }
}
.languages-section .flag {
- font-size: 2em;
- margin-right: .5rem;
- margin-inline-end: .5rem;
+ font-size: 2em;
+ margin-right: 0.5rem;
+ margin-inline-end: 0.5rem;
}
.languages-section .languages-list {
- font-size: var(--step-1);
- border-left: 4px solid var(--tab-border-color);
- padding-left: 1rem;
- border-inline-start: 4px solid var(--tab-border-color);
- padding-inline-start: 1rem;
+ font-size: var(--step-1);
+ border-left: 4px solid var(--tab-border-color);
+ padding-left: 1rem;
+ border-inline-start: 4px solid var(--tab-border-color);
+ padding-inline-start: 1rem;
}
diff --git a/docs/src/assets/scss/print.scss b/docs/src/assets/scss/print.scss
index 39dcc9470cde..b87f598cd837 100644
--- a/docs/src/assets/scss/print.scss
+++ b/docs/src/assets/scss/print.scss
@@ -6,34 +6,34 @@ p::first-line,
div::first-line,
blockquote::first-line,
li::first-line {
- background: transparent !important;
- color: #000 !important;
- box-shadow: none !important;
- text-shadow: none !important;
+ background: transparent !important;
+ color: #000 !important;
+ box-shadow: none !important;
+ text-shadow: none !important;
}
body {
- width: 100% !important;
- margin: 0 !important;
- padding: 0 !important;
- line-height: 1.45;
- font-family: Helvetica, sans-serif;
- color: #000;
- background: none;
- font-size: 14pt;
+ width: 100% !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ line-height: 1.45;
+ font-family: Helvetica, sans-serif;
+ color: #000;
+ background: none;
+ font-size: 14pt;
}
.grid {
- display: block;
+ display: block;
}
main,
.docs-content,
.docs-wrapper {
- display: block;
- width: 100%;
- max-width: 75ch;
- margin: 1cm auto;
+ display: block;
+ width: 100%;
+ max-width: 75ch;
+ margin: 1cm auto;
}
/* Headings */
@@ -43,64 +43,66 @@ h3,
h4,
h5,
h6 {
- page-break-after: avoid;
+ page-break-after: avoid;
}
h1 {
- font-size: 19pt;
+ font-size: 19pt;
}
h2 {
- font-size: 17pt;
+ font-size: 17pt;
}
h3 {
- font-size: 15pt;
+ font-size: 15pt;
}
h4,
h5,
h6 {
- font-size: 14pt;
+ font-size: 14pt;
}
p,
h2,
h3 {
- orphans: 3;
- widows: 3;
+ orphans: 3;
+ widows: 3;
}
code {
- font: 12pt Courier, monospace;
+ font:
+ 12pt Courier,
+ monospace;
}
blockquote {
- margin: 1.2em;
- padding: 1em;
- font-size: 12pt;
+ margin: 1.2em;
+ padding: 1em;
+ font-size: 12pt;
}
hr {
- background-color: #ccc;
+ background-color: #ccc;
}
/* Images */
img {
- max-width: 100% !important;
+ max-width: 100% !important;
}
a img {
- border: none;
+ border: none;
}
/* Links */
a:link,
a:visited {
- background: transparent;
- font-weight: 700;
- text-decoration: underline;
- color: #333;
+ background: transparent;
+ font-weight: 700;
+ text-decoration: underline;
+ color: #333;
}
// a:link[href^="http://"]:after,
@@ -110,104 +112,105 @@ a:visited {
// }
abbr[title]::after {
- content: " ("attr(title) ")";
+ content: " (" attr(title) ")";
}
/* Don't show linked images */
-a[href^="http://"] {
- color: #000;
+a[href^="http://"]
+{
+ color: #000;
}
a[href$=".jpg"]::after,
a[href$=".jpeg"]::after,
a[href$=".gif"]::after,
a[href$=".png"]::after {
- content: " ("attr(href) ") ";
- display: none;
+ content: " (" attr(href) ") ";
+ display: none;
}
/* Don't show links that are fragment identifiers, or use the `javascript:` pseudo protocol .. taken from html5boilerplate */
a[href^="#"]::after,
a[href^="javascript:"]::after {
- content: "";
+ content: "";
}
/* Table */
table {
- margin: 1px;
- text-align: left;
+ margin: 1px;
+ text-align: left;
}
th {
- border-bottom: 1px solid #333;
- font-weight: bold;
+ border-bottom: 1px solid #333;
+ font-weight: bold;
}
td {
- border-bottom: 1px solid #333;
+ border-bottom: 1px solid #333;
}
th,
td {
- padding: 4px 10px 4px 0;
+ padding: 4px 10px 4px 0;
}
tfoot {
- font-style: italic;
+ font-style: italic;
}
caption {
- background: #fff;
- margin-bottom: 2em;
- text-align: left;
+ background: #fff;
+ margin-bottom: 2em;
+ text-align: left;
}
thead {
- display: table-header-group;
+ display: table-header-group;
}
img,
tr {
- page-break-inside: avoid;
+ page-break-inside: avoid;
}
body > *:not(main),
aside,
*[class*="sidebar"] {
- display: none;
+ display: none;
}
button,
.c-btn.c-btn--playground,
.docs-edit-link {
- display: none;
+ display: none;
}
a[href^="http"]:not([href*="eslint.org"])::after {
- content: " ("attr(href) ")";
+ content: " (" attr(href) ")";
}
.resource a::after {
- display: none;
+ display: none;
}
ul {
- page-break-inside: avoid;
+ page-break-inside: avoid;
}
.docs-toc,
.docs-index,
.docs-aside,
#skip-link {
- display: none;
+ display: none;
}
@media print {
- @page {
- margin: 1cm;
- }
+ @page {
+ margin: 1cm;
+ }
}
#scroll-up-btn {
- display: none;
+ display: none;
}
diff --git a/docs/src/assets/scss/styles.scss b/docs/src/assets/scss/styles.scss
index 8907a6c4bf9f..7b4b4b43c5c9 100644
--- a/docs/src/assets/scss/styles.scss
+++ b/docs/src/assets/scss/styles.scss
@@ -1,35 +1,37 @@
-@import "tokens/themes";
-@import "tokens/spacing";
-@import "tokens/typography";
-@import "tokens/ui";
+@use "tokens/themes";
+@use "tokens/spacing";
+@use "tokens/typography";
+@use "tokens/ui";
+@use "tokens/opacity";
-@import "foundations";
-@import "syntax-highlighter";
-@import "docs-header";
-@import "docs-footer";
-@import "eslint-site-footer";
-@import "eslint-site-header";
-@import "forms";
-@import "docs";
-@import "versions";
-@import "languages";
+@use "foundations";
+@use "syntax-highlighter";
+@use "docs-header";
+@use "docs-footer";
+@use "eslint-site-footer";
+@use "eslint-site-header";
+@use "forms";
+@use "docs";
+@use "versions";
+@use "languages";
-@import "components/buttons";
-@import "components/docs-navigation";
-@import "components/toc";
-@import "components/search";
-@import "components/alert";
-@import "components/rules";
-@import "components/social-icons";
-@import "components/hero";
-@import "components/theme-switcher";
-@import "components/version-switcher";
-@import "components/language-switcher";
-@import "components/docs-index"; // docs index on the main docs pages
-@import "components/index"; // used in component library
-@import "components/tabs";
-@import "components/resources";
+@use "components/buttons";
+@use "components/docs-navigation";
+@use "components/toc";
+@use "components/search";
+@use "components/alert";
+@use "components/rules";
+@use "components/social-icons";
+@use "components/hero";
+@use "components/theme-switcher";
+@use "components/version-switcher";
+@use "components/language-switcher";
+@use "components/docs-index"; // docs index on the main docs pages
+@use "components/index"; // used in component library
+@use "components/tabs";
+@use "components/resources";
+@use "components/logo";
-@import "carbon-ads";
+@use "ads";
-@import "utilities";
+@use "utilities";
diff --git a/docs/src/assets/scss/syntax-highlighter.scss b/docs/src/assets/scss/syntax-highlighter.scss
index cb744db0e381..868a96fcc722 100644
--- a/docs/src/assets/scss/syntax-highlighter.scss
+++ b/docs/src/assets/scss/syntax-highlighter.scss
@@ -1,74 +1,61 @@
code[class*="language-"],
pre[class*="language-"] {
- font-family:
- var(--mono-font),
- Consolas,
- Monaco,
- "Andale Mono",
- "Ubuntu Mono",
- monospace;
- font-size: 1em;
- text-align: left;
- white-space: pre;
- word-spacing: normal;
- word-break: normal;
- word-wrap: normal;
- line-height: 1.5;
- font-variant-ligatures: none;
- tab-size: 4;
- hyphens: none;
+ font-family: var(--mono-font), Consolas, Monaco, "Andale Mono",
+ "Ubuntu Mono", monospace;
+ font-size: 1em;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+ font-variant-ligatures: none;
+ tab-size: 4;
+ hyphens: none;
}
@media print {
- code[class*="language-"],
- pre[class*="language-"] {
- text-shadow: none;
- }
+ code[class*="language-"],
+ pre[class*="language-"] {
+ text-shadow: none;
+ }
}
/* Code blocks */
pre[class*="language-"] {
- padding: 1.5rem;
- margin: 1.5rem 0;
- overflow: auto;
- border-radius: var(--border-radius);
- background-color: var(--lightest-background-color);
- color: var(--color-neutral-900);
-
- [data-theme="dark"] & {
- color: var(--color-neutral-100);
- }
-
- &.line-numbers-mode {
- padding-left: calc(1.5rem + 2.4em + 1.2rem);
- }
+ padding: 1.5rem;
+ margin: 1.5rem 0;
+ overflow: auto;
+ border-radius: var(--border-radius);
+ background-color: var(--lightest-background-color);
+ color: var(--code-text-color);
+
+ &.line-numbers-mode {
+ padding-left: calc(1.5rem + 2.4em + 1.2rem);
+ }
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
- background-color: var(--lightest-background-color);
+ background-color: var(--lightest-background-color);
}
/* Inline code */
:not(pre) > code[class*="language-"] {
- padding: 0.1em;
- border-radius: 0.3em;
- white-space: normal;
+ padding: 0.1em;
+ border-radius: 0.3em;
+ white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
- color: #6e7f8e;
-
- [data-theme="dark"] & {
- color: #8e9fae;
- }
+ color: var(--code-comments-color);
}
.token.namespace {
- opacity: 0.7;
+ opacity: 0.7;
}
.token.selector,
@@ -77,46 +64,91 @@ pre[class*="language-"] {
.token.char,
.token.builtin,
.token.inserted {
- color: var(--link-color);
+ color: var(--link-color);
}
.token.atrule,
.token.attr-value,
.token.keyword {
- color: var(--link-color);
+ color: var(--link-color);
}
.token.important,
.token.bold {
- font-weight: bold;
+ font-weight: bold;
}
.token.italic {
- font-style: italic;
+ font-style: italic;
}
.token.entity {
- cursor: help;
+ cursor: help;
+}
+
+.token.eslint-marked {
+ /* Draw the wavy line. */
+ background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23f14c4c'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
+ repeat-x bottom left;
+
+ /*
+ * Since the character width of the token span is not constant,
+ * if we use it as is, we may see a shift in the border.
+ * To make the border shift less noticeable, draw it with a smaller width.
+ */
+ background-size: 4.4px auto;
+}
+
+.token.eslint-marked-on-line-feed {
+ /* Use `padding` to give it width so the marker on line feed code is visible. */
+ padding-right: 8px;
+}
+
+.token.eslint-marked:not(.eslint-marked-on-line-feed)
+ + .token.eslint-marked-on-line-feed {
+ /*
+ * If there is a marker before the same line,
+ * there is no need to make visible the marker on the line feed code.
+ */
+ padding-right: 0;
+}
+
+.token.eslint-marked-on-zero-width {
+ position: relative;
+
+ /* Delete the wavy line. */
+ background: none;
+}
+
+.token.eslint-marked-on-zero-width::before {
+ content: "";
+ position: absolute;
+ bottom: 0;
+ left: -2px;
+ border-left: 3px solid transparent;
+ border-right: 3px solid transparent;
+ border-bottom: 4px solid #d11;
}
.line-numbers-wrapper {
- position: absolute;
- top: 0;
- left: 1.5rem;
- text-align: right;
- padding-top: 1.5rem;
- font-size: 1em;
- font-family: var(--mono-font), Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
- line-height: 1.5;
- color: var(--icon-color);
- font-variant-ligatures: none;
-
- .line-number {
- user-select: none;
- color: var(--icon-color);
- display: inline-block;
- font-variant-numeric: tabular-nums;
- text-align: right;
- width: 1.2em;
- }
+ position: absolute;
+ top: 0;
+ left: 1.5rem;
+ text-align: right;
+ padding-top: 1.5rem;
+ font-size: 1em;
+ font-family: var(--mono-font), Consolas, Monaco, "Andale Mono",
+ "Ubuntu Mono", monospace;
+ line-height: 1.5;
+ color: var(--icon-color);
+ font-variant-ligatures: none;
+
+ .line-number {
+ user-select: none;
+ color: var(--icon-color);
+ display: inline-block;
+ font-variant-numeric: tabular-nums;
+ text-align: right;
+ width: 1.2em;
+ }
}
diff --git a/docs/src/assets/scss/tokens/opacity.scss b/docs/src/assets/scss/tokens/opacity.scss
new file mode 100644
index 000000000000..2a6fb240ed6f
--- /dev/null
+++ b/docs/src/assets/scss/tokens/opacity.scss
@@ -0,0 +1,18 @@
+:root {
+ --opacity-100: 1;
+ --opacity-60: 0.6;
+}
+
+@media (prefers-color-scheme: "dark") {
+ :root {
+ --logo-center-opacity: var(--opacity-60);
+ }
+}
+
+html[data-theme="light"] {
+ --logo-center-opacity: var(--opacity-100);
+}
+
+html[data-theme="dark"] {
+ --logo-center-opacity: var(--opacity-60);
+}
diff --git a/docs/src/assets/scss/tokens/spacing.scss b/docs/src/assets/scss/tokens/spacing.scss
index 2bc542459b52..47e63745c918 100644
--- a/docs/src/assets/scss/tokens/spacing.scss
+++ b/docs/src/assets/scss/tokens/spacing.scss
@@ -1,69 +1,144 @@
/* @link https://utopia.fyi/space/calculator?c=320,16,1.125,1023,16,1.25,6,2,&s=0.75|0.5|0.25,1.5|2|3|4|6|8,l-2xl|xl-3xl|xl-4xl|l-3xl|s-l */
:root {
- --fluid-min-width: 320;
- --fluid-max-width: 1023;
+ --fluid-min-width: 320;
+ --fluid-max-width: 1023;
- --fluid-screen: 100vw;
- --fluid-bp: calc((var(--fluid-screen) - var(--fluid-min-width) / 16 * 1rem) / (var(--fluid-max-width) - var(--fluid-min-width)));
+ --fluid-screen: 100vw;
+ --fluid-bp: calc(
+ (var(--fluid-screen) - var(--fluid-min-width) / 16 * 1rem) /
+ (var(--fluid-max-width) - var(--fluid-min-width))
+ );
- --fc-3xs-min: (var(--fc-s-min) * 0.25);
- --fc-3xs-max: (var(--fc-s-max) * 0.25);
+ --fc-3xs-min: (var(--fc-s-min) * 0.25);
+ --fc-3xs-max: (var(--fc-s-max) * 0.25);
- --fc-2xs-min: (var(--fc-s-min) * 0.5);
- --fc-2xs-max: (var(--fc-s-max) * 0.5);
+ --fc-2xs-min: (var(--fc-s-min) * 0.5);
+ --fc-2xs-max: (var(--fc-s-max) * 0.5);
- --fc-xs-min: (var(--fc-s-min) * 0.75);
- --fc-xs-max: (var(--fc-s-max) * 0.75);
+ --fc-xs-min: (var(--fc-s-min) * 0.75);
+ --fc-xs-max: (var(--fc-s-max) * 0.75);
- --fc-s-min: (var(--f-0-min, 16));
- --fc-s-max: (var(--f-0-max, 16));
+ --fc-s-min: (var(--f-0-min, 16));
+ --fc-s-max: (var(--f-0-max, 16));
- --fc-m-min: (var(--fc-s-min) * 1.5);
- --fc-m-max: (var(--fc-s-max) * 1.5);
+ --fc-m-min: (var(--fc-s-min) * 1.5);
+ --fc-m-max: (var(--fc-s-max) * 1.5);
- --fc-l-min: (var(--fc-s-min) * 2);
- --fc-l-max: (var(--fc-s-max) * 2);
+ --fc-l-min: (var(--fc-s-min) * 2);
+ --fc-l-max: (var(--fc-s-max) * 2);
- --fc-xl-min: (var(--fc-s-min) * 3);
- --fc-xl-max: (var(--fc-s-max) * 3);
+ --fc-xl-min: (var(--fc-s-min) * 3);
+ --fc-xl-max: (var(--fc-s-max) * 3);
- --fc-2xl-min: (var(--fc-s-min) * 4);
- --fc-2xl-max: (var(--fc-s-max) * 4);
+ --fc-2xl-min: (var(--fc-s-min) * 4);
+ --fc-2xl-max: (var(--fc-s-max) * 4);
- --fc-3xl-min: (var(--fc-s-min) * 6);
- --fc-3xl-max: (var(--fc-s-max) * 6);
+ --fc-3xl-min: (var(--fc-s-min) * 6);
+ --fc-3xl-max: (var(--fc-s-max) * 6);
- --fc-4xl-min: (var(--fc-s-min) * 8);
- --fc-4xl-max: (var(--fc-s-max) * 8);
+ --fc-4xl-min: (var(--fc-s-min) * 8);
+ --fc-4xl-max: (var(--fc-s-max) * 8);
- /* T-shirt sizes */
- --space-3xs: calc(((var(--fc-3xs-min) / 16) * 1rem) + (var(--fc-3xs-max) - var(--fc-3xs-min)) * var(--fluid-bp));
- --space-2xs: calc(((var(--fc-2xs-min) / 16) * 1rem) + (var(--fc-2xs-max) - var(--fc-2xs-min)) * var(--fluid-bp));
- --space-xs: calc(((var(--fc-xs-min) / 16) * 1rem) + (var(--fc-xs-max) - var(--fc-xs-min)) * var(--fluid-bp));
- --space-s: calc(((var(--fc-s-min) / 16) * 1rem) + (var(--fc-s-max) - var(--fc-s-min)) * var(--fluid-bp));
- --space-m: calc(((var(--fc-m-min) / 16) * 1rem) + (var(--fc-m-max) - var(--fc-m-min)) * var(--fluid-bp));
- --space-l: calc(((var(--fc-l-min) / 16) * 1rem) + (var(--fc-l-max) - var(--fc-l-min)) * var(--fluid-bp));
- --space-xl: calc(((var(--fc-xl-min) / 16) * 1rem) + (var(--fc-xl-max) - var(--fc-xl-min)) * var(--fluid-bp));
- --space-2xl: calc(((var(--fc-2xl-min) / 16) * 1rem) + (var(--fc-2xl-max) - var(--fc-2xl-min)) * var(--fluid-bp));
- --space-3xl: calc(((var(--fc-3xl-min) / 16) * 1rem) + (var(--fc-3xl-max) - var(--fc-3xl-min)) * var(--fluid-bp));
- --space-4xl: calc(((var(--fc-4xl-min) / 16) * 1rem) + (var(--fc-4xl-max) - var(--fc-4xl-min)) * var(--fluid-bp));
+ /* T-shirt sizes */
+ --space-3xs: calc(
+ ((var(--fc-3xs-min) / 16) * 1rem) +
+ (var(--fc-3xs-max) - var(--fc-3xs-min)) * var(--fluid-bp)
+ );
+ --space-2xs: calc(
+ ((var(--fc-2xs-min) / 16) * 1rem) +
+ (var(--fc-2xs-max) - var(--fc-2xs-min)) * var(--fluid-bp)
+ );
+ --space-xs: calc(
+ ((var(--fc-xs-min) / 16) * 1rem) + (var(--fc-xs-max) - var(--fc-xs-min)) *
+ var(--fluid-bp)
+ );
+ --space-s: calc(
+ ((var(--fc-s-min) / 16) * 1rem) + (var(--fc-s-max) - var(--fc-s-min)) *
+ var(--fluid-bp)
+ );
+ --space-m: calc(
+ ((var(--fc-m-min) / 16) * 1rem) + (var(--fc-m-max) - var(--fc-m-min)) *
+ var(--fluid-bp)
+ );
+ --space-l: calc(
+ ((var(--fc-l-min) / 16) * 1rem) + (var(--fc-l-max) - var(--fc-l-min)) *
+ var(--fluid-bp)
+ );
+ --space-xl: calc(
+ ((var(--fc-xl-min) / 16) * 1rem) + (var(--fc-xl-max) - var(--fc-xl-min)) *
+ var(--fluid-bp)
+ );
+ --space-2xl: calc(
+ ((var(--fc-2xl-min) / 16) * 1rem) +
+ (var(--fc-2xl-max) - var(--fc-2xl-min)) * var(--fluid-bp)
+ );
+ --space-3xl: calc(
+ ((var(--fc-3xl-min) / 16) * 1rem) +
+ (var(--fc-3xl-max) - var(--fc-3xl-min)) * var(--fluid-bp)
+ );
+ --space-4xl: calc(
+ ((var(--fc-4xl-min) / 16) * 1rem) +
+ (var(--fc-4xl-max) - var(--fc-4xl-min)) * var(--fluid-bp)
+ );
- /* One-up pairs */
- --space-3xs-2xs: calc(((var(--fc-3xs-min) / 16) * 1rem) + (var(--fc-2xs-max) - var(--fc-3xs-min)) * var(--fluid-bp));
- --space-2xs-xs: calc(((var(--fc-2xs-min) / 16) * 1rem) + (var(--fc-xs-max) - var(--fc-2xs-min)) * var(--fluid-bp));
- --space-xs-s: calc(((var(--fc-xs-min) / 16) * 1rem) + (var(--fc-s-max) - var(--fc-xs-min)) * var(--fluid-bp));
- --space-s-m: calc(((var(--fc-s-min) / 16) * 1rem) + (var(--fc-m-max) - var(--fc-s-min)) * var(--fluid-bp));
- --space-m-l: calc(((var(--fc-m-min) / 16) * 1rem) + (var(--fc-l-max) - var(--fc-m-min)) * var(--fluid-bp));
- --space-l-xl: calc(((var(--fc-l-min) / 16) * 1rem) + (var(--fc-xl-max) - var(--fc-l-min)) * var(--fluid-bp));
- --space-xl-2xl: calc(((var(--fc-xl-min) / 16) * 1rem) + (var(--fc-2xl-max) - var(--fc-xl-min)) * var(--fluid-bp));
- --space-2xl-3xl: calc(((var(--fc-2xl-min) / 16) * 1rem) + (var(--fc-3xl-max) - var(--fc-2xl-min)) * var(--fluid-bp));
- --space-3xl-4xl: calc(((var(--fc-3xl-min) / 16) * 1rem) + (var(--fc-4xl-max) - var(--fc-3xl-min)) * var(--fluid-bp));
+ /* One-up pairs */
+ --space-3xs-2xs: calc(
+ ((var(--fc-3xs-min) / 16) * 1rem) +
+ (var(--fc-2xs-max) - var(--fc-3xs-min)) * var(--fluid-bp)
+ );
+ --space-2xs-xs: calc(
+ ((var(--fc-2xs-min) / 16) * 1rem) +
+ (var(--fc-xs-max) - var(--fc-2xs-min)) * var(--fluid-bp)
+ );
+ --space-xs-s: calc(
+ ((var(--fc-xs-min) / 16) * 1rem) + (var(--fc-s-max) - var(--fc-xs-min)) *
+ var(--fluid-bp)
+ );
+ --space-s-m: calc(
+ ((var(--fc-s-min) / 16) * 1rem) + (var(--fc-m-max) - var(--fc-s-min)) *
+ var(--fluid-bp)
+ );
+ --space-m-l: calc(
+ ((var(--fc-m-min) / 16) * 1rem) + (var(--fc-l-max) - var(--fc-m-min)) *
+ var(--fluid-bp)
+ );
+ --space-l-xl: calc(
+ ((var(--fc-l-min) / 16) * 1rem) + (var(--fc-xl-max) - var(--fc-l-min)) *
+ var(--fluid-bp)
+ );
+ --space-xl-2xl: calc(
+ ((var(--fc-xl-min) / 16) * 1rem) +
+ (var(--fc-2xl-max) - var(--fc-xl-min)) * var(--fluid-bp)
+ );
+ --space-2xl-3xl: calc(
+ ((var(--fc-2xl-min) / 16) * 1rem) +
+ (var(--fc-3xl-max) - var(--fc-2xl-min)) * var(--fluid-bp)
+ );
+ --space-3xl-4xl: calc(
+ ((var(--fc-3xl-min) / 16) * 1rem) +
+ (var(--fc-4xl-max) - var(--fc-3xl-min)) * var(--fluid-bp)
+ );
- /* Custom pairs */
- --space-l-2xl: calc(((var(--fc-l-min) / 16) * 1rem) + (var(--fc-2xl-max) - var(--fc-l-min)) * var(--fluid-bp));
- --space-xl-3xl: calc(((var(--fc-xl-min) / 16) * 1rem) + (var(--fc-3xl-max) - var(--fc-xl-min)) * var(--fluid-bp));
- --space-xl-4xl: calc(((var(--fc-xl-min) / 16) * 1rem) + (var(--fc-4xl-max) - var(--fc-xl-min)) * var(--fluid-bp));
- --space-l-3xl: calc(((var(--fc-l-min) / 16) * 1rem) + (var(--fc-3xl-max) - var(--fc-l-min)) * var(--fluid-bp));
- --space-s-l: calc(((var(--fc-s-min) / 16) * 1rem) + (var(--fc-l-max) - var(--fc-s-min)) * var(--fluid-bp));
+ /* Custom pairs */
+ --space-l-2xl: calc(
+ ((var(--fc-l-min) / 16) * 1rem) + (var(--fc-2xl-max) - var(--fc-l-min)) *
+ var(--fluid-bp)
+ );
+ --space-xl-3xl: calc(
+ ((var(--fc-xl-min) / 16) * 1rem) +
+ (var(--fc-3xl-max) - var(--fc-xl-min)) * var(--fluid-bp)
+ );
+ --space-xl-4xl: calc(
+ ((var(--fc-xl-min) / 16) * 1rem) +
+ (var(--fc-4xl-max) - var(--fc-xl-min)) * var(--fluid-bp)
+ );
+ --space-l-3xl: calc(
+ ((var(--fc-l-min) / 16) * 1rem) + (var(--fc-3xl-max) - var(--fc-l-min)) *
+ var(--fluid-bp)
+ );
+ --space-s-l: calc(
+ ((var(--fc-s-min) / 16) * 1rem) + (var(--fc-l-max) - var(--fc-s-min)) *
+ var(--fluid-bp)
+ );
}
diff --git a/docs/src/assets/scss/tokens/themes.scss b/docs/src/assets/scss/tokens/themes.scss
index a8104a5b3469..6fb58f6bb43a 100644
--- a/docs/src/assets/scss/tokens/themes.scss
+++ b/docs/src/assets/scss/tokens/themes.scss
@@ -1,158 +1,240 @@
:root {
- /* Tier 1 variables */
- // colors
- --color-neutral-25: #fcfcfd;
- --color-neutral-50: #f9fafb;
- --color-neutral-100: #f2f4f7;
- --color-neutral-200: #e4e7ec;
- --color-neutral-300: #d0d5dd;
- --color-neutral-400: #98a2b3;
- --color-neutral-500: #667085;
- --color-neutral-600: #475467;
- --color-neutral-700: #344054;
- --color-neutral-800: #1d2939;
- --color-neutral-900: #101828;
-
- --color-primary-25: #fbfbff;
- --color-primary-50: #f6f6fe;
- --color-primary-100: #ececfd;
- --color-primary-200: #dedeff;
- --color-primary-300: #ccccfa;
- --color-primary-400: #b7b7ff;
- --color-primary-500: #a0a0f5;
- --color-primary-600: #8080f2;
- --color-primary-700: #6358d4;
- --color-primary-800: #4b32c3;
- --color-primary-900: #341bab;
-
- --color-warning-25: #fffcf5;
- --color-warning-50: #fffaeb;
- --color-warning-100: #fef0c7;
- --color-warning-200: #fedf89;
- --color-warning-300: #fec84b;
- --color-warning-400: #fdb022;
- --color-warning-500: #f79009;
- --color-warning-600: #dc6803;
- --color-warning-700: #b54708;
- --color-warning-800: #93370d;
- --color-warning-900: #7a2e0e;
-
- --color-success-25: #f6fef9;
- --color-success-50: #ecfdf3;
- --color-success-100: #d1fadf;
- --color-success-200: #a6f4c5;
- --color-success-300: #6ce9a6;
- --color-success-400: #32d583;
- --color-success-500: #12b76a;
- --color-success-600: #039855;
- --color-success-700: #027a48;
- --color-success-800: #05603a;
- --color-success-900: #054f31;
-
- --color-rose-25: #fff5f6;
- --color-rose-50: #fff1f3;
- --color-rose-100: #ffe4e8;
- --color-rose-200: #fecdd6;
- --color-rose-300: #fea3b4;
- --color-rose-400: #fd6f8e;
- --color-rose-500: #f63d68;
- --color-rose-600: #e31b54;
- --color-rose-700: #c01048;
- --color-rose-800: #a11043;
- --color-rose-900: #89123e;
-
- /* Tier 2 variables */
- --primary-button-background-color: var(--color-primary-800);
- --primary-button-hover-color: var(--color-primary-900);
- --primary-button-text-color: #fff;
- --secondary-button-background-color: var(--color-primary-50);
- --secondary-button-hover-color: var(--color-primary-100);
- --secondary-button-text-color: var(--color-brand);
- --ghost-button-background-color: var(--color-primary-50);
- --ghost-button-text-color: var(--color-brand);
-
- --color-brand: var(--color-primary-800);
- --body-background-color: #fff;
- --body-text-color: var(--color-neutral-500);
- --headings-color: var(--color-neutral-900);
-
- --border-color: var(--color-neutral-300);
- --divider-color: var(--color-neutral-200);
-
- --icon-color: var(--color-neutral-400);
- --dark-icon-color: var(--color-neutral-500);
- --link-color: var(--color-primary-800);
-
- --lighter-background-color: var(--color-neutral-100);
- --lightest-background-color: var(--color-neutral-50);
- --docs-lightest-background-color: var(--color-primary-50);
- --hero-background-color: var(--color-neutral-25);
- --footer-background-color: var(--color-neutral-25);
- --outline-color: var(--color-brand);
+ /* Tier 1 variables */
+ // colors
+ --color-neutral-25: #fcfcfd;
+ --color-neutral-50: #f9fafb;
+ --color-neutral-100: #f2f4f7;
+ --color-neutral-200: #e4e7ec;
+ --color-neutral-300: #d0d5dd;
+ --color-neutral-400: #98a2b3;
+ --color-neutral-500: #667085;
+ --color-neutral-600: #475467;
+ --color-neutral-700: #344054;
+ --color-neutral-800: #1d2939;
+ --color-neutral-900: #101828;
+
+ --color-primary-25: #fbfbff;
+ --color-primary-50: #f6f6fe;
+ --color-primary-100: #ececfd;
+ --color-primary-200: #dedeff;
+ --color-primary-300: #ccccfa;
+ --color-primary-400: #b7b7ff;
+ --color-primary-500: #a0a0f5;
+ --color-primary-600: #8080f2;
+ --color-primary-700: #6358d4;
+ --color-primary-800: #4b32c3;
+ --color-primary-900: #341bab;
+
+ --color-warning-25: #fffcf5;
+ --color-warning-50: #fffaeb;
+ --color-warning-100: #fef0c7;
+ --color-warning-200: #fedf89;
+ --color-warning-300: #fec84b;
+ --color-warning-400: #fdb022;
+ --color-warning-500: #f79009;
+ --color-warning-600: #dc6803;
+ --color-warning-700: #b54708;
+ --color-warning-800: #93370d;
+ --color-warning-900: #7a2e0e;
+
+ --color-success-25: #f6fef9;
+ --color-success-50: #ecfdf3;
+ --color-success-100: #d1fadf;
+ --color-success-200: #a6f4c5;
+ --color-success-300: #6ce9a6;
+ --color-success-400: #32d583;
+ --color-success-500: #12b76a;
+ --color-success-600: #039855;
+ --color-success-700: #027a48;
+ --color-success-800: #05603a;
+ --color-success-900: #054f31;
+
+ --color-rose-25: #fff5f6;
+ --color-rose-50: #fff1f3;
+ --color-rose-100: #ffe4e8;
+ --color-rose-200: #fecdd6;
+ --color-rose-300: #fea3b4;
+ --color-rose-400: #fd6f8e;
+ --color-rose-500: #f63d68;
+ --color-rose-600: #e31b54;
+ --color-rose-700: #c01048;
+ --color-rose-800: #a11043;
+ --color-rose-900: #89123e;
+
+ /* Tier 2 variables */
+ --primary-button-background-color: var(--color-primary-800);
+ --primary-button-hover-color: var(--color-primary-900);
+ --primary-button-text-color: #fff;
+ --secondary-button-background-color: var(--color-primary-50);
+ --secondary-button-hover-color: var(--color-primary-100);
+ --secondary-button-text-color: var(--color-brand);
+ --ghost-button-background-color: var(--color-primary-50);
+ --ghost-button-text-color: var(--color-brand);
+
+ --color-brand: var(--color-primary-800);
+ --body-background-color: #fff;
+ --body-text-color: var(--color-neutral-500);
+ --code-comments-color: var(--color-neutral-500);
+ --headings-color: var(--color-neutral-900);
+
+ --border-color: var(--color-neutral-300);
+ --divider-color: var(--color-neutral-200);
+
+ --icon-color: var(--color-neutral-400);
+ --dark-icon-color: var(--color-neutral-500);
+ --link-color: var(--color-primary-800);
+
+ --lighter-background-color: var(--color-neutral-100);
+ --lightest-background-color: var(--color-neutral-50);
+ --docs-lightest-background-color: var(--color-primary-50);
+ --hero-background-color: var(--color-neutral-25);
+ --footer-background-color: var(--color-neutral-25);
+ --outline-color: var(--color-brand);
+ --img-background-color: #fff;
+
+ --code-text-color: var(--color-neutral-900);
+
+ --logo-color: var(--color-primary-800);
+ --logo-center-color: var(--color-primary-600);
+
+ --alert-tip-heading-color: var(--color-success-700);
+ --alert-tip-color: var(--color-success-600);
+ --alert-tip-background-color: var(--color-success-25);
+
+ --alert-important-heading-color: var(--color-warning-700);
+ --alert-important-color: var(--color-warning-600);
+ --alert-important-background-color: var(--color-warning-25);
+
+ --alert-warning-heading-color: var(--color-rose-700);
+ --alert-warning-color: var(--color-rose-600);
+ --alert-warning-background-color: var(--color-rose-25);
+
+ --rule-status-background-color: var(--color-rose-50);
}
@media (prefers-color-scheme: dark) {
- :root {
- --body-background-color: var(--color-neutral-900);
- --body-text-color: var(--color-neutral-300);
- --headings-color: #fff;
-
- --divider-color: var(--color-neutral-600);
- --border-color: var(--color-neutral-500);
-
- --icon-color: var(--body-text-color);
- --dark-icon-color: #fff;
- --link-color: var(--color-primary-400);
-
- --lighter-background-color: var(--color-neutral-800);
- --lightest-background-color: var(--color-neutral-800);
- --docs-lightest-background-color: var(--color-neutral-800);
- --hero-background-color: var(--color-neutral-800);
- --footer-background-color: var(--color-neutral-800);
- --outline-color: #fff;
- }
+ :root {
+ --body-background-color: var(--color-neutral-900);
+ --body-text-color: var(--color-neutral-300);
+ --code-comments-color: var(--color-neutral-400);
+ --headings-color: #fff;
+
+ --divider-color: var(--color-neutral-600);
+ --border-color: var(--color-neutral-500);
+
+ --icon-color: var(--body-text-color);
+ --dark-icon-color: #fff;
+ --link-color: var(--color-primary-400);
+
+ --lighter-background-color: var(--color-neutral-800);
+ --lightest-background-color: var(--color-neutral-800);
+ --docs-lightest-background-color: var(--color-neutral-800);
+ --hero-background-color: var(--color-neutral-800);
+ --footer-background-color: var(--color-neutral-800);
+ --outline-color: #fff;
+ --img-background-color: var(--color-neutral-300);
+
+ --code-text-color: var(--color-neutral-100);
+
+ --logo-color: #fff;
+ --logo-center-color: #fff;
+
+ --alert-tip-heading-color: var(--color-success-200);
+ --alert-tip-color: var(--color-success-300);
+ --alert-tip-background-color: var(--color-success-900);
+
+ --alert-important-heading-color: var(--color-warning-200);
+ --alert-important-color: var(--color-warning-300);
+ --alert-important-background-color: var(--color-warning-900);
+
+ --alert-warning-heading-color: var(--color-rose-200);
+ --alert-warning-color: var(--color-rose-300);
+ --alert-warning-background-color: var(--color-rose-900);
+
+ --rule-status-background-color: var(--color-neutral-900);
+ }
}
html[data-theme="light"] {
- --body-background-color: #fff;
- --body-text-color: var(--color-neutral-500);
- --headings-color: var(--color-neutral-900);
-
- --border-color: var(--color-neutral-300);
- --divider-color: var(--color-neutral-200);
-
- --icon-color: var(--color-neutral-400);
- --dark-icon-color: var(--color-neutral-500);
- --link-color: var(--color-primary-800);
-
- --lighter-background-color: var(--color-neutral-100);
- --lightest-background-color: var(--color-neutral-50);
- --docs-lightest-background-color: var(--color-primary-50);
- --hero-background-color: var(--color-neutral-25);
- --footer-background-color: var(--color-neutral-25);
- --outline-color: var(--color-brand);
- --img-background-color: #fff;
+ --body-background-color: #fff;
+ --body-text-color: var(--color-neutral-500);
+ --code-comments-color: var(--color-neutral-500);
+ --headings-color: var(--color-neutral-900);
+
+ --border-color: var(--color-neutral-300);
+ --divider-color: var(--color-neutral-200);
+
+ --icon-color: var(--color-neutral-400);
+ --dark-icon-color: var(--color-neutral-500);
+ --link-color: var(--color-primary-800);
+
+ --lighter-background-color: var(--color-neutral-100);
+ --lightest-background-color: var(--color-neutral-50);
+ --docs-lightest-background-color: var(--color-primary-50);
+ --hero-background-color: var(--color-neutral-25);
+ --footer-background-color: var(--color-neutral-25);
+ --outline-color: var(--color-brand);
+ --img-background-color: #fff;
+
+ --code-text-color: var(--color-neutral-900);
+
+ --logo-color: var(--color-primary-800);
+ --logo-center-color: var(--color-primary-600);
+
+ --alert-tip-heading-color: var(--color-success-700);
+ --alert-tip-color: var(--color-success-600);
+ --alert-tip-background-color: var(--color-success-25);
+
+ --alert-important-heading-color: var(--color-warning-700);
+ --alert-important-color: var(--color-warning-600);
+ --alert-important-background-color: var(--color-warning-25);
+
+ --alert-warning-heading-color: var(--color-rose-700);
+ --alert-warning-color: var(--color-rose-600);
+ --alert-warning-background-color: var(--color-rose-25);
+
+ --rule-status-background-color: var(--color-rose-50);
}
html[data-theme="dark"] {
- color-scheme: dark;
-
- --body-background-color: var(--color-neutral-900);
- --body-text-color: var(--color-neutral-300);
- --headings-color: #fff;
-
- --divider-color: var(--color-neutral-600);
- --border-color: var(--color-neutral-500);
-
- --icon-color: var(--body-text-color);
- --dark-icon-color: #fff;
- --link-color: var(--color-primary-400);
-
- --lighter-background-color: var(--color-neutral-800);
- --lightest-background-color: var(--color-neutral-800);
- --docs-lightest-background-color: var(--color-neutral-800);
- --hero-background-color: var(--color-neutral-800);
- --footer-background-color: var(--color-neutral-800);
- --outline-color: #fff;
- --img-background-color: var(--color-neutral-300);
+ color-scheme: dark;
+
+ --body-background-color: var(--color-neutral-900);
+ --body-text-color: var(--color-neutral-300);
+ --code-comments-color: var(--color-neutral-400);
+ --headings-color: #fff;
+
+ --divider-color: var(--color-neutral-600);
+ --border-color: var(--color-neutral-500);
+
+ --icon-color: var(--body-text-color);
+ --dark-icon-color: #fff;
+ --link-color: var(--color-primary-400);
+
+ --lighter-background-color: var(--color-neutral-800);
+ --lightest-background-color: var(--color-neutral-800);
+ --docs-lightest-background-color: var(--color-neutral-800);
+ --hero-background-color: var(--color-neutral-800);
+ --footer-background-color: var(--color-neutral-800);
+ --outline-color: #fff;
+ --img-background-color: var(--color-neutral-300);
+
+ --code-text-color: var(--color-neutral-100);
+
+ --logo-color: #fff;
+ --logo-center-color: #fff;
+
+ --alert-tip-heading-color: var(--color-success-200);
+ --alert-tip-color: var(--color-success-300);
+ --alert-tip-background-color: var(--color-success-900);
+
+ --alert-important-heading-color: var(--color-warning-200);
+ --alert-important-color: var(--color-warning-300);
+ --alert-important-background-color: var(--color-warning-900);
+
+ --alert-warning-heading-color: var(--color-rose-200);
+ --alert-warning-color: var(--color-rose-300);
+ --alert-warning-background-color: var(--color-rose-900);
+
+ --rule-status-background-color: var(--color-neutral-900);
}
diff --git a/docs/src/assets/scss/tokens/typography.scss b/docs/src/assets/scss/tokens/typography.scss
index a9e935b2a01f..a03390682767 100644
--- a/docs/src/assets/scss/tokens/typography.scss
+++ b/docs/src/assets/scss/tokens/typography.scss
@@ -1,78 +1,89 @@
/* @link https://utopia.fyi/type/calculator?c=320,16,1.125,1280,16,1.25,6,2,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l */
@media screen and (min-width: 1280px) {
- :root {
- --fluid-screen: calc(var(--fluid-max-width) * 1px);
- }
+ :root {
+ --fluid-screen: calc(var(--fluid-max-width) * 1px);
+ }
}
:root {
- --fluid-min-width: 320;
- --fluid-max-width: 1280;
+ --fluid-min-width: 320;
+ --fluid-max-width: 1280;
- --fluid-screen: 100vw;
- --fluid-bp: calc((var(--fluid-screen) - var(--fluid-min-width) / 16 * 1rem) / (var(--fluid-max-width) - var(--fluid-min-width)));
+ --fluid-screen: 100vw;
+ --fluid-bp: calc(
+ (var(--fluid-screen) - var(--fluid-min-width) / 16 * 1rem) /
+ (var(--fluid-max-width) - var(--fluid-min-width))
+ );
- --f--2-min: 12.64;
- --f--2-max: 10.24;
- --step--2: calc(((var(--f--2-min) / 16) * 1rem) + (var(--f--2-max) - var(--f--2-min)) * var(--fluid-bp));
+ --f--2-min: 12.64;
+ --f--2-max: 10.24;
+ --step--2: calc(
+ ((var(--f--2-min) / 16) * 1rem) + (var(--f--2-max) - var(--f--2-min)) *
+ var(--fluid-bp)
+ );
- --f--1-min: 14.22;
- --f--1-max: 12.80;
- --step--1: calc(((var(--f--1-min) / 16) * 1rem) + (var(--f--1-max) - var(--f--1-min)) * var(--fluid-bp));
+ --f--1-min: 14.22;
+ --f--1-max: 12.8;
+ --step--1: calc(
+ ((var(--f--1-min) / 16) * 1rem) + (var(--f--1-max) - var(--f--1-min)) *
+ var(--fluid-bp)
+ );
- --f-0-min: 16.00;
- --f-0-max: 16.00;
- --step-0: calc(((var(--f-0-min) / 16) * 1rem) + (var(--f-0-max) - var(--f-0-min)) * var(--fluid-bp));
+ --f-0-min: 16;
+ --f-0-max: 16;
+ --step-0: calc(
+ ((var(--f-0-min) / 16) * 1rem) + (var(--f-0-max) - var(--f-0-min)) *
+ var(--fluid-bp)
+ );
- --f-1-min: 18.00;
- --f-1-max: 20.00;
- --step-1: calc(((var(--f-1-min) / 16) * 1rem) + (var(--f-1-max) - var(--f-1-min)) * var(--fluid-bp));
+ --f-1-min: 18;
+ --f-1-max: 20;
+ --step-1: calc(
+ ((var(--f-1-min) / 16) * 1rem) + (var(--f-1-max) - var(--f-1-min)) *
+ var(--fluid-bp)
+ );
- --f-2-min: 20.25;
- --f-2-max: 25.00;
- --step-2: calc(((var(--f-2-min) / 16) * 1rem) + (var(--f-2-max) - var(--f-2-min)) * var(--fluid-bp));
+ --f-2-min: 20.25;
+ --f-2-max: 25;
+ --step-2: calc(
+ ((var(--f-2-min) / 16) * 1rem) + (var(--f-2-max) - var(--f-2-min)) *
+ var(--fluid-bp)
+ );
- --f-3-min: 22.78;
- --f-3-max: 31.25;
- --step-3: calc(((var(--f-3-min) / 16) * 1rem) + (var(--f-3-max) - var(--f-3-min)) * var(--fluid-bp));
+ --f-3-min: 22.78;
+ --f-3-max: 31.25;
+ --step-3: calc(
+ ((var(--f-3-min) / 16) * 1rem) + (var(--f-3-max) - var(--f-3-min)) *
+ var(--fluid-bp)
+ );
- --f-4-min: 25.63;
- --f-4-max: 39.06;
- --step-4: calc(((var(--f-4-min) / 16) * 1rem) + (var(--f-4-max) - var(--f-4-min)) * var(--fluid-bp));
+ --f-4-min: 25.63;
+ --f-4-max: 39.06;
+ --step-4: calc(
+ ((var(--f-4-min) / 16) * 1rem) + (var(--f-4-max) - var(--f-4-min)) *
+ var(--fluid-bp)
+ );
- --f-5-min: 28.83;
- --f-5-max: 48.83;
- --step-5: calc(((var(--f-5-min) / 16) * 1rem) + (var(--f-5-max) - var(--f-5-min)) * var(--fluid-bp));
+ --f-5-min: 28.83;
+ --f-5-max: 48.83;
+ --step-5: calc(
+ ((var(--f-5-min) / 16) * 1rem) + (var(--f-5-max) - var(--f-5-min)) *
+ var(--fluid-bp)
+ );
- --f-6-min: 32.44;
- --f-6-max: 61.04;
- --step-6: calc(((var(--f-6-min) / 16) * 1rem) + (var(--f-6-max) - var(--f-6-min)) * var(--fluid-bp));
+ --f-6-min: 32.44;
+ --f-6-max: 61.04;
+ --step-6: calc(
+ ((var(--f-6-min) / 16) * 1rem) + (var(--f-6-max) - var(--f-6-min)) *
+ var(--fluid-bp)
+ );
- --mono-font: "Mono Punctuators", "Space Mono", monospace;
- --text-font:
- "Inter",
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Roboto,
- Helvetica,
- Arial,
- sans-serif,
- "Apple Color Emoji",
- "Twemoji Country Flags",
- "Segoe UI Emoji",
- "Segoe UI Symbol";
- --display-font:
- "Space Grotesk",
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Roboto,
- Helvetica,
- Arial,
- sans-serif,
- "Apple Color Emoji",
- "Segoe UI Emoji",
- "Segoe UI Symbol";
+ --mono-font: "Mono Punctuators", "Space Mono", monospace;
+ --text-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+ Helvetica, Arial, sans-serif, "Apple Color Emoji",
+ "Twemoji Country Flags", "Segoe UI Emoji", "Segoe UI Symbol";
+ --display-font: "Space Grotesk", -apple-system, BlinkMacSystemFont,
+ "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji",
+ "Segoe UI Emoji", "Segoe UI Symbol";
}
diff --git a/docs/src/assets/scss/tokens/ui.scss b/docs/src/assets/scss/tokens/ui.scss
index 49380e12da85..7865467affa6 100644
--- a/docs/src/assets/scss/tokens/ui.scss
+++ b/docs/src/assets/scss/tokens/ui.scss
@@ -1,9 +1,8 @@
:root {
- // elevations
- --shadow-lg:
- 0 12px 16px -4px rgba(16, 24, 40, 0.1),
- 0 4px 6px -2px rgba(16, 24, 40, 0.05);
- --shadow-xs: 0 1px 2px rgba(16, 24, 40, 0.05);
+ // elevations
+ --shadow-lg: 0 12px 16px -4px rgba(16, 24, 40, 0.1),
+ 0 4px 6px -2px rgba(16, 24, 40, 0.05);
+ --shadow-xs: 0 1px 2px rgba(16, 24, 40, 0.05);
- --border-radius: .5rem;
+ --border-radius: 0.5rem;
}
diff --git a/docs/src/assets/scss/utilities.scss b/docs/src/assets/scss/utilities.scss
index c296358837ee..5705bed3a068 100644
--- a/docs/src/assets/scss/utilities.scss
+++ b/docs/src/assets/scss/utilities.scss
@@ -1,171 +1,171 @@
.grid {
- @media all and (min-width: 1024px) {
- display: grid;
- grid-template-columns: repeat(12, 1fr);
- grid-gap: 2rem;
- align-items: start;
- }
+ @media all and (min-width: 1024px) {
+ display: grid;
+ grid-template-columns: repeat(12, 1fr);
+ grid-gap: 2rem;
+ align-items: start;
+ }
}
.visually-hidden {
- clip: rect(0 0 0 0);
- clip-path: inset(100%);
- height: 1px;
- overflow: hidden;
- position: absolute;
- width: 1px;
- white-space: nowrap;
+ clip: rect(0 0 0 0);
+ clip-path: inset(100%);
+ height: 1px;
+ overflow: hidden;
+ position: absolute;
+ width: 1px;
+ white-space: nowrap;
}
[hidden] {
- display: none !important;
+ display: none !important;
}
.mobile-only {
- @media all and (min-width: 1024px) {
- display: none;
- }
+ @media all and (min-width: 1024px) {
+ display: none;
+ }
}
.desktop-only {
- @media all and (max-width: 1023px) {
- display: none;
- }
+ @media all and (max-width: 1023px) {
+ display: none;
+ }
}
.text.text {
- color: inherit;
- font: inherit;
- font-family: var(--text-font);
- margin: 0;
+ color: inherit;
+ font: inherit;
+ font-family: var(--text-font);
+ margin: 0;
}
.color-brand {
- color: var(--link-color);
+ color: var(--link-color);
}
.font-weight-medium {
- font-weight: 500;
+ font-weight: 500;
}
.center-text {
- text-align: center;
- grid-column: 1 / -1;
+ text-align: center;
+ grid-column: 1 / -1;
}
.text-dark {
- color: var(--headings-color);
+ color: var(--headings-color);
}
.divider {
- border-bottom: 1px solid var(--divider-color);
- border-block-end: 1px solid var(--divider-color);
+ border-bottom: 1px solid var(--divider-color);
+ border-block-end: 1px solid var(--divider-color);
}
.fs-step--1 {
- font-size: .875rem;
+ font-size: 0.875rem;
}
.fs-step-0 {
- font-size: var(--step-0);
+ font-size: var(--step-0);
}
.fs-step-1 {
- font-size: var(--step-1);
+ font-size: var(--step-1);
}
.fs-step-2 {
- font-size: var(--step-2);
+ font-size: var(--step-2);
}
.fs-step-3 {
- font-size: var(--step-3);
+ font-size: var(--step-3);
}
.fs-step-4 {
- font-size: var(--step-4);
+ font-size: var(--step-4);
}
.fs-step-5 {
- font-size: var(--step-5);
+ font-size: var(--step-5);
}
.fs-step-6 {
- font-size: var(--step-6);
+ font-size: var(--step-6);
}
.grid--center-items {
- align-items: center;
+ align-items: center;
}
.span-1-3 {
- grid-column: 1 / 4;
+ grid-column: 1 / 4;
}
.span-1-4 {
- grid-column: 1 / 5;
+ grid-column: 1 / 5;
}
.span-1-5 {
- grid-column: 1 / 6;
+ grid-column: 1 / 6;
}
.span-1-6 {
- grid-column: 1 / 7;
+ grid-column: 1 / 7;
}
.span-1-7 {
- grid-column: 1 / 8;
+ grid-column: 1 / 8;
}
.span-1-12 {
- grid-column: 1 / -1;
+ grid-column: 1 / -1;
}
.span-4-12 {
- grid-column: 4 / 13;
+ grid-column: 4 / 13;
}
.span-6-12 {
- grid-column: 6 / 13;
+ grid-column: 6 / 13;
}
.span-7-12 {
- grid-column: 7 / 13;
+ grid-column: 7 / 13;
}
.span-8-12 {
- grid-column: 8 / 13;
+ grid-column: 8 / 13;
}
.span-10-12 {
- grid-column: 10 / 13;
+ grid-column: 10 / 13;
}
.span-11-12 {
- grid-column: 11 / 13;
+ grid-column: 11 / 13;
}
.span-4-9 {
- grid-column: 4 / 10;
+ grid-column: 4 / 10;
}
.span-4-11 {
- grid-column: 4 / 11;
+ grid-column: 4 / 11;
}
.span-5-12 {
- grid-column: 5 / 12;
+ grid-column: 5 / 12;
}
.span-3-10 {
- grid-column: 3 / 11;
+ grid-column: 3 / 11;
}
.span-6-7 {
- grid-column: 6 / 8;
+ grid-column: 6 / 8;
}
.span-5-8 {
- grid-column: 5 / 9;
+ grid-column: 5 / 9;
}
diff --git a/docs/src/assets/scss/versions.scss b/docs/src/assets/scss/versions.scss
index f0979c64f4ea..bcaac9511469 100644
--- a/docs/src/assets/scss/versions.scss
+++ b/docs/src/assets/scss/versions.scss
@@ -1,50 +1,50 @@
.versions-list {
- margin: 0;
- padding: 0;
- font-size: var(--step-1);
+ margin: 0;
+ padding: 0;
+ font-size: var(--step-1);
- li {
- margin: 0;
+ li {
+ margin: 0;
- &:last-of-type a {
- border-bottom: 0;
- border-block-end: 0;
- }
- }
+ &:last-of-type a {
+ border-bottom: 0;
+ border-block-end: 0;
+ }
+ }
- a {
- color: var(--link-color);
- width: 100%;
- padding: 1rem .5rem;
- text-decoration: none;
- display: block;
- display: flex;
- align-items: center;
- border-bottom: 1px solid var(--divider-color);
- border-block-end: 1px solid var(--divider-color);
+ a {
+ color: var(--link-color);
+ width: 100%;
+ padding: 1rem 0.5rem;
+ text-decoration: none;
+ display: block;
+ display: flex;
+ align-items: center;
+ border-bottom: 1px solid var(--divider-color);
+ border-block-end: 1px solid var(--divider-color);
- &[data-current="true"] {
- font-weight: 500;
- color: var(--link-color);
+ &[data-current="true"] {
+ font-weight: 500;
+ color: var(--link-color);
- &::after {
- content: " âī¸";
- white-space: pre;
- color: rgba(100%, 0%, 0%, 0);
- text-shadow: 0 0 0 var(--headings-color);
- }
- }
+ &::after {
+ content: " âī¸";
+ white-space: pre;
+ color: rgba(100%, 0%, 0%, 0);
+ text-shadow: 0 0 0 var(--headings-color);
+ }
+ }
- &:hover {
- background-color: var(--lightest-background-color);
- }
- }
+ &:hover {
+ background-color: var(--lightest-background-color);
+ }
+ }
}
.versions-section .versions-list {
- font-size: var(--step-1);
- border-left: 4px solid var(--tab-border-color);
- padding-left: 1rem;
- border-inline-start: 4px solid var(--tab-border-color);
- padding-inline-start: 1rem;
+ font-size: var(--step-1);
+ border-left: 4px solid var(--tab-border-color);
+ padding-left: 1rem;
+ border-inline-start: 4px solid var(--tab-border-color);
+ padding-inline-start: 1rem;
}
diff --git a/docs/src/contribute/architecture/index.md b/docs/src/contribute/architecture/index.md
index 3370e6df4480..992fce89d425 100644
--- a/docs/src/contribute/architecture/index.md
+++ b/docs/src/contribute/architecture/index.md
@@ -13,14 +13,14 @@ eleventyNavigation:
At a high level, there are a few key parts to ESLint:
-* `bin/eslint.js` - this is the file that actually gets executed with the command line utility. It's a dumb wrapper that does nothing more than bootstrap ESLint, passing the command line arguments to `cli`. This is intentionally small so as not to require heavy testing.
-* `lib/api.js` - this is the entry point of `require("eslint")`. This file exposes an object that contains public classes `Linter`, `ESLint`, `RuleTester`, and `SourceCode`.
-* `lib/cli.js` - this is the heart of the ESLint CLI. It takes an array of arguments and then uses `eslint` to execute the commands. By keeping this as a separate utility, it allows others to effectively call ESLint from within another Node.js program as if it were done on the command line. The main call is `cli.execute()`. This is also the part that does all the file reading, directory traversing, input, and output.
-* `lib/cli-engine/` - this module is `CLIEngine` class that finds source code files and configuration files then does code verifying with the `Linter` class. This includes the loading logic of configuration files, parsers, plugins, and formatters.
-* `lib/linter/` - this module is the core `Linter` class that does code verifying based on configuration options. This file does no file I/O and does not interact with the `console` at all. For other Node.js programs that have JavaScript text to verify, they would be able to use this interface directly.
-* `lib/rule-tester/` - this module is `RuleTester` class that is a wrapper around Mocha so that rules can be unit tested. This class lets us write consistently formatted tests for each rule that is implemented and be confident that each of the rules work. The RuleTester interface was modeled after Mocha and works with Mocha's global testing methods. RuleTester can also be modified to work with other testing frameworks.
-* `lib/source-code/` - this module is `SourceCode` class that is used to represent the parsed source code. It takes in source code and the Program node of the AST representing the code.
-* `lib/rules/` - this contains built-in rules that verify source code.
+- `bin/eslint.js` - this is the file that actually gets executed with the command line utility. It's a dumb wrapper that does nothing more than bootstrap ESLint, passing the command line arguments to `cli`. This is intentionally small so as not to require heavy testing.
+- `lib/api.js` - this is the entry point of `require("eslint")`. This file exposes an object that contains public classes `Linter`, `ESLint`, `RuleTester`, and `SourceCode`.
+- `lib/cli.js` - this is the heart of the ESLint CLI. It takes an array of arguments and then uses `eslint` to execute the commands. By keeping this as a separate utility, it allows others to effectively call ESLint from within another Node.js program as if it were done on the command line. The main call is `cli.execute()`. This is also the part that does all the file reading, directory traversing, input, and output.
+- `lib/cli-engine/` - this module is `CLIEngine` class that finds source code files and configuration files then does code verifying with the `Linter` class. This includes the loading logic of configuration files, parsers, plugins, and formatters.
+- `lib/linter/` - this module is the core `Linter` class that does code verifying based on configuration options. This file does no file I/O and does not interact with the `console` at all. For other Node.js programs that have JavaScript text to verify, they would be able to use this interface directly.
+- `lib/rule-tester/` - this module is `RuleTester` class that is a wrapper around Mocha so that rules can be unit tested. This class lets us write consistently formatted tests for each rule that is implemented and be confident that each of the rules work. The RuleTester interface was modeled after Mocha and works with Mocha's global testing methods. RuleTester can also be modified to work with other testing frameworks.
+- `lib/source-code/` - this module is `SourceCode` class that is used to represent the parsed source code. It takes in source code and the Program node of the AST representing the code.
+- `lib/rules/` - this contains built-in rules that verify source code.
## The `cli` object
@@ -30,17 +30,17 @@ The main method is `cli.execute()`, which accepts an array of strings that repre
This object's responsibilities include:
-* Interpreting command line arguments
-* Reading from the file system
-* Outputting to the console
-* Outputting to the filesystem
-* Use a formatter
-* Returning the correct exit code
+- Interpreting command line arguments.
+- Reading from the file system.
+- Outputting to the console.
+- Outputting to the filesystem.
+- Use a formatter.
+- Returning the correct exit code.
This object may not:
-* Call `process.exit()` directly
-* Perform any asynchronous operations
+- Call `process.exit()` directly.
+- Perform any asynchronous operations.
## The `CLIEngine` object
@@ -50,16 +50,16 @@ The main method of the `CLIEngine` is `executeOnFiles()`, which accepts an array
This object's responsibilities include:
-* Managing the execution environment for `Linter`
-* Reading from the file system
-* Reading configuration information from config files (including `.eslintrc` and `package.json`)
+- Managing the execution environment for `Linter`.
+- Reading from the file system.
+- Reading configuration information from config files (including `.eslintrc` and `package.json`).
This object may not:
-* Call `process.exit()` directly
-* Perform any asynchronous operations
-* Output to the console
-* Use formatters
+- Call `process.exit()` directly.
+- Perform any asynchronous operations.
+- Output to the console.
+- Use formatters.
## The `Linter` object
@@ -69,18 +69,18 @@ Once the AST is available, `estraverse` is used to traverse the AST from top to
This object's responsibilities include:
-* Inspecting JavaScript code strings
-* Creating an AST for the code
-* Executing rules on the AST
-* Reporting back the results of the execution
+- Inspecting JavaScript code strings.
+- Creating an AST for the code.
+- Executing rules on the AST.
+- Reporting back the results of the execution.
This object may not:
-* Call `process.exit()` directly
-* Perform any asynchronous operations
-* Use Node.js-specific features
-* Access the file system
-* Call `console.log()` or any other similar method
+- Call `process.exit()` directly.
+- Perform any asynchronous operations.
+- Use Node.js-specific features.
+- Access the file system.
+- Call `console.log()` or any other similar method.
## Rules
@@ -88,13 +88,13 @@ Individual rules are the most specialized part of the ESLint architecture. Rules
These objects' responsibilities are:
-* Inspect the AST for specific patterns
-* Reporting warnings when certain patterns are found
+- Inspect the AST for specific patterns.
+- Reporting warnings when certain patterns are found.
These objects may not:
-* Call `process.exit()` directly
-* Perform any asynchronous operations
-* Use Node.js-specific features
-* Access the file system
-* Call `console.log()` or any other similar method
+- Call `process.exit()` directly.
+- Perform any asynchronous operations.
+- Use Node.js-specific features.
+- Access the file system.
+- Call `console.log()` or any other similar method.
diff --git a/docs/src/contribute/core-rules.md b/docs/src/contribute/core-rules.md
index 93b7647f52be..e5dd8991c5a6 100644
--- a/docs/src/contribute/core-rules.md
+++ b/docs/src/contribute/core-rules.md
@@ -20,9 +20,9 @@ For full reference information on writing rules, refer to [Custom Rules](../exte
Each core rule in ESLint has three files named with its identifier (for example, `no-extra-semi`).
-* in the `lib/rules` directory: a source file (for example, `no-extra-semi.js`)
-* in the `tests/lib/rules` directory: a test file (for example, `no-extra-semi.js`)
-* in the `docs/src/rules` directory: a Markdown documentation file (for example, `no-extra-semi.md`)
+- in the `lib/rules` directory: a source file (for example, `no-extra-semi.js`).
+- in the `tests/lib/rules` directory: a test file (for example, `no-extra-semi.js`).
+- in the `docs/src/rules` directory: a Markdown documentation file (for example, `no-extra-semi.md`).
**Important:** If you submit a core rule to the ESLint repository, you **must** follow the conventions explained below.
@@ -40,24 +40,24 @@ Here is the basic format of the source file for a rule:
// Rule Definition
//------------------------------------------------------------------------------
-/** @type {import('../shared/types').Rule} */
+/** @type {import('../types').Rule.RuleModule} */
module.exports = {
- meta: {
- type: "suggestion",
-
- docs: {
- description: "disallow unnecessary semicolons",
- recommended: true,
- url: "https://eslint.org/docs/rules/no-extra-semi"
- },
- fixable: "code",
- schema: [] // no options
- },
- create: function(context) {
- return {
- // callback functions
- };
- }
+ meta: {
+ type: "suggestion",
+
+ docs: {
+ description: "disallow unnecessary semicolons",
+ recommended: true,
+ url: "https://eslint.org/docs/rules/no-extra-semi",
+ },
+ fixable: "code",
+ schema: [], // no options
+ },
+ create: function (context) {
+ return {
+ // callback functions
+ };
+ },
};
```
@@ -105,6 +105,19 @@ Performance budget ok: 1443.736547ms (limit: 3409.090909090909ms)
The rule naming conventions for ESLint are as follows:
-* Use dashes between words.
-* If your rule only disallows something, prefix it with `no-` such as `no-eval` for disallowing `eval()` and `no-debugger` for disallowing `debugger`.
-* If your rule is enforcing the inclusion of something, use a short name without a special prefix.
+- Use dashes between words.
+- If your rule only disallows something, prefix it with `no-` such as `no-eval` for disallowing `eval()` and `no-debugger` for disallowing `debugger`.
+- If your rule is enforcing the inclusion of something, use a short name without a special prefix.
+
+## Frozen Rules
+
+When rules are feature complete, they are marked as frozen (indicated with âī¸ in the documentation). Rules are considered feature complete when the intended purpose of the rule has been fully implemented such that it catches 80% or more of expected violations and covers the majority of common exceptions. After that point, we expect users to use [disable comments](../use/configure/rules#using-configuration-comments-1) when they find an edge case that isn't covered.
+
+When a rule is frozen, it means:
+
+- **Bug fixes**: We will still fix confirmed bugs.
+- **New ECMAScript features**: We will ensure compatibility with new ECMAScript features, meaning the rule will not break on new syntax.
+- **TypeScript support**: We will ensure compatibility with TypeScript syntax, meaning the rule will not break on TypeScript syntax and violations are appropriate for TypeScript.
+- **New options**: We will **not** add any new options unless an option is the only way to fix a bug or support a newly-added ECMAScript feature.
+
+If you find that a frozen rule would work better for you with a change, we recommend copying the rule source code and modifying it to fit your needs.
diff --git a/docs/src/contribute/development-environment.md b/docs/src/contribute/development-environment.md
index b84af4e32743..b156daf2f0c0 100644
--- a/docs/src/contribute/development-environment.md
+++ b/docs/src/contribute/development-environment.md
@@ -7,6 +7,8 @@ eleventyNavigation:
order: 6
---
+{%- from 'components/npm_tabs.macro.html' import npm_tabs with context %}
+
ESLint has a very lightweight development environment that makes updating code fast and easy. This is a step-by-step guide to setting up a local development environment that will let you contribute back to the project.
## Step 1: Install Node.js
@@ -22,23 +24,28 @@ Go to and click the "Fork" button. Follow the
Clone your fork:
```shell
-git clone https://github.com//eslint
+git clone https://github.com//eslint
```
Once you've cloned the repository, run `npm install` to get all the necessary dependencies:
```shell
cd eslint
-npm install
```
+{{ npm_tabs({
+ command: "install",
+ packages: [],
+ args: []
+}) }}
+
You must be connected to the Internet for this step to work. You'll see a lot of utilities being downloaded.
**Note:** It's a good idea to re-run `npm install` whenever you pull from the main repository to ensure you have the latest development dependencies.
## Step 3: Add the Upstream Source
-The *upstream source* is the main ESLint repository where active development happens. While you won't have push access to upstream, you will have pull access, allowing you to pull in the latest code whenever you want.
+The _upstream source_ is the main ESLint repository where active development happens. While you won't have push access to upstream, you will have pull access, allowing you to pull in the latest code whenever you want.
To add the upstream source for ESLint, run the following in your repository:
@@ -52,15 +59,19 @@ Now, the remote `upstream` points to the upstream source.
[Yeoman](https://yeoman.io) is a scaffold generator that ESLint uses to help streamline development of new rules. If you don't already have Yeoman installed, you can install it via npm:
-```shell
-npm install -g yo
-```
+{{ npm_tabs({
+ command: "install",
+ packages: ["yo"],
+ args: ["--global"]
+}) }}
Then, you can install the ESLint Yeoman generator:
-```shell
-npm install -g generator-eslint
-```
+{{ npm_tabs({
+ command: "install",
+ packages: ["generator-eslint"],
+ args: ["--global"]
+}) }}
Please see the [generator documentation](https://github.com/eslint/generator-eslint) for instructions on how to use it.
@@ -80,16 +91,16 @@ The testing takes a few minutes to complete. If any tests fail, that likely mean
The ESLint directory and file structure is as follows:
-* `bin` - executable files that are available when ESLint is installed
-* `conf` - default configuration information
-* `docs` - documentation for the project
-* `lib` - contains the source code
- * `formatters` - all source files defining formatters
- * `rules` - all source files defining rules
-* `tests` - the main unit test folder
- * `lib` - tests for the source code
- * `formatters` - tests for the formatters
- * `rules` - tests for the rules
+- `bin` - executable files that are available when ESLint is installed.
+- `conf` - default configuration information.
+- `docs` - documentation for the project.
+- `lib` - contains the source code.
+ - `formatters` - all source files defining formatters.
+ - `rules` - all source files defining rules.
+- `tests` - the main unit test folder.
+ - `lib` - tests for the source code.
+ - `formatters` - tests for the formatters.
+ - `rules` - tests for the rules.
### Workflow
@@ -103,11 +114,11 @@ ESLint has several build scripts that help with various parts of development.
The primary script to use is `npm test`, which does several things:
-1. Lints all JavaScript (including tests) and JSON
-1. Runs all tests on Node.js
-1. Checks code coverage targets
-1. Generates `build/eslint.js` for use in a browser
-1. Runs a subset of tests in PhantomJS
+1. Lints all JavaScript (including tests) and JSON.
+1. Runs all tests on Node.js.
+1. Checks code coverage targets.
+1. Generates `build/eslint.js` for use in a browser.
+1. Runs a subset of tests in PhantomJS.
Be sure to run this after making changes and before sending a pull request with your changes.
diff --git a/docs/src/contribute/governance.md b/docs/src/contribute/governance.md
index 3f761fb2a513..9f206ed1f6e2 100644
--- a/docs/src/contribute/governance.md
+++ b/docs/src/contribute/governance.md
@@ -5,7 +5,6 @@ eleventyNavigation:
parent: contribute to eslint
title: Governance
order: 12
-
---
ESLint is an open source project that depends on contributions from the community. Anyone may contribute to the project at any time by submitting code, participating in discussions, making suggestions, or any other contribution they see fit. This document describes how various types of contributors work within the ESLint project.
@@ -30,20 +29,25 @@ As Contributors gain experience and familiarity with the project, their profile
Website Team Members are community members who have shown that they are committed to the continued maintenance of [eslint.org](https://eslint.org/) through ongoing engagement with the community. Website Team Members are given push access to the `eslint.org` GitHub repository and must abide by the project's [Contribution Guidelines](../contribute/).
- Website Team Members:
+Website Team Members:
-* Are expected to work on public branches of the source repository and submit pull requests from that branch to the master branch.
-* Are expected to delete their public branches when they are no longer necessary.
-* Must submit pull requests for all changes.
-* Have their work reviewed by Reviewers and TSC members before acceptance into the repository.
-* May label and close website-related issues (see [Manage Issues](../maintain/manage-issues))
-* May merge some pull requests (see [Review Pull Requests](../maintain/review-pull-requests))
+- Are expected to work at least one hour per week triaging issues and reviewing pull requests.
+- Are expected to work at least two hours total per week on ESLint.
+- May invoice for the hours they spend working on ESLint at a rate of $50 USD per hour.
+- Are expected to check in on the `#team` Discord channel once per week day (excluding holidays and other time off) for team updates.
+- Are expected to work on public branches of the source repository and submit pull requests from that branch to the master branch.
+- Are expected to delete their public branches when they are no longer necessary.
+- Must submit pull requests for all changes.
+- Have their work reviewed by Reviewers and TSC members before acceptance into the repository.
+- May label and close website-related issues (see [Manage Issues](../maintain/manage-issues)).
+- May merge some pull requests (see [Review Pull Requests](../maintain/review-pull-requests)).
+- May take time off whenever they want, and are expected to post in the `#team` Discord channel when they will be away for more than a couple of days.
-To become a Website Team Member:
+To become a Website Team Member:
-* One must have shown a willingness and ability to participate in the maintenance of [eslint.org](https://eslint.org/) as a team player. Typically, a potential Website Team Member will need to show that they have an understanding of the structure of the website and how it fits into the larger ESLint project's objectives and strategy.
-* Website Team Members are expected to be respectful of every community member and to work collaboratively in the spirit of inclusion.
-* Have submitted a minimum of 10 website-related pull requests. What's a website-related pull request? One that is made to the `eslint.org` repository or the `docs` directory in the `eslint` repository and requires little effort to accept because it's well documented and tested.
+- One must have shown a willingness and ability to participate in the maintenance of [eslint.org](https://eslint.org/) as a team player. Typically, a potential Website Team Member will need to show that they have an understanding of the structure of the website and how it fits into the larger ESLint project's objectives and strategy.
+- Website Team Members are expected to be respectful of every community member and to work collaboratively in the spirit of inclusion.
+- Have submitted a minimum of 10 website-related pull requests. What's a website-related pull request? One that is made to the `eslint.org` repository or the `docs` directory in the `eslint` repository and requires little effort to accept because it's well documented and tested.
New Website Team Members can be nominated by any existing Website Team Member or Committer. Once they have been nominated, there will be a vote by the TSC members.
@@ -55,18 +59,25 @@ Committers are community members who have shown that they are committed to the c
Committers:
-* Are expected to work on public branches of the source repository and submit pull requests from that branch to the master branch.
-* Are expected to delete their public branches when they are no longer necessary.
-* Must submit pull requests for all changes.
-* Have their work reviewed by TSC members before acceptance into the repository.
-* May label and close issues (see [Manage Issues](../maintain/manage-issues))
-* May merge some pull requests (see [Review Pull Requests](../maintain/review-pull-requests))
+- Are expected to work at least one hour per week triaging issues and reviewing pull requests.
+- Are expected to work at least two hours total per week on ESLint.
+- May invoice for the hours they spend working on ESLint at a rate of $50 USD per hour.
+- Are expected to check in on the `#team` Discord channel once per week day (excluding holidays and other time off) for team updates.
+- Are expected to work on public branches of the source repository and submit pull requests from that branch to the master branch.
+- Are expected to delete their public branches when they are no longer necessary.
+- Are expected to provide feedback on issues in the "Feedback Needed" column of the [Triage Board](https://github.com/orgs/eslint/projects/3/views/1).
+- Are expected to work on at least one issue in the "Ready to Implement" column of the [Triage Board](https://github.com/orgs/eslint/projects/3/views/1) that they didn't create each month.
+- Must submit pull requests for all changes.
+- Have their work reviewed by TSC members before acceptance into the repository.
+- May label and close issues (see [Manage Issues](../maintain/manage-issues)).
+- May merge some pull requests (see [Review Pull Requests](../maintain/review-pull-requests)).
+- May take time off whenever they want, and are expected to post in the `#team` Discord channel when they will be away for more than a couple of days.
To become a Committer:
-* One must have shown a willingness and ability to participate in the project as a team player. Typically, a potential Committer will need to show that they have an understanding of and alignment with the project, its objectives, and its strategy.
-* Committers are expected to be respectful of every community member and to work collaboratively in the spirit of inclusion.
-* Have submitted a minimum of 10 qualifying pull requests. What's a qualifying pull request? One that carries significant technical weight and requires little effort to accept because it's well documented and tested.
+- One must have shown a willingness and ability to participate in the project as a team player. Typically, a potential Committer will need to show that they have an understanding of and alignment with the project, its objectives, and its strategy.
+- Committers are expected to be respectful of every community member and to work collaboratively in the spirit of inclusion.
+- Have submitted a minimum of 10 qualifying pull requests. What's a qualifying pull request? One that carries significant technical weight and requires little effort to accept because it's well documented and tested.
New Committers can be nominated by any existing Committer. Once they have been nominated, there will be a vote by the TSC members.
@@ -74,47 +85,35 @@ It is important to recognize that committership is a privilege, not a right. Tha
A Committer who shows an above-average level of contribution to the project, particularly with respect to its strategic direction and long-term health, may be nominated to become a reviewer, described below.
-#### Process for Adding Committers
-
-1. Send email congratulating the new committer and confirming that they would like to accept. This should also outline the responsibilities of a committer with a link to the maintainer guide.
-1. Add the GitHub user to the "ESLint Team" team
-1. Add committer email to the ESLint team mailing list
-1. Invite to Discord team channel
-1. Tweet congratulations to the new committer from the ESLint Twitter account
-
### Reviewers
Reviewers are community members who have contributed a significant amount of time to the project through triaging of issues, fixing bugs, implementing enhancements/features, and are trusted community leaders.
Reviewers may perform all of the duties of Committers, and also:
-* May merge external pull requests for accepted issues upon reviewing and approving the changes.
-* May merge their own pull requests once they have collected the feedback they deem necessary. (No pull request should be merged without at least one Committer/Reviewer/TSC member comment stating they've looked at the code.)
+- May merge external pull requests for accepted issues upon reviewing and approving the changes.
+- May merge their own pull requests once they have collected the feedback they deem necessary. (No pull request should be merged without at least one Committer/Reviewer/TSC member comment stating they've looked at the code.)
+- May invoice for the hours they spend working on ESLint at a rate of $80 USD per hour.
To become a Reviewer:
-* Work in a helpful and collaborative way with the community.
-* Have given good feedback on others' submissions and displayed an overall understanding of the code quality standards for the project.
-* Commit to being a part of the community for the long-term.
-* Have submitted a minimum of 50 qualifying pull requests.
+- Work in a helpful and collaborative way with the community.
+- Have given good feedback on others' submissions and displayed an overall understanding of the code quality standards for the project.
+- Commit to being a part of the community for the long-term.
+- Have submitted a minimum of 50 qualifying pull requests.
A Committer is invited to become a Reviewer by existing Reviewers and TSC members. A nomination will result in discussion and then a decision by the TSC.
-#### Process for Adding Reviewers
-
-1. Add the GitHub user to the "ESLint Reviewers" GitHub team
-1. Tweet congratulations to the new Reviewer from the ESLint Twitter account
-
### Technical Steering Committee (TSC)
The ESLint project is jointly governed by a Technical Steering Committee (TSC) which is responsible for high-level guidance of the project.
The TSC has final authority over this project including:
-* Technical direction
-* Project governance and process (including this policy)
-* Contribution policy
-* GitHub repository hosting
+- Technical direction
+- Project governance and process (including this policy)
+- Contribution policy
+- GitHub repository hosting
TSC seats are not time-limited. The size of the TSC can not be larger than five members. This size ensures adequate coverage of important areas of expertise balanced with the ability to make decisions efficiently.
@@ -130,27 +129,15 @@ TSC members have additional responsibilities over and above those of a Reviewer.
TSC members may perform all of the duties of Reviewers, and also:
-* May release new versions of all ESLint projects.
-* May participate in TSC meetings.
-* May propose budget items.
-* May propose new ESLint projects.
+- May release new versions of all ESLint projects.
+- May participate in TSC meetings.
+- May propose budget items.
+- May propose new ESLint projects.
There is no specific set of requirements or qualifications for TSC members beyond those that are expected of Reviewers.
A Reviewer is invited to become a TSC member by existing TSC members. A nomination will result in discussion and then a decision by the TSC.
-#### Process for Adding TSC Members
-
-1. Add the GitHub user to the "ESLint TSC" GitHub team
-1. Set the GitHub user to be have the "Owner" role for the ESLint organization
-1. Send a welcome email with a link to the [Maintain ESLint documentation](../maintain/) and instructions for npm 2FA.
-1. Invite to the Discord TSC channel
-1. Make the TSC member an admin on the ESLint team mailing list
-1. Add the TSC member to the recurring TSC meeting event on Google Calendar
-1. Add the TSC member as an admin to ESLint Twitter Account on Tweetdeck
-1. Add the TSC member to the ESLint TSC mailing list as an "Owner"
-1. Tweet congratulations to the new TSC member from the ESLint Twitter account
-
#### TSC Meetings
The TSC meets every other week in the TSC Meeting [Discord](https://eslint.org/chat) channel. The meeting is run by a designated moderator approved by the TSC.
@@ -197,7 +184,7 @@ either a closing vote or a vote to table the issue to the next
meeting. The call for a vote must be approved by a majority of the TSC
or else the discussion will continue. Simple majority wins.
-----
+---
This work is a derivative of [YUI Contributor Model](https://github.com/yui/yui3/wiki/Contributor-Model) and the [Node.js Project Governance Model](https://github.com/nodejs/node/blob/master/GOVERNANCE.md).
diff --git a/docs/src/contribute/index.md b/docs/src/contribute/index.md
index 3b0a6d8f5c53..8bf55b30d302 100644
--- a/docs/src/contribute/index.md
+++ b/docs/src/contribute/index.md
@@ -60,7 +60,7 @@ Describes the governance policy for ESLint, including the rights and privileges
## [Report a Security Vulnerability](report-security-vulnerability)
-To report a security vulnerability in ESLint, please create an advisory on Github.
+To report a security vulnerability in ESLint, please create an advisory on GitHub.
## Sign the CLA
diff --git a/docs/src/contribute/package-json-conventions.md b/docs/src/contribute/package-json-conventions.md
index a030dc3f3dc9..16327785b46a 100644
--- a/docs/src/contribute/package-json-conventions.md
+++ b/docs/src/contribute/package-json-conventions.md
@@ -1,6 +1,5 @@
---
title: Package.json Conventions
-edit_link: https://github.com/eslint/eslint/edit/main/docs/src/contribute/package-json-conventions.md
eleventyNavigation:
key: package.json conventions
parent: contribute to eslint
@@ -19,7 +18,7 @@ Here is a summary of the proposal in ABNF.
```abnf
name = life-cycle / main target? option* ":watch"?
life-cycle = "prepare" / "preinstall" / "install" / "postinstall" / "prepublish" / "preprepare" / "prepare" / "postprepare" / "prepack" / "postpack" / "prepublishOnly"
-main = "build" / "lint" ":fix"? / "release" / "start" / "test" / "fetch"
+main = "build" / "lint" ":fix"? / "fmt" ":check"? / "release" / "start" / "test" / "fetch"
target = ":" word ("-" word)* / extension ("+" extension)*
option = ":" word ("-" word)*
word = ALPHA +
@@ -28,7 +27,7 @@ extension = ( ALPHA / DIGIT )+
## Order
-The script names MUST appear in the package.json file in alphabetical order. The other conventions outlined in this document ensure that alphabetical order will coincide with logical groupings.
+The script names MUST appear in the `package.json` file in alphabetical order. The other conventions outlined in this document ensure that alphabetical order will coincide with logical groupings.
## Main Script Names
@@ -48,7 +47,7 @@ If a package contains any `fetch:*` scripts, there MAY be a script named `fetch`
### Release
-Scripts that have public side effects (publishing the web site, committing to Git, etc.) MUST begin with `release`.
+Scripts that have public side effects (publishing the website, committing to Git, etc.) MUST begin with `release`.
### Lint
@@ -58,6 +57,12 @@ If a package contains any `lint:*` scripts, there SHOULD be a script named `lint
If fixing is available, a linter MUST NOT apply fixes UNLESS the script contains the `:fix` modifier (see below).
+### Fmt
+
+Scripts that format source code MUST have names that begin with `fmt`.
+
+If a package contains any `fmt:*` scripts, there SHOULD be a script named `fmt` that applies formatting fixes to all source files. There SHOULD also be a script named `fmt:check` that validates code formatting without modifying files and exits non-zero if any files are out of compliance.
+
### Start
A `start` script is used to start a server. As of this writing, no ESLint package has more than one `start` script, so there's no need `start` to have any modifiers.
@@ -74,19 +79,23 @@ A test script SHOULD report test coverage when possible.
## Modifiers
-One or more of the following modifiers MAY be appended to the standard script names above. If a target has modifiers, they MUST be in the order in which they appear below (e.g. `lint:fix:js:watch` not `lint:watch:js:fix`)
+One or more of the following modifiers MAY be appended to the standard script names above. If a target has modifiers, they MUST be in the order in which they appear below (e.g. `lint:fix:js:watch` not `lint:watch:js:fix`).
### Fix
If it's possible for a linter to fix problems that it finds, add a copy of the script with `:fix` appended to the end that also fixes.
+### Check
+
+If a script validates code or artifacts without making any modifications, append `:check` to the script name. This modifier is typically used for formatters (e.g., `fmt:check`) to verify that files conform to the expected format and to exit with a non-zero status if any issues are found. Scripts with the `:check` modifier MUST NOT alter any files or outputs.
+
### Target
The name of the target of the action being run. In the case of a `build` script, it SHOULD identify the build artifact(s), e.g. "javascript" or "css" or "website". In the case of a `lint` or `test` script, it SHOULD identify the item(s) being linted or tested. In the case of a `start` script, it SHOULD identify which server is starting.
A target MAY refer to a list of affected file extensions (such as `cjs` or `less`) delimited by a `+`. If there is more than one extension, the list SHOULD be alphabetized. When a file extension has variants (such as `cjs` for CommonJS and `mjs` for ESM), the common part of the extension MAY be used instead of explicitly listing out all of the variants (e.g. `js` instead of `cjs+jsx+mjs`).
-The target SHOULD NOT refer to name of the name of the tool that's performing the action (`eleventy`, `webpack`, etc.)
+The target SHOULD NOT refer to name of the name of the tool that's performing the action (`eleventy`, `webpack`, etc.).
### Options
diff --git a/docs/src/contribute/propose-new-rule.md b/docs/src/contribute/propose-new-rule.md
index 7aa9d3a0e7cc..bdd152d2989f 100644
--- a/docs/src/contribute/propose-new-rule.md
+++ b/docs/src/contribute/propose-new-rule.md
@@ -34,9 +34,9 @@ We need all of this information in order to determine whether or not the rule is
In order for a rule to be accepted in the ESLint core, it must:
-1. Fulfill all the criteria listed in the "Core Rule Guidelines" section
-1. Have an ESLint team member champion inclusion of the rule
-1. Be related to an ECMAScript feature that has reached stage 4 in the preceding 12 months
+1. Fulfill all the criteria listed in the "Core Rule Guidelines" section.
+1. Have an ESLint team member champion inclusion of the rule.
+1. Be related to an ECMAScript feature that has reached stage 4 in the preceding 12 months.
Keep in mind that we have over 200 rules, and that is daunting both for end users and the ESLint team (who has to maintain them). As such, any new rules must be deemed of high importance to be considered for inclusion in ESLint.
diff --git a/docs/src/contribute/propose-rule-change.md b/docs/src/contribute/propose-rule-change.md
index d2d198b19bde..8bb85a110a33 100644
--- a/docs/src/contribute/propose-rule-change.md
+++ b/docs/src/contribute/propose-rule-change.md
@@ -19,9 +19,9 @@ We need all of this information in order to determine whether or not the change
In order for a rule change to be accepted into ESLint, it must:
-1. Adhere to the [Core Rule Guidelines](propose-new-rule#core-rule-guidelines)
-1. Have an ESLint team member champion the change
-1. Be important enough that rule is deemed incomplete without this change
+1. Adhere to the [Core Rule Guidelines](propose-new-rule#core-rule-guidelines).
+1. Have an ESLint team member champion the change.
+1. Be important enough that rule is deemed incomplete without this change.
## Implementation is Your Responsibility
diff --git a/docs/src/contribute/pull-requests.md b/docs/src/contribute/pull-requests.md
index 578118603752..6b5f395b1ce9 100644
--- a/docs/src/contribute/pull-requests.md
+++ b/docs/src/contribute/pull-requests.md
@@ -22,13 +22,13 @@ After that, you're ready to start working on code.
The process of submitting a pull request is fairly straightforward and generally follows the same pattern each time:
-1. [Create a new branch](#step1)
-2. [Make your changes](#step2)
-3. [Rebase onto upstream](#step3)
-4. [Run the tests](#step4)
-5. [Double check your submission](#step5)
-6. [Push your changes](#step6)
-7. [Submit the pull request](#step7)
+1. [Create a new branch](#step1).
+2. [Make your changes](#step2).
+3. [Rebase onto upstream](#step3).
+4. [Run the tests](#step4).
+5. [Double check your submission](#step5).
+6. [Push your changes](#step6).
+7. [Submit the pull request](#step7).
Details about each step are found below.
@@ -53,7 +53,7 @@ git add -A
git commit
```
-All ESLint projects follow [Conventional Commits](https://www.conventionalcommits.org/) for our commit messages. (Note: we donât support the optional scope in messages.) Here's an example commit message:
+All ESLint projects follow [Conventional Commits](https://www.conventionalcommits.org/) for our commit messages. (Note: we donât support the optional scope in messages.) Here's an example commit message:
```txt
tag: Short description of what you did
@@ -67,17 +67,17 @@ The first line of the commit message (the summary) must have a specific format.
The `tag` is one of the following:
-* `fix` - for a bug fix.
-* `feat` - either for a backwards-compatible enhancement or for a rule change that adds reported problems.
-* `fix!` - for a backwards-incompatible bug fix.
-* `feat!` - for a backwards-incompatible enhancement or feature.
-* `docs` - changes to documentation only.
-* `chore` - for changes that aren't user-facing.
-* `build` - changes to build process only.
-* `refactor` - a change that doesn't affect APIs or user experience.
-* `test` - just changes to test files.
-* `ci` - changes to our CI configuration files and scripts.
-* `perf` - a code change that improves performance.
+- `fix` - for a bug fix.
+- `feat` - either for a backwards-compatible enhancement or for a rule change that adds reported problems.
+- `fix!` - for a backwards-incompatible bug fix.
+- `feat!` - for a backwards-incompatible enhancement or feature.
+- `docs` - changes to documentation only.
+- `chore` - for changes that aren't user-facing.
+- `build` - changes to build process only.
+- `refactor` - a change that doesn't affect APIs or user experience.
+- `test` - just changes to test files.
+- `ci` - changes to our CI configuration files and scripts.
+- `perf` - a code change that improves performance.
Use the [labels of the issue you are working on](work-on-issue#issue-labels) to determine the best tag.
@@ -114,12 +114,12 @@ If there are any failing tests, update your code until all tests pass.
With your code ready to go, this is a good time to double-check your submission to make sure it follows our conventions. Here are the things to check:
-* The commit message is properly formatted.
-* The change introduces no functional regression. Be sure to run `npm test` to verify your changes before submitting a pull request.
-* Make separate pull requests for unrelated changes. Large pull requests with multiple unrelated changes may be closed without merging.
-* All changes must be accompanied by tests, even if the feature you're working on previously had no tests.
-* All user-facing changes must be accompanied by appropriate documentation.
-* Follow the [Code Conventions](./code-conventions).
+- The commit message is properly formatted.
+- The change introduces no functional regression. Be sure to run `npm test` to verify your changes before submitting a pull request.
+- Make separate pull requests for unrelated changes. Large pull requests with multiple unrelated changes may be closed without merging.
+- All changes must be accompanied by tests, even if the feature you're working on previously had no tests.
+- All user-facing changes must be accompanied by appropriate documentation.
+- Follow the [Code Conventions](./code-conventions).
### Step 6: Push your changes
@@ -139,7 +139,7 @@ git push -f origin issue1234
Now you're ready to send the pull request. Go to your ESLint fork and then follow the [GitHub documentation](https://help.github.com/articles/creating-a-pull-request) on how to send a pull request.
-In order to submit code or documentation to an ESLint project, youâll be asked to sign our CLA when you send your first pull request. (Read more about the Open JS Foundation CLA process at .)
+In order to submit code or documentation to an ESLint project, youâll be asked to sign our CLA when you send your first pull request. (Read more about the OpenJS Foundation CLA process at .)
The pull request title is autogenerated from the summary of the first commit, but it can be edited before the pull request is submitted.
diff --git a/docs/src/contribute/tests.md b/docs/src/contribute/tests.md
index 9ad25500d82b..1fca848bc29b 100644
--- a/docs/src/contribute/tests.md
+++ b/docs/src/contribute/tests.md
@@ -29,18 +29,18 @@ If you want to run just one or a subset of `RuleTester` test cases, add `only: t
```js
ruleTester.run("my-rule", myRule, {
- valid: [
- RuleTester.only("const valid = 42;"),
- // Other valid cases
- ],
- invalid: [
- {
- code: "const invalid = 42;",
- only: true,
- },
- // Other invalid cases
- ]
-})
+ valid: [
+ RuleTester.only("const valid = 42;"),
+ // Other valid cases
+ ],
+ invalid: [
+ {
+ code: "const invalid = 42;",
+ only: true,
+ },
+ // Other invalid cases
+ ],
+});
```
Running individual tests is useful when you're working on a specific bug and iterating on the solution. You should be sure to run `npm test` before submitting a pull request. `npm test` uses Mocha's `--forbid-only` option to prevent `only` tests from passing full test runs.
diff --git a/docs/src/contribute/work-on-issue.md b/docs/src/contribute/work-on-issue.md
index d258aa4eb156..8232a1ef1d9a 100644
--- a/docs/src/contribute/work-on-issue.md
+++ b/docs/src/contribute/work-on-issue.md
@@ -13,9 +13,10 @@ Our public [issues tracker](https://github.com/eslint/eslint/issues) lists all o
We use labels to indicate the status of issues. The most complete documentation on the labels is found in the [Maintain ESLint documentation](../maintain/manage-issues#when-an-issue-or-pull-request-is-opened), but most contributors should find the information on this page sufficient. The most important questions that labels can help you, as a contributor, answer are:
-1. Is this issue available for me to work on? If you have little or no experience contributing to ESLint, the [`good first issue`](https://github.com/eslint/eslint/labels/good%20first%20issue) label marks appropriate issues. Otherwise, the [`help wanted`](https://github.com/eslint/eslint/labels/help%20wanted) label is an invitation to work on the issue. If you have more experience, you can try working on other issues labeled [`accepted`](https://github.com/eslint/eslint/labels/accepted). Conversely, issues not yet ready to work on are labeled `triage`, `evaluating`, and/or `needs bikeshedding`, and issues that cannot currently be worked on because of something else, such as a bug in a dependency, are labeled `blocked`.
-1. What is this issue about? Labels describing the nature of issues include `bug`, `enhancement`, `feature`, `question`, `rule`, `documentation`, `core`, `build`, `cli`, `infrastructure`, `breaking`, and `chore`. These are documented in [Maintain ESLint](../maintain/manage-issues#types-of-issues-and-pull-requests).
-1. What is the priority of this issue? Because we have a lot of issues, we prioritize certain issues above others. The following is the list of priorities, from highest to lowest:
+1. **Is this issue ready for a pull request?** Issues that are ready for pull requests have the [`accepted`](https://github.com/eslint/eslint/labels/accepted) label, which indicates that the team has agreed to accept a pull request. Please do not send pull requests for issues that have not been marked as accepted.
+2. **Is this issue right for a beginner?** If you have little or no experience contributing to ESLint, the [`good first issue`](https://github.com/eslint/eslint/labels/good%20first%20issue) label marks appropriate issues. Otherwise, the [`help wanted`](https://github.com/eslint/eslint/labels/help%20wanted) label is an invitation to work on the issue. If you have more experience, you can try working on other issues labeled [`accepted`](https://github.com/eslint/eslint/labels/accepted).
+3. **What is this issue about?** Labels describing the nature of issues include `bug`, `enhancement`, `feature`, `question`, `rule`, `documentation`, `core`, `build`, `cli`, `infrastructure`, `breaking`, and `chore`. These are documented in [Maintain ESLint](../maintain/manage-issues#types-of-issues-and-pull-requests).
+4. **What is the priority of this issue?** Because we have a lot of issues, we prioritize certain issues above others. The following is the list of priorities, from highest to lowest:
1. **Bugs** - problems with the project are actively affecting users. We want to get these resolved as quickly as possible.
1. **Documentation** - documentation issues are a type of bug in that they actively affect current users. As such, we want to address documentation issues as quickly as possible.
@@ -23,21 +24,38 @@ We use labels to indicate the status of issues. The most complete documentation
1. **Enhancements** - requested improvements for existing functionality.
1. **Other** - anything else.
- Some issues have had monetary rewards attached to them. Those are labeled `bounty`. Bounties are assigned via [BountySource](https://www.bountysource.com/teams/eslint/issues).
-
## Starting Work
-If you're going to work on an issue, please add a comment to that issue saying so and indicating when you think you will complete it. It will help us to avoid duplication of effort. Some examples of good comments are:
+::: important
+Before starting to work on an existing issue, please check if the issue has been assigned to anyone. If it has, then that person is already responsible for submitting a pull request and you should choose a different issue to work on.
+:::
+
+### Claiming an issue
+
+If you're going to work on an issue, please _claim_ the issue by adding a comment saying you're working on it and indicating when you think you will complete it. This helps us to avoid duplication of effort. Some examples of good claim comments are:
+
+- "I'll take a look at this over the weekend."
+- "I'm going to do this, give me two weeks."
+- "Working on this" (as in, I'm working on it right now)
+
+The team will validate your claim by assigning the issue to you.
+
+### Offering help on a claimed issue
+
+If an issue has an assignee or has already been claimed by someone, please be respectful of that person's desire to complete the work and don't work on it unless you verify that they are no longer interested or would welcome the help. If there hasn't been activity on the issue after two weeks, you can express your interest in helping with the issue. For example:
+
+- "Are you still working on this? If not, I'd love to work on it."
+- "Do you need any help on this? I'm interested."
+
+It is up to the assignee to decide if they're going to continue working on the issue or if they'd like your help.
-* "I'll take a look at this over the weekend."
-* "I'm going to do this, give me two weeks."
-* "Working on this" (as in, I'm working on it right now)
+If there is no response after a week, please contact a team member for help.
-If an issue has already been claimed by someone, please be respectful of that person's desire to complete the work and don't work on it unless you verify that they are no longer interested.
+### Unclaiming an issue
-If you find you can't finish the work, then simply add a comment letting people know, for example:
+If you claimed an issue and find you can't finish the work, then add a comment letting people know, for example:
-* "Sorry, it looks like I don't have time to do this."
-* "I thought I knew enough to fix this, but it turns out I don't."
+- "Sorry, it looks like I don't have time to do this."
+- "I thought I knew enough to fix this, but it turns out I don't."
No one will blame you for backing out of an issue if you are unable to complete it. We just want to keep the process moving along as efficiently as possible.
diff --git a/docs/src/extend/code-path-analysis.md b/docs/src/extend/code-path-analysis.md
index 879119574907..9bcbe73da1e4 100644
--- a/docs/src/extend/code-path-analysis.md
+++ b/docs/src/extend/code-path-analysis.md
@@ -1,6 +1,5 @@
---
title: Code Path Analysis Details
-
---
ESLint's rules can use code paths.
@@ -9,7 +8,7 @@ It forks/joins at such as `if` statements.
```js
if (a && b) {
- foo();
+ foo();
}
bar();
```
@@ -18,6 +17,10 @@ bar();

:::
+::: tip
+You can view code path diagrams for any JavaScript code using [Code Explorer](http://explorer.eslint.org).
+:::
+
## Objects
Program is expressed with several code paths.
@@ -31,15 +34,14 @@ This has references of both the initial segment and the final segments of a code
`CodePath` has the following properties:
-* `id` (`string`) - A unique string. Respective rules can use `id` to save additional information for each code path.
-* `origin` (`string`) - The reason that the code path was started. May be `"program"`, `"function"`, `"class-field-initializer"`, or `"class-static-block"`.
-* `initialSegment` (`CodePathSegment`) - The initial segment of this code path.
-* `finalSegments` (`CodePathSegment[]`) - The final segments which includes both returned and thrown.
-* `returnedSegments` (`CodePathSegment[]`) - The final segments which includes only returned.
-* `thrownSegments` (`CodePathSegment[]`) - The final segments which includes only thrown.
-* `currentSegments` (`CodePathSegment[]`) - **Deprecated.** Segments of the current traversal position.
-* `upper` (`CodePath|null`) - The code path of the upper function/global scope.
-* `childCodePaths` (`CodePath[]`) - Code paths of functions this code path contains.
+- `id` (`string`) - A unique string. Respective rules can use `id` to save additional information for each code path.
+- `origin` (`string`) - The reason that the code path was started. May be `"program"`, `"function"`, `"class-field-initializer"`, or `"class-static-block"`.
+- `initialSegment` (`CodePathSegment`) - The initial segment of this code path.
+- `finalSegments` (`CodePathSegment[]`) - The final segments which includes both returned and thrown.
+- `returnedSegments` (`CodePathSegment[]`) - The final segments which includes only returned.
+- `thrownSegments` (`CodePathSegment[]`) - The final segments which includes only thrown.
+- `upper` (`CodePath|null`) - The code path of the upper function/global scope.
+- `childCodePaths` (`CodePath[]`) - Code paths of functions this code path contains.
### `CodePathSegment`
@@ -49,10 +51,10 @@ Difference from doubly linked list is what there are forking and merging (the ne
`CodePathSegment` has the following properties:
-* `id` (`string`) - A unique string. Respective rules can use `id` to save additional information for each segment.
-* `nextSegments` (`CodePathSegment[]`) - The next segments. If forking, there are two or more. If final, there is nothing.
-* `prevSegments` (`CodePathSegment[]`) - The previous segments. If merging, there are two or more. If initial, there is nothing.
-* `reachable` (`boolean`) - A flag which shows whether or not it's reachable. This becomes `false` when preceded by `return`, `throw`, `break`, or `continue`.
+- `id` (`string`) - A unique string. Respective rules can use `id` to save additional information for each segment.
+- `nextSegments` (`CodePathSegment[]`) - The next segments. If forking, there are two or more. If final, there is nothing.
+- `prevSegments` (`CodePathSegment[]`) - The previous segments. If merging, there are two or more. If initial, there is nothing.
+- `reachable` (`boolean`) - A flag which shows whether or not it's reachable. This becomes `false` when preceded by `return`, `throw`, `break`, or `continue`.
## Events
@@ -60,106 +62,104 @@ There are seven events related to code paths, and you can define event handlers
```js
module.exports = {
- meta: {
- // ...
- },
- create(context) {
-
- return {
- /**
- * This is called at the start of analyzing a code path.
- * In this time, the code path object has only the initial segment.
- *
- * @param {CodePath} codePath - The new code path.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- onCodePathStart(codePath, node) {
- // do something with codePath
- },
-
- /**
- * This is called at the end of analyzing a code path.
- * In this time, the code path object is complete.
- *
- * @param {CodePath} codePath - The completed code path.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- onCodePathEnd(codePath, node) {
- // do something with codePath
- },
-
- /**
- * This is called when a reachable code path segment was created.
- * It meant the code path is forked or merged.
- * In this time, the segment has the previous segments and has been
- * judged reachable or not.
- *
- * @param {CodePathSegment} segment - The new code path segment.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- onCodePathSegmentStart(segment, node) {
- // do something with segment
- },
-
- /**
- * This is called when a reachable code path segment was left.
- * In this time, the segment does not have the next segments yet.
- *
- * @param {CodePathSegment} segment - The left code path segment.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- onCodePathSegmentEnd(segment, node) {
- // do something with segment
- },
-
- /**
- * This is called when an unreachable code path segment was created.
- * It meant the code path is forked or merged.
- * In this time, the segment has the previous segments and has been
- * judged reachable or not.
- *
- * @param {CodePathSegment} segment - The new code path segment.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- onUnreachableCodePathSegmentStart(segment, node) {
- // do something with segment
- },
-
- /**
- * This is called when an unreachable code path segment was left.
- * In this time, the segment does not have the next segments yet.
- *
- * @param {CodePathSegment} segment - The left code path segment.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- onUnreachableCodePathSegmentEnd(segment, node) {
- // do something with segment
- },
-
- /**
- * This is called when a code path segment was looped.
- * Usually segments have each previous segments when created,
- * but when looped, a segment is added as a new previous segment into a
- * existing segment.
- *
- * @param {CodePathSegment} fromSegment - A code path segment of source.
- * @param {CodePathSegment} toSegment - A code path segment of destination.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- onCodePathSegmentLoop(fromSegment, toSegment, node) {
- // do something with segment
- }
- };
-
- }
-}
+ meta: {
+ // ...
+ },
+ create(context) {
+ return {
+ /**
+ * This is called at the start of analyzing a code path.
+ * In this time, the code path object has only the initial segment.
+ *
+ * @param {CodePath} codePath - The new code path.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathStart(codePath, node) {
+ // do something with codePath
+ },
+
+ /**
+ * This is called at the end of analyzing a code path.
+ * In this time, the code path object is complete.
+ *
+ * @param {CodePath} codePath - The completed code path.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathEnd(codePath, node) {
+ // do something with codePath
+ },
+
+ /**
+ * This is called when a reachable code path segment was created.
+ * It meant the code path is forked or merged.
+ * In this time, the segment has the previous segments and has been
+ * judged reachable or not.
+ *
+ * @param {CodePathSegment} segment - The new code path segment.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathSegmentStart(segment, node) {
+ // do something with segment
+ },
+
+ /**
+ * This is called when a reachable code path segment was left.
+ * In this time, the segment does not have the next segments yet.
+ *
+ * @param {CodePathSegment} segment - The left code path segment.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathSegmentEnd(segment, node) {
+ // do something with segment
+ },
+
+ /**
+ * This is called when an unreachable code path segment was created.
+ * It meant the code path is forked or merged.
+ * In this time, the segment has the previous segments and has been
+ * judged reachable or not.
+ *
+ * @param {CodePathSegment} segment - The new code path segment.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onUnreachableCodePathSegmentStart(segment, node) {
+ // do something with segment
+ },
+
+ /**
+ * This is called when an unreachable code path segment was left.
+ * In this time, the segment does not have the next segments yet.
+ *
+ * @param {CodePathSegment} segment - The left code path segment.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onUnreachableCodePathSegmentEnd(segment, node) {
+ // do something with segment
+ },
+
+ /**
+ * This is called when a code path segment was looped.
+ * Usually segments have each previous segments when created,
+ * but when looped, a segment is added as a new previous segment into a
+ * existing segment.
+ *
+ * @param {CodePathSegment} fromSegment - A code path segment of source.
+ * @param {CodePathSegment} toSegment - A code path segment of destination.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathSegmentLoop(fromSegment, toSegment, node) {
+ // do something with segment
+ },
+ };
+ },
+};
```
### About `onCodePathSegmentLoop`
@@ -171,7 +171,7 @@ For Example 1:
```js
while (a) {
- a = foo();
+ a = foo();
}
bar();
```
@@ -179,7 +179,7 @@ bar();
1. First, the analysis advances to the end of loop.
:::img-container
- 
+
:::
2. Second, it creates the looping path.
@@ -187,20 +187,20 @@ bar();
It fires `onCodePathSegmentLoop` instead.
:::img-container
- 
+
:::
3. Last, it advances to the end.
:::img-container
- 
+
:::
For example 2:
```js
for (let i = 0; i < 10; ++i) {
- foo(i);
+ foo(i);
}
bar();
```
@@ -210,7 +210,7 @@ bar();
The `update` segment is hovered at first.
:::img-container
- 
+
:::
2. Second, it advances to `ForStatement.body`.
@@ -218,7 +218,7 @@ bar();
It keeps the `update` segment hovering.
:::img-container
- 
+
:::
3. Third, it creates the looping path from `body` segment to `update` segment.
@@ -226,7 +226,7 @@ bar();
It fires `onCodePathSegmentLoop` instead.
:::img-container
- 
+
:::
4. Fourth, also it creates the looping path from `update` segment to `test` segment.
@@ -234,13 +234,13 @@ bar();
It fires `onCodePathSegmentLoop` instead.
:::img-container
- 
+
:::
5. Last, it advances to the end.
:::img-container
- 
+
:::
## Usage Examples
@@ -251,51 +251,48 @@ To track the current code path segment position, you can define a rule like this
```js
module.exports = {
- meta: {
- // ...
- },
- create(context) {
-
- // tracks the code path we are currently in
- let currentCodePath;
-
- // tracks the segments we've traversed in the current code path
- let currentSegments;
-
- // tracks all current segments for all open paths
- const allCurrentSegments = [];
-
- return {
-
- onCodePathStart(codePath) {
- currentCodePath = codePath;
- allCurrentSegments.push(currentSegments);
- currentSegments = new Set();
- },
-
- onCodePathEnd(codePath) {
- currentCodePath = codePath.upper;
- currentSegments = allCurrentSegments.pop();
- },
-
- onCodePathSegmentStart(segment) {
- currentSegments.add(segment);
- },
-
- onCodePathSegmentEnd(segment) {
- currentSegments.delete(segment);
- },
-
- onUnreachableCodePathSegmentStart(segment) {
- currentSegments.add(segment);
- },
-
- onUnreachableCodePathSegmentEnd(segment) {
- currentSegments.delete(segment);
- }
- };
-
- }
+ meta: {
+ // ...
+ },
+ create(context) {
+ // tracks the code path we are currently in
+ let currentCodePath;
+
+ // tracks the segments we've traversed in the current code path
+ let currentSegments;
+
+ // tracks all current segments for all open paths
+ const allCurrentSegments = [];
+
+ return {
+ onCodePathStart(codePath) {
+ currentCodePath = codePath;
+ allCurrentSegments.push(currentSegments);
+ currentSegments = new Set();
+ },
+
+ onCodePathEnd(codePath) {
+ currentCodePath = codePath.upper;
+ currentSegments = allCurrentSegments.pop();
+ },
+
+ onCodePathSegmentStart(segment) {
+ currentSegments.add(segment);
+ },
+
+ onCodePathSegmentEnd(segment) {
+ currentSegments.delete(segment);
+ },
+
+ onUnreachableCodePathSegmentStart(segment) {
+ currentSegments.add(segment);
+ },
+
+ onUnreachableCodePathSegmentEnd(segment) {
+ currentSegments.delete(segment);
+ },
+ };
+ },
};
```
@@ -309,70 +306,65 @@ To find an unreachable node, track the current segment position and then use a n
```js
function areAnySegmentsReachable(segments) {
- for (const segment of segments) {
- if (segment.reachable) {
- return true;
- }
- }
+ for (const segment of segments) {
+ if (segment.reachable) {
+ return true;
+ }
+ }
- return false;
+ return false;
}
module.exports = {
- meta: {
- // ...
- },
- create(context) {
-
- // tracks the code path we are currently in
- let currentCodePath;
-
- // tracks the segments we've traversed in the current code path
- let currentSegments;
-
- // tracks all current segments for all open paths
- const allCurrentSegments = [];
-
- return {
-
- onCodePathStart(codePath) {
- currentCodePath = codePath;
- allCurrentSegments.push(currentSegments);
- currentSegments = new Set();
- },
-
- onCodePathEnd(codePath) {
- currentCodePath = codePath.upper;
- currentSegments = allCurrentSegments.pop();
- },
-
- onCodePathSegmentStart(segment) {
- currentSegments.add(segment);
- },
-
- onCodePathSegmentEnd(segment) {
- currentSegments.delete(segment);
- },
-
- onUnreachableCodePathSegmentStart(segment) {
- currentSegments.add(segment);
- },
-
- onUnreachableCodePathSegmentEnd(segment) {
- currentSegments.delete(segment);
- },
-
- ExpressionStatement(node) {
-
- // check all the code path segments that led to this node
- if (!areAnySegmentsReachable(currentSegments)) {
- context.report({ message: "Unreachable!", node });
- }
- }
-
- };
-
- }
+ meta: {
+ // ...
+ },
+ create(context) {
+ // tracks the code path we are currently in
+ let currentCodePath;
+
+ // tracks the segments we've traversed in the current code path
+ let currentSegments;
+
+ // tracks all current segments for all open paths
+ const allCurrentSegments = [];
+
+ return {
+ onCodePathStart(codePath) {
+ currentCodePath = codePath;
+ allCurrentSegments.push(currentSegments);
+ currentSegments = new Set();
+ },
+
+ onCodePathEnd(codePath) {
+ currentCodePath = codePath.upper;
+ currentSegments = allCurrentSegments.pop();
+ },
+
+ onCodePathSegmentStart(segment) {
+ currentSegments.add(segment);
+ },
+
+ onCodePathSegmentEnd(segment) {
+ currentSegments.delete(segment);
+ },
+
+ onUnreachableCodePathSegmentStart(segment) {
+ currentSegments.add(segment);
+ },
+
+ onUnreachableCodePathSegmentEnd(segment) {
+ currentSegments.delete(segment);
+ },
+
+ ExpressionStatement(node) {
+ // check all the code path segments that led to this node
+ if (!areAnySegmentsReachable(currentSegments)) {
+ context.report({ message: "Unreachable!", node });
+ }
+ },
+ };
+ },
};
```
@@ -390,114 +382,113 @@ Please use a map of information instead.
```js
function hasCb(node, context) {
- if (node.type.indexOf("Function") !== -1) {
- const sourceCode = context.sourceCode;
- return sourceCode.getDeclaredVariables(node).some(function(v) {
- return v.type === "Parameter" && v.name === "cb";
- });
- }
- return false;
+ if (node.type.indexOf("Function") !== -1) {
+ const sourceCode = context.sourceCode;
+ return sourceCode.getDeclaredVariables(node).some(function (v) {
+ return v.type === "Parameter" && v.name === "cb";
+ });
+ }
+ return false;
}
function isCbCalled(info) {
- return info.cbCalled;
+ return info.cbCalled;
}
module.exports = {
- meta: {
- // ...
- },
- create(context) {
-
- let funcInfo;
- const funcInfoStack = [];
- const segmentInfoMap = Object.create(null);
-
- return {
- // Checks `cb`.
- onCodePathStart(codePath, node) {
- funcInfoStack.push(funcInfo);
-
- funcInfo = {
- codePath: codePath,
- hasCb: hasCb(node, context),
- currentSegments: new Set()
- };
- },
-
- onCodePathEnd(codePath, node) {
- funcInfo = funcInfoStack.pop();
-
- // Checks `cb` was called in every paths.
- const cbCalled = codePath.finalSegments.every(function(segment) {
- const info = segmentInfoMap[segment.id];
- return info.cbCalled;
- });
-
- if (!cbCalled) {
- context.report({
- message: "`cb` should be called in every path.",
- node: node
- });
- }
- },
-
- // Manages state of code paths and tracks traversed segments
- onCodePathSegmentStart(segment) {
-
- funcInfo.currentSegments.add(segment);
-
- // Ignores if `cb` doesn't exist.
- if (!funcInfo.hasCb) {
- return;
- }
-
- // Initialize state of this path.
- const info = segmentInfoMap[segment.id] = {
- cbCalled: false
- };
-
- // If there are the previous paths, merges state.
- // Checks `cb` was called in every previous path.
- if (segment.prevSegments.length > 0) {
- info.cbCalled = segment.prevSegments.every(isCbCalled);
- }
- },
-
- // Tracks unreachable segment traversal
- onUnreachableCodePathSegmentStart(segment) {
- funcInfo.currentSegments.add(segment);
- },
-
- // Tracks reachable segment traversal
- onCodePathSegmentEnd(segment) {
- funcInfo.currentSegments.delete(segment);
- },
-
- // Tracks unreachable segment traversal
- onUnreachableCodePathSegmentEnd(segment) {
- funcInfo.currentSegments.delete(segment);
- },
-
- // Checks reachable or not.
- CallExpression(node) {
-
- // Ignores if `cb` doesn't exist.
- if (!funcInfo.hasCb) {
- return;
- }
-
- // Sets marks that `cb` was called.
- const callee = node.callee;
- if (callee.type === "Identifier" && callee.name === "cb") {
- funcInfo.currentSegments.forEach(segment => {
- const info = segmentInfoMap[segment.id];
- info.cbCalled = true;
- });
- }
- }
- };
- }
+ meta: {
+ // ...
+ },
+ create(context) {
+ let funcInfo;
+ const funcInfoStack = [];
+ const segmentInfoMap = Object.create(null);
+
+ return {
+ // Checks `cb`.
+ onCodePathStart(codePath, node) {
+ funcInfoStack.push(funcInfo);
+
+ funcInfo = {
+ codePath: codePath,
+ hasCb: hasCb(node, context),
+ currentSegments: new Set(),
+ };
+ },
+
+ onCodePathEnd(codePath, node) {
+ funcInfo = funcInfoStack.pop();
+
+ // Checks `cb` was called in every paths.
+ const cbCalled = codePath.finalSegments.every(
+ function (segment) {
+ const info = segmentInfoMap[segment.id];
+ return info.cbCalled;
+ },
+ );
+
+ if (!cbCalled) {
+ context.report({
+ message: "`cb` should be called in every path.",
+ node: node,
+ });
+ }
+ },
+
+ // Manages state of code paths and tracks traversed segments
+ onCodePathSegmentStart(segment) {
+ funcInfo.currentSegments.add(segment);
+
+ // Ignores if `cb` doesn't exist.
+ if (!funcInfo.hasCb) {
+ return;
+ }
+
+ // Initialize state of this path.
+ const info = (segmentInfoMap[segment.id] = {
+ cbCalled: false,
+ });
+
+ // If there are the previous paths, merges state.
+ // Checks `cb` was called in every previous path.
+ if (segment.prevSegments.length > 0) {
+ info.cbCalled = segment.prevSegments.every(isCbCalled);
+ }
+ },
+
+ // Tracks unreachable segment traversal
+ onUnreachableCodePathSegmentStart(segment) {
+ funcInfo.currentSegments.add(segment);
+ },
+
+ // Tracks reachable segment traversal
+ onCodePathSegmentEnd(segment) {
+ funcInfo.currentSegments.delete(segment);
+ },
+
+ // Tracks unreachable segment traversal
+ onUnreachableCodePathSegmentEnd(segment) {
+ funcInfo.currentSegments.delete(segment);
+ },
+
+ // Checks reachable or not.
+ CallExpression(node) {
+ // Ignores if `cb` doesn't exist.
+ if (!funcInfo.hasCb) {
+ return;
+ }
+
+ // Sets marks that `cb` was called.
+ const callee = node.callee;
+ if (callee.type === "Identifier" && callee.name === "cb") {
+ funcInfo.currentSegments.forEach(segment => {
+ const info = segmentInfoMap[segment.id];
+ info.cbCalled = true;
+ });
+ }
+ },
+ };
+ },
};
```
@@ -521,9 +512,9 @@ console.log("Hello world!");
```js
if (a) {
- foo();
+ foo();
} else {
- bar();
+ bar();
}
```
@@ -535,11 +526,11 @@ if (a) {
```js
if (a) {
- foo();
+ foo();
} else if (b) {
- bar();
+ bar();
} else if (c) {
- hoge();
+ hoge();
}
```
@@ -551,18 +542,18 @@ if (a) {
```js
switch (a) {
- case 0:
- foo();
- break;
-
- case 1:
- case 2:
- bar();
- // fallthrough
-
- case 3:
- hoge();
- break;
+ case 0:
+ foo();
+ break;
+
+ case 1:
+ case 2:
+ bar();
+ // fallthrough
+
+ case 3:
+ hoge();
+ break;
}
```
@@ -574,22 +565,22 @@ switch (a) {
```js
switch (a) {
- case 0:
- foo();
- break;
-
- case 1:
- case 2:
- bar();
- // fallthrough
-
- case 3:
- hoge();
- break;
-
- default:
- fuga();
- break;
+ case 0:
+ foo();
+ break;
+
+ case 1:
+ case 2:
+ bar();
+ // fallthrough
+
+ case 3:
+ hoge();
+ break;
+
+ default:
+ fuga();
+ break;
}
```
@@ -601,22 +592,22 @@ switch (a) {
```js
try {
- foo();
- if (a) {
- throw new Error();
- }
- bar();
+ foo();
+ if (a) {
+ throw new Error();
+ }
+ bar();
} catch (err) {
- hoge(err);
+ hoge(err);
}
last();
```
It creates the paths from `try` block to `catch` block at:
-* `throw` statements.
-* The first throwable node (e.g. a function call) in the `try` block.
-* The end of the `try` block.
+- `throw` statements.
+- The first throwable node (e.g. a function call) in the `try` block.
+- The end of the `try` block.
:::img-container

@@ -626,16 +617,16 @@ It creates the paths from `try` block to `catch` block at:
```js
try {
- foo();
- bar();
+ foo();
+ bar();
} finally {
- fuga();
+ fuga();
}
last();
```
If there is not `catch` block, `finally` block has two current segments.
-At this time, `CodePath.currentSegments.length` is `2`.
+At this time when running the previous example to find unreachable nodes, `currentSegments.length` is `2`.
One is the normal path, and another is the leaving path (`throw` or `return`).
:::img-container
@@ -646,12 +637,12 @@ One is the normal path, and another is the leaving path (`throw` or `return`).
```js
try {
- foo();
- bar();
+ foo();
+ bar();
} catch (err) {
- hoge(err);
+ hoge(err);
} finally {
- fuga();
+ fuga();
}
last();
```
@@ -664,11 +655,11 @@ last();
```js
while (a) {
- foo();
- if (b) {
- continue;
- }
- bar();
+ foo();
+ if (b) {
+ continue;
+ }
+ bar();
}
```
@@ -680,8 +671,8 @@ while (a) {
```js
do {
- foo();
- bar();
+ foo();
+ bar();
} while (a);
```
@@ -693,11 +684,11 @@ do {
```js
for (let i = 0; i < 10; ++i) {
- foo();
- if (b) {
- break;
- }
- bar();
+ foo();
+ if (b) {
+ break;
+ }
+ bar();
}
```
@@ -709,7 +700,7 @@ for (let i = 0; i < 10; ++i) {
```js
for (;;) {
- foo();
+ foo();
}
bar();
```
@@ -722,7 +713,7 @@ bar();
```js
for (let key in obj) {
- foo(key);
+ foo(key);
}
```
@@ -734,10 +725,10 @@ for (let key in obj) {
```js
function foo(a) {
- if (a) {
- return;
- }
- bar();
+ if (a) {
+ return;
+ }
+ bar();
}
foo(false);
@@ -745,14 +736,14 @@ foo(false);
It creates two code paths.
-* The global's
+- The global's
:::img-container
- 
+
:::
-* The function's
+- The function's
:::img-container
- 
+
:::
diff --git a/docs/src/extend/custom-formatters.md b/docs/src/extend/custom-formatters.md
index b95db0f8bd62..6f2c40ebcab4 100644
--- a/docs/src/extend/custom-formatters.md
+++ b/docs/src/extend/custom-formatters.md
@@ -5,7 +5,6 @@ eleventyNavigation:
parent: extend eslint
title: Custom Formatters
order: 4
-
---
Custom formatters let you display linting results in a format that best fits your needs, whether that's in a specific file format, a certain display style, or a format optimized for a particular tool.
@@ -20,8 +19,8 @@ Each formatter is a function that receives a `results` object and a `context` as
```js
//my-awesome-formatter.js
-module.exports = function(results, context) {
- return JSON.stringify(results, null, 2);
+module.exports = function (results, context) {
+ return JSON.stringify(results, null, 2);
};
```
@@ -29,9 +28,9 @@ A formatter can also be an async function (from ESLint v8.4.0), the following sh
```js
//my-awesome-formatter.js
-module.exports = async function(results) {
- const formatted = await asyncTask();
- return formatted;
+module.exports = async function (results) {
+ const formatted = await asyncTask();
+ return formatted;
};
```
@@ -49,42 +48,41 @@ The `results` object passed into a formatter is an array of [`result`](#the-resu
```js
[
- {
- filePath: "/path/to/a/file.js",
- messages: [
- {
- ruleId: "curly",
- severity: 2,
- message: "Expected { after 'if' condition.",
- line: 2,
- column: 1,
- nodeType: "IfStatement"
- },
- {
- ruleId: "no-process-exit",
- severity: 2,
- message: "Don't use process.exit(); throw an error instead.",
- line: 3,
- column: 1,
- nodeType: "CallExpression"
- }
- ],
- errorCount: 2,
- warningCount: 0,
- fixableErrorCount: 0,
- fixableWarningCount: 0,
- source:
- "var err = doStuff();\nif (err) console.log('failed tests: ' + err);\nprocess.exit(1);\n"
- },
- {
- filePath: "/path/to/Gruntfile.js",
- messages: [],
- errorCount: 0,
- warningCount: 0,
- fixableErrorCount: 0,
- fixableWarningCount: 0
- }
-]
+ {
+ filePath: "/path/to/a/file.js",
+ messages: [
+ {
+ ruleId: "curly",
+ severity: 2,
+ message: "Expected { after 'if' condition.",
+ line: 2,
+ column: 1,
+ nodeType: "IfStatement",
+ },
+ {
+ ruleId: "no-process-exit",
+ severity: 2,
+ message: "Don't use process.exit(); throw an error instead.",
+ line: 3,
+ column: 1,
+ nodeType: "CallExpression",
+ },
+ ],
+ errorCount: 2,
+ warningCount: 0,
+ fixableErrorCount: 0,
+ fixableWarningCount: 0,
+ source: "var err = doStuff();\nif (err) console.log('failed tests: ' + err);\nprocess.exit(1);\n",
+ },
+ {
+ filePath: "/path/to/Gruntfile.js",
+ messages: [],
+ errorCount: 0,
+ warningCount: 0,
+ fixableErrorCount: 0,
+ fixableWarningCount: 0,
+ },
+];
```
#### The `result` Object
@@ -94,33 +92,34 @@ also be manually applied to that page. -->
Each object in the `results` array is a `result` object. Each `result` object contains the path of the file that was linted and information about linting issues that were encountered. Here are the properties available on each `result` object:
-* **filePath**: The absolute path to the file that was linted.
-* **messages**: An array of [`message`](#the-message-object) objects. See below for more info about messages.
-* **errorCount**: The number of errors for the given file.
-* **warningCount**: The number of warnings for the given file.
-* **source**: The source code for the given file. This property is omitted if this file has no errors/warnings or if the `output` property is present.
-* **output**: The source code for the given file with as many fixes applied as possible. This property is omitted if no fix is available.
+- **filePath**: The absolute path to the file that was linted.
+- **messages**: An array of [`message`](#the-message-object) objects. See below for more info about messages.
+- **errorCount**: The number of errors for the given file.
+- **warningCount**: The number of warnings for the given file.
+- **stats**: The optional [`stats`](./stats#-stats-type) object that only exists when the `stats` option is used.
+- **source**: The source code for the given file. This property is omitted if this file has no errors/warnings or if the `output` property is present.
+- **output**: The source code for the given file with as many fixes applied as possible. This property is omitted if no fix is available.
##### The `message` Object
Each `message` object contains information about the ESLint rule that was triggered by some source code. The properties available on each `message` object are:
-* **ruleId**: the ID of the rule that produced the error or warning.
-* **severity**: the severity of the failure, `1` for warnings and `2` for errors.
-* **message**: the human readable description of the error.
-* **line**: the line where the issue is located.
-* **column**: the column where the issue is located.
-* **nodeType**: the type of the node in the [AST](https://github.com/estree/estree/blob/master/es5.md#node-objects)
+- **ruleId**: the ID of the rule that produced the error or warning. If the error or warning was not produced by a rule (for example, if it's a parsing error), this is `null`.
+- **severity**: the severity of the failure, `1` for warnings and `2` for errors.
+- **message**: the human readable description of the error.
+- **line**: the line where the issue is located.
+- **column**: the column where the issue is located.
+- **nodeType**: (**Deprecated:** This property will be removed in a future version of ESLint.) the type of the node in the [AST](https://github.com/estree/estree/blob/master/es5.md#node-objects) or `null` if the issue isn't related to a particular AST node.
### The `context` Argument
The formatter function receives a `context` object as its second argument. The object has the following properties:
-* `cwd`: The current working directory. This value comes from the `cwd` constructor option of the [ESLint](../integrate/nodejs-api#-new-eslintoptions) class.
-* `maxWarningsExceeded` (optional): If `--max-warnings` was set and the number of warnings exceeded the limit, this property's value is an object containing two properties:
- * `maxWarnings`: the value of the `--max-warnings` option
- * `foundWarnings`: the number of lint warnings
-* `rulesMeta`: The `meta` property values of rules. See the [Custom Rules](custom-rules) page for more information about rules.
+- `cwd`: The current working directory. This value comes from the `cwd` constructor option of the [ESLint](../integrate/nodejs-api#-new-eslintoptions) class.
+- `maxWarningsExceeded` (optional): If `--max-warnings` was set and the number of warnings exceeded the limit, this property's value is an object containing two properties:
+ - `maxWarnings`: the value of the `--max-warnings` option
+ - `foundWarnings`: the number of lint warnings
+- `rulesMeta`: The `meta` property values of rules. See the [Custom Rules](custom-rules) page for more information about rules.
For example, here's what the object would look like if the rule `no-extra-semi` had been run:
@@ -162,61 +161,61 @@ Custom formatters have access to environment variables and so can change their b
Here's an example that uses a `FORMATTER_SKIP_WARNINGS` environment variable to determine whether to show warnings in the results:
```js
-module.exports = function(results) {
- var skipWarnings = process.env.FORMATTER_SKIP_WARNINGS === "true";
-
- var results = results || [];
- var summary = results.reduce(
- function(seq, current) {
- current.messages.forEach(function(msg) {
- var logMessage = {
- filePath: current.filePath,
- ruleId: msg.ruleId,
- message: msg.message,
- line: msg.line,
- column: msg.column
- };
-
- if (msg.severity === 1) {
- logMessage.type = "warning";
- seq.warnings.push(logMessage);
- }
- if (msg.severity === 2) {
- logMessage.type = "error";
- seq.errors.push(logMessage);
- }
- });
- return seq;
- },
- {
- errors: [],
- warnings: []
- }
- );
-
- if (summary.errors.length > 0 || summary.warnings.length > 0) {
- var warnings = !skipWarnings ? summary.warnings : []; // skip the warnings in that case
-
- var lines = summary.errors
- .concat(warnings)
- .map(function(msg) {
- return (
- "\n" +
- msg.type +
- " " +
- msg.ruleId +
- "\n " +
- msg.filePath +
- ":" +
- msg.line +
- ":" +
- msg.column
- );
- })
- .join("\n");
-
- return lines + "\n";
- }
+module.exports = function (results) {
+ var skipWarnings = process.env.FORMATTER_SKIP_WARNINGS === "true";
+
+ var results = results || [];
+ var summary = results.reduce(
+ function (seq, current) {
+ current.messages.forEach(function (msg) {
+ var logMessage = {
+ filePath: current.filePath,
+ ruleId: msg.ruleId,
+ message: msg.message,
+ line: msg.line,
+ column: msg.column,
+ };
+
+ if (msg.severity === 1) {
+ logMessage.type = "warning";
+ seq.warnings.push(logMessage);
+ }
+ if (msg.severity === 2) {
+ logMessage.type = "error";
+ seq.errors.push(logMessage);
+ }
+ });
+ return seq;
+ },
+ {
+ errors: [],
+ warnings: [],
+ },
+ );
+
+ if (summary.errors.length > 0 || summary.warnings.length > 0) {
+ var warnings = !skipWarnings ? summary.warnings : []; // skip the warnings in that case
+
+ var lines = summary.errors
+ .concat(warnings)
+ .map(function (msg) {
+ return (
+ "\n" +
+ msg.type +
+ " " +
+ msg.ruleId +
+ "\n " +
+ msg.filePath +
+ ":" +
+ msg.line +
+ ":" +
+ msg.column
+ );
+ })
+ .join("\n");
+
+ return lines + "\n";
+ }
};
```
@@ -266,11 +265,11 @@ Because ESLint knows to look for packages beginning with `eslint-formatter-` whe
Tips for the `package.json` of a custom formatter:
-* The [`main`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#main) entry point must be the JavaScript file implementing your custom formatter.
-* Add these [`keywords`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#keywords) to help users find your formatter:
- * `"eslint"`
- * `"eslint-formatter"`
- * `"eslintformatter"`
+- The [`main`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#main) entry point must be the JavaScript file implementing your custom formatter.
+- Add these [`keywords`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#keywords) to help users find your formatter:
+ - `"eslint"`
+ - `"eslint-formatter"`
+ - `"eslintformatter"`
See all [custom formatters on npm](https://www.npmjs.com/search?q=eslint-formatter).
@@ -281,28 +280,28 @@ See all [custom formatters on npm](https://www.npmjs.com/search?q=eslint-formatt
A formatter that only reports on the total count of errors and warnings will look like this:
```javascript
-module.exports = function(results, context) {
- // accumulate the errors and warnings
- var summary = results.reduce(
- function(seq, current) {
- seq.errors += current.errorCount;
- seq.warnings += current.warningCount;
- return seq;
- },
- { errors: 0, warnings: 0 }
- );
-
- if (summary.errors > 0 || summary.warnings > 0) {
- return (
- "Errors: " +
- summary.errors +
- ", Warnings: " +
- summary.warnings +
- "\n"
- );
- }
-
- return "";
+module.exports = function (results, context) {
+ // accumulate the errors and warnings
+ var summary = results.reduce(
+ function (seq, current) {
+ seq.errors += current.errorCount;
+ seq.warnings += current.warningCount;
+ return seq;
+ },
+ { errors: 0, warnings: 0 },
+ );
+
+ if (summary.errors > 0 || summary.warnings > 0) {
+ return (
+ "Errors: " +
+ summary.errors +
+ ", Warnings: " +
+ summary.warnings +
+ "\n"
+ );
+ }
+
+ return "";
};
```
@@ -323,59 +322,60 @@ Errors: 2, Warnings: 4
A more complex report could look like this:
```javascript
-module.exports = function(results, context) {
- var results = results || [];
-
- var summary = results.reduce(
- function(seq, current) {
- current.messages.forEach(function(msg) {
- var logMessage = {
- filePath: current.filePath,
- ruleId: msg.ruleId,
- ruleUrl: context.rulesMeta[msg.ruleId].docs.url,
- message: msg.message,
- line: msg.line,
- column: msg.column
- };
-
- if (msg.severity === 1) {
- logMessage.type = "warning";
- seq.warnings.push(logMessage);
- }
- if (msg.severity === 2) {
- logMessage.type = "error";
- seq.errors.push(logMessage);
- }
- });
- return seq;
- },
- {
- errors: [],
- warnings: []
- }
- );
-
- if (summary.errors.length > 0 || summary.warnings.length > 0) {
- var lines = summary.errors
- .concat(summary.warnings)
- .map(function(msg) {
- return (
- "\n" +
- msg.type +
- " " +
- msg.ruleId + (msg.ruleUrl ? " (" + msg.ruleUrl + ")" : "") +
- "\n " +
- msg.filePath +
- ":" +
- msg.line +
- ":" +
- msg.column
- );
- })
- .join("\n");
-
- return lines + "\n";
- }
+module.exports = function (results, context) {
+ var results = results || [];
+
+ var summary = results.reduce(
+ function (seq, current) {
+ current.messages.forEach(function (msg) {
+ var logMessage = {
+ filePath: current.filePath,
+ ruleId: msg.ruleId,
+ ruleUrl: context.rulesMeta[msg.ruleId].docs.url,
+ message: msg.message,
+ line: msg.line,
+ column: msg.column,
+ };
+
+ if (msg.severity === 1) {
+ logMessage.type = "warning";
+ seq.warnings.push(logMessage);
+ }
+ if (msg.severity === 2) {
+ logMessage.type = "error";
+ seq.errors.push(logMessage);
+ }
+ });
+ return seq;
+ },
+ {
+ errors: [],
+ warnings: [],
+ },
+ );
+
+ if (summary.errors.length > 0 || summary.warnings.length > 0) {
+ var lines = summary.errors
+ .concat(summary.warnings)
+ .map(function (msg) {
+ return (
+ "\n" +
+ msg.type +
+ " " +
+ msg.ruleId +
+ (msg.ruleUrl ? " (" + msg.ruleUrl + ")" : "") +
+ "\n " +
+ msg.filePath +
+ ":" +
+ msg.line +
+ ":" +
+ msg.column
+ );
+ })
+ .join("\n");
+
+ return lines + "\n";
+ }
};
```
diff --git a/docs/src/extend/custom-parsers.md b/docs/src/extend/custom-parsers.md
index 55ed726f046c..8a84dda68199 100644
--- a/docs/src/extend/custom-parsers.md
+++ b/docs/src/extend/custom-parsers.md
@@ -5,18 +5,19 @@ eleventyNavigation:
parent: extend eslint
title: Custom Parsers
order: 5
-
---
+{%- from 'components/npm_tabs.macro.html' import npm_tabs with context %}
+
ESLint custom parsers let you extend ESLint to support linting new non-standard JavaScript language features or custom syntax in your code. A parser is responsible for taking your code and transforming it into an abstract syntax tree (AST) that ESLint can then analyze and lint.
## Creating a Custom Parser
### Methods in Custom Parsers
-A custom parser is a JavaScript object with either a `parse` or `parseForESLint` method. The `parse` method only returns the AST, whereas `parseForESLint` also returns additional values that let the parser customize the behavior of ESLint even more.
+A custom parser is a JavaScript object with either a `parse()` or `parseForESLint()` method. The `parse` method only returns the AST, whereas `parseForESLint()` also returns additional values that let the parser customize the behavior of ESLint even more.
-Both methods should take in the source code as the first argument, and an optional configuration object as the second argument, which is provided as [`parserOptions`](../use/configure/language-options#specifying-parser-options) in a configuration file.
+Both methods should be instance (own) properties and take in the source code as the first argument, and an optional configuration object as the second argument, which is provided as [`parserOptions`](../use/configure/language-options#specifying-parser-options) in a configuration file.
```javascript
// customParser.js
@@ -25,12 +26,12 @@ const espree = require("espree");
// Logs the duration it takes to parse each file.
function parse(code, options) {
- const label = `Parsing file "${options.filePath}"`;
- console.time(label);
- const ast = espree.parse(code, options);
- console.timeEnd(label);
- return ast; // Only the AST is returned.
-};
+ const label = `Parsing file "${options.filePath}"`;
+ console.time(label);
+ const ast = espree.parse(code, options);
+ console.timeEnd(label);
+ return ast; // Only the AST is returned.
+}
module.exports = { parse };
```
@@ -43,12 +44,12 @@ The `parse` method should simply return the [AST](#ast-specification) object.
The `parseForESLint` method should return an object that contains the required property `ast` and optional properties `services`, `scopeManager`, and `visitorKeys`.
-* `ast` should contain the [AST](#ast-specification) object.
-* `services` can contain any parser-dependent services (such as type checkers for nodes). The value of the `services` property is available to rules as `context.sourceCode.parserServices`. Default is an empty object.
-* `scopeManager` can be a [ScopeManager](./scope-manager-interface) object. Custom parsers can use customized scope analysis for experimental/enhancement syntaxes. The default is the `ScopeManager` object which is created by [eslint-scope](https://github.com/eslint/eslint-scope).
- * Support for `scopeManager` was added in ESLint v4.14.0. ESLint versions that support `scopeManager` will provide an `eslintScopeManager: true` property in `parserOptions`, which can be used for feature detection.
-* `visitorKeys` can be an object to customize AST traversal. The keys of the object are the type of AST nodes. Each value is an array of the property names which should be traversed. The default is [KEYS of `eslint-visitor-keys`](https://github.com/eslint/eslint-visitor-keys#evkkeys).
- * Support for `visitorKeys` was added in ESLint v4.14.0. ESLint versions that support `visitorKeys` will provide an `eslintVisitorKeys: true` property in `parserOptions`, which can be used for feature detection.
+- `ast` should contain the [AST](#ast-specification) object.
+- `services` can contain any parser-dependent services (such as type checkers for nodes). The value of the `services` property is available to rules as `context.sourceCode.parserServices`. Default is an empty object.
+- `scopeManager` can be a [ScopeManager](./scope-manager-interface) object. Custom parsers can use customized scope analysis for experimental/enhancement syntaxes. The default is the `ScopeManager` object which is created by [eslint-scope](https://github.com/eslint/js/tree/main/packages/eslint-scope).
+ - Support for `scopeManager` was added in ESLint v4.14.0. ESLint versions that support `scopeManager` will provide an `eslintScopeManager: true` property in `parserOptions`, which can be used for feature detection.
+- `visitorKeys` can be an object to customize AST traversal. The keys of the object are the type of AST nodes. Each value is an array of the property names which should be traversed. The default is [KEYS of `eslint-visitor-keys`](https://github.com/eslint/js/tree/main/packages/eslint-visitor-keys#evkkeys).
+ - Support for `visitorKeys` was added in ESLint v4.14.0. ESLint versions that support `visitorKeys` will provide an `eslintVisitorKeys: true` property in `parserOptions`, which can be used for feature detection.
### Meta Data in Custom Parsers
@@ -57,10 +58,10 @@ For easier debugging and more effective caching of custom parsers, it's recommen
```js
// preferred location of name and version
module.exports = {
- meta: {
- name: "eslint-parser-custom",
- version: "1.2.3"
- }
+ meta: {
+ name: "eslint-parser-custom",
+ version: "1.2.3",
+ },
};
```
@@ -74,8 +75,8 @@ The AST that custom parsers should create is based on [ESTree](https://github.co
All nodes must have `range` property.
-* `range` (`number[]`) is an array of two numbers. Both numbers are a 0-based index which is the position in the array of source code characters. The first is the start position of the node, the second is the end position of the node. `code.slice(node.range[0], node.range[1])` must be the text of the node. This range does not include spaces/parentheses which are around the node.
-* `loc` (`SourceLocation`) must not be `null`. [The `loc` property is defined as nullable by ESTree](https://github.com/estree/estree/blob/25834f7247d44d3156030f8e8a2d07644d771fdb/es5.md#node-objects), but ESLint requires this property. The `SourceLocation#source` property can be `undefined`. ESLint does not use the `SourceLocation#source` property.
+- `range` (`number[]`) is an array of two numbers. Both numbers are a 0-based index which is the position in the array of source code characters. The first is the start position of the node, the second is the end position of the node. `code.slice(node.range[0], node.range[1])` must be the text of the node. This range does not include spaces/parentheses which are around the node.
+- `loc` (`SourceLocation`) must not be `null`. [The `loc` property is defined as nullable by ESTree](https://github.com/estree/estree/blob/25834f7247d44d3156030f8e8a2d07644d771fdb/es5.md#node-objects), but ESLint requires this property. The `SourceLocation#source` property can be `undefined`. ESLint does not use the `SourceLocation#source` property.
The `parent` property of all nodes must be rewritable. Before any rules have access to the AST, ESLint sets each node's `parent` property to its parent node while traversing.
@@ -85,16 +86,16 @@ The `Program` node must have `tokens` and `comments` properties. Both properties
```ts
interface Token {
- type: string;
- loc: SourceLocation;
- // See the "All Nodes" section for details of the `range` property.
- range: [number, number];
- value: string;
+ type: string;
+ loc: SourceLocation;
+ // See the "All Nodes" section for details of the `range` property.
+ range: [number, number];
+ value: string;
}
```
-* `tokens` (`Token[]`) is the array of tokens which affect the behavior of programs. Arbitrary spaces can exist between tokens, so rules check the `Token#range` to detect spaces between tokens. This must be sorted by `Token#range[0]`.
-* `comments` (`Token[]`) is the array of comment tokens. This must be sorted by `Token#range[0]`.
+- `tokens` (`Token[]`) is the array of tokens which affect the behavior of programs. Arbitrary spaces can exist between tokens, so rules check the `Token#range` to detect spaces between tokens. This must be sorted by `Token#range[0]`.
+- `comments` (`Token[]`) is the array of comment tokens. This must be sorted by `Token#range[0]`.
The range indexes of all tokens and comments must not overlap with the range of other tokens and comments.
@@ -102,7 +103,7 @@ The range indexes of all tokens and comments must not overlap with the range of
The `Literal` node must have `raw` property.
-* `raw` (`string`) is the source code of this literal. This is the same as `code.slice(node.range[0], node.range[1])`.
+- `raw` (`string`) is the source code of this literal. This is the same as `code.slice(node.range[0], node.range[1])`.
## Packaging a Custom Parser
@@ -117,18 +118,37 @@ For more information on publishing an npm package, refer to the [npm documentati
Once you've published the npm package, you can use it by adding the package to your project. For example:
-```shell
-npm install eslint-parser-myparser --save-dev
+{{ npm_tabs({
+ command: "install",
+ packages: ["eslint-parser-myparser"],
+ args: ["--save-dev"]
+}) }}
+
+Then add the custom parser to your ESLint configuration file with the `languageOptions.parser` property. For example:
+
+```js
+// eslint.config.js
+
+const myparser = require("eslint-parser-myparser");
+
+module.exports = [
+ {
+ languageOptions: {
+ parser: myparser,
+ },
+ // ... rest of configuration
+ },
+];
```
-Then add the custom parser to your ESLint configuration file with the `parser` property. For example:
+When using legacy configuration, specify the `parser` property as a string:
```js
// .eslintrc.js
module.exports = {
- parser: 'eslint-parser-myparser',
- // ... rest of configuration
+ parser: "eslint-parser-myparser",
+ // ... rest of configuration
};
```
@@ -144,23 +164,36 @@ A simple custom parser that provides a `context.sourceCode.parserServices.foo()`
// awesome-custom-parser.js
var espree = require("espree");
function parseForESLint(code, options) {
- return {
- ast: espree.parse(code, options),
- services: {
- foo: function() {
- console.log("foo");
- }
- },
- scopeManager: null,
- visitorKeys: null
- };
-};
+ return {
+ ast: espree.parse(code, options),
+ services: {
+ foo: function () {
+ console.log("foo");
+ },
+ },
+ scopeManager: null,
+ visitorKeys: null,
+ };
+}
module.exports = { parseForESLint };
```
Include the custom parser in an ESLint configuration file:
+```js
+// eslint.config.js
+module.exports = [
+ {
+ languageOptions: {
+ parser: require("./path/to/awesome-custom-parser"),
+ },
+ },
+];
+```
+
+Or if using legacy configuration:
+
```js
// .eslintrc.json
{
diff --git a/docs/src/extend/custom-processors-deprecated.md b/docs/src/extend/custom-processors-deprecated.md
new file mode 100644
index 000000000000..1de004e55bd8
--- /dev/null
+++ b/docs/src/extend/custom-processors-deprecated.md
@@ -0,0 +1,188 @@
+---
+title: Custom Processors (Deprecated)
+---
+
+::: warning
+This documentation is for custom processors using the deprecated eslintrc configuration format. [View the updated documentation](custom-processors).
+:::
+
+You can also create custom processors that tell ESLint how to process files other than standard JavaScript. For example, you could write a custom processor to extract and process JavaScript from Markdown files ([@eslint/markdown](https://www.npmjs.com/package/@eslint/markdown) includes a custom processor for this).
+
+## Custom Processor Specification
+
+In order to create a custom processor, the object exported from your module has to conform to the following interface:
+
+```js
+module.exports = {
+ processors: {
+ "processor-name": {
+ meta: {
+ name: "eslint-processor-name",
+ version: "1.2.3",
+ },
+ // takes text of the file and filename
+ preprocess: function (text, filename) {
+ // here, you can strip out any non-JS content
+ // and split into multiple strings to lint
+
+ return [
+ // return an array of code blocks to lint
+ { text: code1, filename: "0.js" },
+ { text: code2, filename: "1.js" },
+ ];
+ },
+
+ // takes a Message[][] and filename
+ postprocess: function (messages, filename) {
+ // `messages` argument contains two-dimensional array of Message objects
+ // where each top-level array item contains array of lint messages related
+ // to the text that was returned in array from preprocess() method
+
+ // you need to return a one-dimensional array of the messages you want to keep
+ return [].concat(...messages);
+ },
+
+ supportsAutofix: true, // (optional, defaults to false)
+ },
+ },
+};
+```
+
+**The `preprocess` method** takes the file contents and filename as arguments, and returns an array of code blocks to lint. The code blocks will be linted separately but still be registered to the filename.
+
+A code block has two properties `text` and `filename`. The `text` property is the content of the block and the `filename` property is the name of the block. The name of the block can be anything, but should include the file extension, which tells the linter how to process the current block. The linter checks the [`--ext` CLI option](../use/command-line-interface#--ext) to see if the current block should be linted and resolves `overrides` configs to check how to process the current block.
+
+It's up to the plugin to decide if it needs to return just one part of the non-JavaScript file or multiple pieces. For example in the case of processing `.html` files, you might want to return just one item in the array by combining all scripts. However, for `.md` files, you can return multiple items because each JavaScript block might be independent.
+
+**The `postprocess` method** takes a two-dimensional array of arrays of lint messages and the filename. Each item in the input array corresponds to the part that was returned from the `preprocess` method. The `postprocess` method must adjust the locations of all errors to correspond to locations in the original, unprocessed code, and aggregate them into a single flat array and return it.
+
+Reported problems have the following location information in each lint message:
+
+```typescript
+type LintMessage = {
+ /// The 1-based line number where the message occurs.
+ line?: number;
+
+ /// The 1-based column number where the message occurs.
+ column?: number;
+
+ /// The 1-based line number of the end location.
+ endLine?: number;
+
+ /// The 1-based column number of the end location.
+ endColumn?: number;
+
+ /// If `true`, this is a fatal error.
+ fatal?: boolean;
+
+ /// Information for an autofix.
+ fix: Fix;
+
+ /// The error message.
+ message: string;
+
+ /// The ID of the rule which generated the message, or `null` if not applicable.
+ ruleId: string | null;
+
+ /// The severity of the message.
+ severity: 0 | 1 | 2;
+
+ /// Information for suggestions.
+ suggestions?: Suggestion[];
+};
+
+type Fix = {
+ range: [number, number];
+ text: string;
+};
+
+type Suggestion = {
+ desc?: string;
+ messageId?: string;
+ fix: Fix;
+};
+```
+
+By default, ESLint does not perform autofixes when a custom processor is used, even when the `--fix` flag is enabled on the command line. To allow ESLint to autofix code when using your processor, you should take the following additional steps:
+
+1. Update the `postprocess` method to additionally transform the `fix` property of reported problems. All autofixable problems have a `fix` property, which is an object with the following schema:
+
+ ```typescript
+ {
+ range: [number, number],
+ text: string
+ }
+ ```
+
+ The `range` property contains two indexes in the code, referring to the start and end location of a contiguous section of text that will be replaced. The `text` property refers to the text that will replace the given range.
+
+ In the initial list of problems, the `fix` property will refer to a fix in the processed JavaScript. The `postprocess` method should transform the object to refer to a fix in the original, unprocessed file.
+
+2. Add a `supportsAutofix: true` property to the processor.
+
+You can have both rules and custom processors in a single plugin. You can also have multiple processors in one plugin. To support multiple extensions, add each one to the `processors` element and point them to the same object.
+
+**The `meta` object** helps ESLint cache the processor and provide more friendly debug message. The `meta.name` property should match the processor name and the `meta.version` property should match the npm package version for your processors. The easiest way to accomplish this is by reading this information from your `package.json`.
+
+## Specifying Processor in Config Files
+
+To use a processor, add its ID to a `processor` section in the config file. Processor ID is a concatenated string of plugin name and processor name with a slash as a separator. This can also be added to a `overrides` section of the config, to specify which processors should handle which files.
+
+For example:
+
+```yml
+plugins:
+ - a-plugin
+overrides:
+ - files: "*.md"
+ processor: a-plugin/markdown
+```
+
+See [Specify a Processor](../use/configure/plugins#specify-a-processor) in the Plugin Configuration documentation for more details.
+
+## File Extension-named Processor
+
+::: warning
+This feature is deprecated and only works in eslintrc-style configuration files. Flat config files do not automatically apply processors; you need to explicitly set the `processor` property.
+:::
+
+If a custom processor name starts with `.`, ESLint handles the processor as a **file extension-named processor**. ESLint applies the processor to files with that filename extension automatically. Users don't need to specify the file extension-named processors in their config files.
+
+For example:
+
+```js
+module.exports = {
+ processors: {
+ // This processor will be applied to `*.md` files automatically.
+ // Also, you can use this processor as "plugin-id/.md" explicitly.
+ ".md": {
+ preprocess(text, filename) { /* ... */ },
+ postprocess(messageLists, filename) { /* ... */ }
+ }
+ // This processor will not be applied to any files automatically.
+ // To use this processor, you must explicitly specify it
+ // in your configuration as "plugin-id/markdown".
+ "markdown": {
+ preprocess(text, filename) { /* ... */ },
+ postprocess(messageLists, filename) { /* ... */ }
+ }
+ }
+}
+```
+
+You can also use the same custom processor with multiple filename extensions. The following example shows using the same processor for both `.md` and `.mdx` files:
+
+```js
+const myCustomProcessor = {
+ /* processor methods */
+};
+
+module.exports = {
+ // The same custom processor is applied to both
+ // `.md` and `.mdx` files.
+ processors: {
+ ".md": myCustomProcessor,
+ ".mdx": myCustomProcessor,
+ },
+};
+```
diff --git a/docs/src/extend/custom-processors.md b/docs/src/extend/custom-processors.md
index d5a5261d9769..3c65dcfe9ea1 100644
--- a/docs/src/extend/custom-processors.md
+++ b/docs/src/extend/custom-processors.md
@@ -5,53 +5,67 @@ eleventyNavigation:
parent: create plugins
title: Custom Processors
order: 3
-
---
-You can also create custom processors that tell ESLint how to process files other than standard JavaScript. For example, you could write a custom processor to extract and process JavaScript from Markdown files ([eslint-plugin-markdown](https://www.npmjs.com/package/eslint-plugin-markdown) includes a custom processor for this).
+You can also create custom processors that tell ESLint how to process files other than standard JavaScript. For example, you could write a custom processor to extract and process JavaScript from Markdown files ([@eslint/markdown](https://www.npmjs.com/package/@eslint/markdown) includes a custom processor for this).
+
+::: tip
+This page explains how to create a custom processor for use with the flat config format. For the deprecated eslintrc format, [see the deprecated documentation](custom-processors-deprecated).
+:::
## Custom Processor Specification
In order to create a custom processor, the object exported from your module has to conform to the following interface:
```js
-module.exports = {
- processors: {
- "processor-name": {
- meta: {
- name: "eslint-processor-name",
- version: "1.2.3"
- },
- // takes text of the file and filename
- preprocess: function(text, filename) {
- // here, you can strip out any non-JS content
- // and split into multiple strings to lint
-
- return [ // return an array of code blocks to lint
- { text: code1, filename: "0.js" },
- { text: code2, filename: "1.js" },
- ];
- },
-
- // takes a Message[][] and filename
- postprocess: function(messages, filename) {
- // `messages` argument contains two-dimensional array of Message objects
- // where each top-level array item contains array of lint messages related
- // to the text that was returned in array from preprocess() method
-
- // you need to return a one-dimensional array of the messages you want to keep
- return [].concat(...messages);
- },
-
- supportsAutofix: true // (optional, defaults to false)
- }
- }
+const plugin = {
+ meta: {
+ name: "eslint-plugin-example",
+ version: "1.2.3",
+ },
+ processors: {
+ "processor-name": {
+ meta: {
+ name: "eslint-processor-name",
+ version: "1.2.3",
+ },
+ // takes text of the file and filename
+ preprocess(text, filename) {
+ // here, you can strip out any non-JS content
+ // and split into multiple strings to lint
+
+ return [
+ // return an array of code blocks to lint
+ { text: code1, filename: "0.js" },
+ { text: code2, filename: "1.js" },
+ ];
+ },
+
+ // takes a Message[][] and filename
+ postprocess(messages, filename) {
+ // `messages` argument contains two-dimensional array of Message objects
+ // where each top-level array item contains array of lint messages related
+ // to the text that was returned in array from preprocess() method
+
+ // you need to return a one-dimensional array of the messages you want to keep
+ return [].concat(...messages);
+ },
+
+ supportsAutofix: true, // (optional, defaults to false)
+ },
+ },
};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
```
**The `preprocess` method** takes the file contents and filename as arguments, and returns an array of code blocks to lint. The code blocks will be linted separately but still be registered to the filename.
-A code block has two properties `text` and `filename`. The `text` property is the content of the block and the `filename` property is the name of the block. The name of the block can be anything, but should include the file extension, which tells the linter how to process the current block. The linter checks the [`--ext` CLI option](../use/command-line-interface#--ext) to see if the current block should be linted and resolves `overrides` configs to check how to process the current block.
+A code block has two properties `text` and `filename`. The `text` property is the content of the block and the `filename` property is the name of the block. The name of the block can be anything, but should include the file extension, which tells ESLint how to process the current block. ESLint checks matching `files` entries in the project's config to determine if the code blocks should be linted.
It's up to the plugin to decide if it needs to return just one part of the non-JavaScript file or multiple pieces. For example in the case of processing `.html` files, you might want to return just one item in the array by combining all scripts. However, for `.md` files, you can return multiple items because each JavaScript block might be independent.
@@ -61,49 +75,47 @@ Reported problems have the following location information in each lint message:
```typescript
type LintMessage = {
+ /// The 1-based line number where the message occurs.
+ line?: number;
- /// The 1-based line number where the message occurs.
- line?: number;
-
- /// The 1-based column number where the message occurs.
- column?: number;
+ /// The 1-based column number where the message occurs.
+ column?: number;
- /// The 1-based line number of the end location.
- endLine?: number;
+ /// The 1-based line number of the end location.
+ endLine?: number;
- /// The 1-based column number of the end location.
- endColumn?: number;
+ /// The 1-based column number of the end location.
+ endColumn?: number;
- /// If `true`, this is a fatal error.
- fatal?: boolean;
+ /// If `true`, this is a fatal error.
+ fatal?: boolean;
- /// Information for an autofix.
- fix: Fix;
+ /// Information for an autofix.
+ fix: Fix;
- /// The error message.
- message: string;
+ /// The error message.
+ message: string;
- /// The ID of the rule which generated the message, or `null` if not applicable.
- ruleId: string | null;
+ /// The ID of the rule which generated the message, or `null` if not applicable.
+ ruleId: string | null;
- /// The severity of the message.
- severity: 0 | 1 | 2;
+ /// The severity of the message.
+ severity: 0 | 1 | 2;
- /// Information for suggestions.
- suggestions?: Suggestion[];
+ /// Information for suggestions.
+ suggestions?: Suggestion[];
};
type Fix = {
- range: [number, number];
- text: string;
-}
+ range: [number, number];
+ text: string;
+};
type Suggestion = {
- desc?: string;
- messageId?: string;
- fix: Fix;
-}
-
+ desc?: string;
+ messageId?: string;
+ fix: Fix;
+};
```
By default, ESLint does not perform autofixes when a custom processor is used, even when the `--fix` flag is enabled on the command line. To allow ESLint to autofix code when using your processor, you should take the following additional steps:
@@ -125,65 +137,79 @@ By default, ESLint does not perform autofixes when a custom processor is used, e
You can have both rules and custom processors in a single plugin. You can also have multiple processors in one plugin. To support multiple extensions, add each one to the `processors` element and point them to the same object.
-**The `meta` object** helps ESLint cache the processor and provide more friendly debug message. The `meta.name` property should match the processor name and the `meta.version` property should match the npm package version for your processors. The easiest way to accomplish this is by reading this information from your `package.json`.
+### How `meta` Objects are Used
-## Specifying Processor in Config Files
+The `meta` object helps ESLint cache configurations that use a processor and to provide more friendly debug messages.
-To use a processor, add its ID to a `processor` section in the config file. Processor ID is a concatenated string of plugin name and processor name with a slash as a separator. This can also be added to a `overrides` section of the config, to specify which processors should handle which files.
+#### Plugin `meta` Object
-For example:
+The [plugin `meta` object](plugins#meta-data-in-plugins) provides information about the plugin itself. When a processor is specified using the string format `plugin-name/processor-name`, ESLint automatically uses the plugin `meta` to generate a name for the processor. This is the most common case for processors.
-```yml
-plugins:
- - a-plugin
-overrides:
- - files: "*.md"
- processor: a-plugin/markdown
-```
+Example:
-See [Specify a Processor](../use/configure/plugins#specify-a-processor) in the Plugin Configuration documentation for more details.
+```js
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import example from "eslint-plugin-example";
+
+export default defineConfig([
+ {
+ files: ["**/*.txt"], // apply processor to text files
+ plugins: {
+ example,
+ },
+ processor: "example/processor-name",
+ },
+ // ... other configs
+]);
+```
-## File Extension-named Processor
+In this example, the processor name is `"example/processor-name"`, and that's the value that will be used for serializing configurations.
-::: warning
-This feature is deprecated and only works in eslintrc-style configuration files. Flat config files do not automatically apply processors; you need to explicitly set the `processor` property.
-:::
+#### Processor `meta` Object
-If a custom processor name starts with `.`, ESLint handles the processor as a **file extension-named processor**. ESLint applies the processor to files with that filename extension automatically. Users don't need to specify the file extension-named processors in their config files.
+Each processor can also specify its own `meta` object. This information is used when the processor object is passed directly to `processor` in a configuration. In that case, ESLint doesn't know which plugin the processor belongs to. The `meta.name` property should match the processor name and the `meta.version` property should match the npm package version for your processors. The easiest way to accomplish this is by reading this information from your `package.json`.
-For example:
+Example:
```js
-module.exports = {
- processors: {
- // This processor will be applied to `*.md` files automatically.
- // Also, you can use this processor as "plugin-id/.md" explicitly.
- ".md": {
- preprocess(text, filename) { /* ... */ },
- postprocess(messageLists, filename) { /* ... */ }
- }
- // This processor will not be applied to any files automatically.
- // To use this processor, you must explicitly specify it
- // in your configuration as "plugin-id/markdown".
- "markdown": {
- preprocess(text, filename) { /* ... */ },
- postprocess(messageLists, filename) { /* ... */ }
- }
- }
-}
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import example from "eslint-plugin-example";
+
+export default defineConfig([
+ {
+ files: ["**/*.txt"],
+ processor: example.processors["processor-name"],
+ },
+ // ... other configs
+]);
```
-You can also use the same custom processor with multiple filename extensions. The following example shows using the same processor for both `.md` and `.mdx` files:
+In this example, specifying `example.processors["processor-name"]` directly uses the processor's own `meta` object, which must be defined to ensure proper handling when the processor is not referenced through the plugin name.
+
+#### Why Both Meta Objects are Needed
+
+It is recommended that both the plugin and each processor provide their respective meta objects. This ensures that features relying on meta objects, such as `--print-config` and `--cache`, work correctly regardless of how the processor is specified in the configuration.
+
+## Specifying Processor in Config Files
+
+In order to use a processor from a plugin in a configuration file, import the plugin and include it in the `plugins` key, specifying a namespace. Then, use that namespace to reference the processor in the `processor` configuration, like this:
```js
-const myCustomProcessor = { /* processor methods */ };
-
-module.exports = {
- // The same custom processor is applied to both
- // `.md` and `.mdx` files.
- processors: {
- ".md": myCustomProcessor,
- ".mdx": myCustomProcessor
- }
-}
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import example from "eslint-plugin-example";
+
+export default defineConfig([
+ {
+ files: ["**/*.txt"],
+ plugins: {
+ example,
+ },
+ processor: "example/processor-name",
+ },
+]);
```
+
+See [Specify a Processor](../use/configure/plugins#specify-a-processor) in the Plugin Configuration documentation for more details.
diff --git a/docs/src/extend/custom-rule-tutorial.md b/docs/src/extend/custom-rule-tutorial.md
index 8426961acae5..0556cc3c0ef8 100644
--- a/docs/src/extend/custom-rule-tutorial.md
+++ b/docs/src/extend/custom-rule-tutorial.md
@@ -6,14 +6,18 @@ eleventyNavigation:
title: Custom Rule Tutorial
order: 1
---
+
+{%- from 'components/npm_tabs.macro.html' import npm_tabs with context %}
+{%- from 'components/npx_tabs.macro.html' import npx_tabs %}
+
This tutorial covers how to create a custom rule for ESLint and distribute it with a plugin.
You can create custom rules to validate if your code meets a certain expectation, and determine what to do if it does not meet that expectation. Plugins package custom rules and other configuration, allowing you to easily share and reuse them in different projects.
To learn more about custom rules and plugins refer to the following documentation:
-* [Custom Rules](custom-rules)
-* [Plugins](plugins)
+- [Custom Rules](custom-rules)
+- [Plugins](plugins)
## Why Create a Custom Rule?
@@ -25,8 +29,8 @@ Before creating a custom rule that isn't specific to your company or project, it
Before you begin, make sure you have the following installed in your development environment:
-* [Node.js](https://nodejs.org/en/download/)
-* [npm](https://www.npmjs.com/)
+- [Node.js](https://nodejs.org/en/download/)
+- [npm](https://www.npmjs.com/)
This tutorial also assumes that you have a basic understanding of ESLint and ESLint rules.
@@ -69,14 +73,14 @@ In the `enforce-foo-bar.js` file, add some scaffolding for the `enforce-foo-bar`
// enforce-foo-bar.js
module.exports = {
- meta: {
- // TODO: add metadata
- },
- create(context) {
- return {
- // TODO: add callback function(s)
- };
- }
+ meta: {
+ // TODO: add metadata
+ },
+ create(context) {
+ return {
+ // TODO: add callback function(s)
+ };
+ },
};
```
@@ -90,19 +94,20 @@ Start by exporting an object with a `meta` property containing the rule's metada
// enforce-foo-bar.js
module.exports = {
- meta: {
- type: "problem",
- docs: {
- description: "Enforce that a variable named `foo` can only be assigned a value of 'bar'.",
- },
- fixable: "code",
- schema: []
- },
- create(context) {
- return {
- // TODO: add callback function(s)
- };
- }
+ meta: {
+ type: "problem",
+ docs: {
+ description:
+ "Enforce that a variable named `foo` can only be assigned a value of 'bar'.",
+ },
+ fixable: "code",
+ schema: [],
+ },
+ create(context) {
+ return {
+ // TODO: add callback function(s)
+ };
+ },
};
```
@@ -113,6 +118,10 @@ To learn more about rule metadata, refer to [Rule Structure](custom-rules#rule-s
Define the rule's `create` function, which accepts a `context` object and returns an object with a property for each syntax node type you want to handle. In this case, you want to handle `VariableDeclarator` nodes.
You can choose any [ESTree node type](https://github.com/estree/estree) or [selector](selectors).
+::: tip
+You can view the AST for any JavaScript code using [Code Explorer](http://explorer.eslint.org). This is helpful in determining the type of nodes you'd like to target.
+:::
+
Inside the `VariableDeclarator` visitor method, check if the node represents a `const` variable declaration, if its name is `foo`, and if it's not assigned to the string `"bar"`. You do this by evaluating the `node` passed to the `VariableDeclaration` method.
If the `const foo` declaration is assigned a value of `"bar"`, then the rule does nothing. If `const foo` **is not** assigned a value of `"bar"`, then `context.report()` reports an error to ESLint. The error report includes information about the error and how to fix it.
@@ -185,9 +194,11 @@ touch enforce-foo-bar.test.js
You will use the `eslint` package in the test file. Install it as a development dependency:
-```shell
-npm install eslint --save-dev
-```
+{{ npm_tabs({
+ command: "install",
+ packages: ["eslint"],
+ args: ["--save-dev"]
+}) }}
And add a test script to your `package.json` file to run the tests:
@@ -211,31 +222,36 @@ The `RuleTester#run()` method tests the rule against valid and invalid test case
```javascript
// enforce-foo-bar.test.js
-const {RuleTester} = require("eslint");
+const { RuleTester } = require("eslint");
const fooBarRule = require("./enforce-foo-bar");
const ruleTester = new RuleTester({
- // Must use at least ecmaVersion 2015 because
- // that's when `const` variables were introduced.
- parserOptions: { ecmaVersion: 2015 }
+ // Must use at least ecmaVersion 2015 because
+ // that's when `const` variables were introduced.
+ languageOptions: { ecmaVersion: 2015 },
});
// Throws error if the tests in ruleTester.run() do not pass
ruleTester.run(
- "enforce-foo-bar", // rule name
- fooBarRule, // rule code
- { // checks
- // 'valid' checks cases that should pass
- valid: [{
- code: "const foo = 'bar';",
- }],
- // 'invalid' checks cases that should not pass
- invalid: [{
- code: "const foo = 'baz';",
- output: 'const foo = "bar";',
- errors: 1,
- }],
- }
+ "enforce-foo-bar", // rule name
+ fooBarRule, // rule code
+ {
+ // checks
+ // 'valid' checks cases that should pass
+ valid: [
+ {
+ code: "const foo = 'bar';",
+ },
+ ],
+ // 'invalid' checks cases that should not pass
+ invalid: [
+ {
+ code: "const foo = 'baz';",
+ output: 'const foo = "bar";',
+ errors: 1,
+ },
+ ],
+ },
);
console.log("All tests passed!");
@@ -281,10 +297,10 @@ You can use a locally defined plugin to execute the custom rule in your project.
You might want to use a locally defined plugin in one of the following scenarios:
-* You want to test the plugin before publishing it to npm.
-* You want to use a plugin, but do not want to publish it to npm.
+- You want to test the plugin before publishing it to npm.
+- You want to use a plugin, but do not want to publish it to npm.
-Before you can add the plugin to the project, create an ESLint configuration for your project using a [flat configuration file](../use/configure/configuration-files-new), `eslint.config.js`:
+Before you can add the plugin to the project, create an ESLint configuration for your project using a [flat configuration file](../use/configure/configuration-files), `eslint.config.js`:
```shell
touch eslint.config.js
@@ -300,19 +316,19 @@ Then, add the following code to `eslint.config.js`:
const eslintPluginExample = require("./eslint-plugin-example");
module.exports = [
- {
- files: ["**/*.js"],
- languageOptions: {
- sourceType: "commonjs",
- ecmaVersion: "latest",
- },
- // Using the eslint-plugin-example plugin defined locally
- plugins: {"example": eslintPluginExample},
- rules: {
- "example/enforce-foo-bar": "error",
- },
- }
-]
+ {
+ files: ["**/*.js"],
+ languageOptions: {
+ sourceType: "commonjs",
+ ecmaVersion: "latest",
+ },
+ // Using the eslint-plugin-example plugin defined locally
+ plugins: { example: eslintPluginExample },
+ rules: {
+ "example/enforce-foo-bar": "error",
+ },
+ },
+];
```
Before you can test the rule, you must create a file to test the rule on.
@@ -329,11 +345,11 @@ Add the following code to `example.js`:
// example.js
function correctFooBar() {
- const foo = "bar";
+ const foo = "bar";
}
-function incorrectFoo(){
- const foo = "baz"; // Problem!
+function incorrectFoo() {
+ const foo = "baz"; // Problem!
}
```
@@ -341,9 +357,10 @@ Now you're ready to test the custom rule with the locally defined plugin.
Run ESLint on `example.js`:
-```shell
-npx eslint example.js
-```
+{{ npx_tabs({
+ package: "eslint",
+ args: ["example.js"]
+}) }}
This produces the following output in the terminal:
@@ -362,7 +379,7 @@ To publish a plugin containing a rule to npm, you need to configure the `package
1. `"name"`: A unique name for the package. No other package on npm can have the same name.
1. `"main"`: The relative path to the plugin file. Following this example, the path is `"eslint-plugin-example.js"`.
1. `"description"`: A description of the package that's viewable on npm.
-1. `"peerDependencies"`: Add `"eslint": ">=8.0.0"` as a peer dependency. Any version greater than or equal to that is necessary to use the plugin. Declaring `eslint` as a peer dependency requires that users add the package to the project separately from the plugin.
+1. `"peerDependencies"`: Add `"eslint": ">=9.0.0"` as a peer dependency. Any version greater than or equal to that is necessary to use the plugin. Declaring `eslint` as a peer dependency requires that users add the package to the project separately from the plugin.
1. `"keywords"`: Include the standard keywords `["eslint", "eslintplugin", "eslint-plugin"]` to make the package easy to find. You can add any other keywords that might be relevant to your plugin as well.
A complete annotated example of what a plugin's `package.json` file should look like:
@@ -379,9 +396,9 @@ A complete annotated example of what a plugin's `package.json` file should look
"scripts": {
"test": "node enforce-foo-bar.test.js"
},
- // Add eslint>=8.0.0 as a peer dependency.
+ // Add eslint>=9.0.0 as a peer dependency.
"peerDependencies": {
- "eslint": ">=8.0.0"
+ "eslint": ">=9.0.0"
},
// Add these standard keywords to make plugin easy to find!
"keywords": [
@@ -392,7 +409,7 @@ A complete annotated example of what a plugin's `package.json` file should look
"author": "",
"license": "ISC",
"devDependencies": {
- "eslint": "^8.36.0"
+ "eslint": "^9.0.0"
}
}
```
@@ -407,9 +424,12 @@ Next, you can use the published plugin.
Run the following command in your project to download the package:
-```shell
-npm install --save-dev eslint-plugin-example # Add your package name here
-```
+{{ npm_tabs({
+ command: "install",
+ packages: ["eslint-plugin-example"],
+ args: ["--save-dev"],
+ comment: "Add your package name here"
+}) }}
Update the `eslint.config.js` to use the packaged version of the plugin:
@@ -427,9 +447,10 @@ Now you're ready to test the custom rule.
Run ESLint on the `example.js` file you created in step 8, now with the downloaded plugin:
-```shell
-npx eslint example.js
-```
+{{ npx_tabs({
+ package: "eslint",
+ args: ["example.js"]
+}) }}
This produces the following output in the terminal:
@@ -445,9 +466,10 @@ As you can see in the above message, you can actually fix the issue with the `--
Run ESLint again with the `--fix` flag:
-```shell
-npx eslint example.js --fix
-```
+{{ npx_tabs({
+ package: "eslint",
+ args: ["example.js", "--fix"]
+}) }}
There is no error output in the terminal when you run this, but you can see the fix applied in `example.js`. You should see the following:
@@ -456,8 +478,8 @@ There is no error output in the terminal when you run this, but you can see the
// ... rest of file
-function incorrectFoo(){
- const foo = "bar"; // Fixed!
+function incorrectFoo() {
+ const foo = "bar"; // Fixed!
}
```
diff --git a/docs/src/extend/custom-rules-deprecated.md b/docs/src/extend/custom-rules-deprecated.md
index 49faa4f13c7e..33770091b86b 100644
--- a/docs/src/extend/custom-rules-deprecated.md
+++ b/docs/src/extend/custom-rules-deprecated.md
@@ -1,580 +1,7 @@
---
title: Working with Rules (Deprecated)
-
---
-**Note:** This page covers the deprecated rule format for ESLint <= 2.13.1. [This is the most recent rule format](./custom-rules).
-
-Each rule in ESLint has two files named with its identifier (for example, `no-extra-semi`).
-
-* in the `lib/rules` directory: a source file (for example, `no-extra-semi.js`)
-* in the `tests/lib/rules` directory: a test file (for example, `no-extra-semi.js`)
-
-**Important:** If you submit a **core** rule to the ESLint repository, you **must** follow some conventions explained below.
-
-Here is the basic format of the source file for a rule:
-
-```js
-/**
- * @fileoverview Rule to disallow unnecessary semicolons
- * @author Nicholas C. Zakas
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Rule Definition
-//------------------------------------------------------------------------------
-
-module.exports = function(context) {
- return {
- // callback functions
- };
-};
-
-module.exports.schema = []; // no options
-```
-
-## Rule Basics
-
-`schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../use/configure/rules)
-
-`create` (function) returns an object with methods that ESLint calls to "visit" nodes while traversing the abstract syntax tree (AST as defined by [ESTree](https://github.com/estree/estree)) of JavaScript code:
-
-* if a key is a node type, ESLint calls that **visitor** function while going **down** the tree
-* if a key is a node type plus `:exit`, ESLint calls that **visitor** function while going **up** the tree
-* if a key is an event name, ESLint calls that **handler** function for [code path analysis](code-path-analysis)
-
-A rule can use the current node and its surrounding tree to report or fix problems.
-
-Here are methods for the [array-callback-return](../rules/array-callback-return) rule:
-
-```js
-function checkLastSegment (node) {
- // report problem for function if last code path segment is reachable
-}
-
-module.exports = function(context) {
- // declare the state of the rule
- return {
- ReturnStatement: function(node) {
- // at a ReturnStatement node while going down
- },
- // at a function expression node while going up:
- "FunctionExpression:exit": checkLastSegment,
- "ArrowFunctionExpression:exit": checkLastSegment,
- onCodePathStart: function (codePath, node) {
- // at the start of analyzing a code path
- },
- onCodePathEnd: function(codePath, node) {
- // at the end of analyzing a code path
- }
- };
-};
-```
-
-## The Context Object
-
-The `context` object contains additional functionality that is helpful for rules to do their jobs. As the name implies, the `context` object contains information that is relevant to the context of the rule. The `context` object has the following properties:
-
-* `parserOptions` - the parser options configured for this run (more details [here](../use/configure/language-options#specifying-parser-options)).
-* `id` - the rule ID.
-* `options` - an array of rule options.
-* `settings` - the `settings` from configuration.
-* `parserPath` - the full path to the `parser` from configuration.
-
-Additionally, the `context` object has the following methods:
-
-* `getAncestors()` - returns an array of ancestor nodes based on the current traversal.
-* `getDeclaredVariables(node)` - returns the declared variables on the given node.
-* `getFilename()` - returns the filename associated with the source.
-* `getScope()` - returns the current scope.
-* `getSourceCode()` - returns a `SourceCode` object that you can use to work with the source that was passed to ESLint
-* `markVariableAsUsed(name)` - marks the named variable in scope as used. This affects the [no-unused-vars](../rules/no-unused-vars) rule.
-* `report(descriptor)` - reports a problem in the code.
-
-**Deprecated:** The following methods on the `context` object are deprecated. Please use the corresponding methods on `SourceCode` instead:
-
-* `getAllComments()` - returns an array of all comments in the source. Use `sourceCode.getAllComments()` instead.
-* `getComments(node)` - returns the leading and trailing comments arrays for the given node. Use `sourceCode.getComments(node)` instead.
-* `getFirstToken(node)` - returns the first token representing the given node. Use `sourceCode.getFirstToken(node)` instead.
-* `getFirstTokens(node, count)` - returns the first `count` tokens representing the given node. Use `sourceCode.getFirstTokens(node, count)` instead.
-* `getJSDocComment(node)` - returns the JSDoc comment for a given node or `null` if there is none. Use `sourceCode.getJSDocComment(node)` instead.
-* `getLastToken(node)` - returns the last token representing the given node. Use `sourceCode.getLastToken(node)` instead.
-* `getLastTokens(node, count)` - returns the last `count` tokens representing the given node. Use `sourceCode.getLastTokens(node, count)` instead.
-* `getNodeByRangeIndex(index)` - returns the deepest node in the AST containing the given source index. Use `sourceCode.getNodeByRangeIndex(index)` instead.
-* `getSource(node)` - returns the source code for the given node. Omit `node` to get the whole source. Use `sourceCode.getText(node)` instead.
-* `getSourceLines()` - returns the entire source code split into an array of string lines. Use `sourceCode.lines` instead.
-* `getTokenAfter(nodeOrToken)` - returns the first token after the given node or token. Use `sourceCode.getTokenAfter(nodeOrToken)` instead.
-* `getTokenBefore(nodeOrToken)` - returns the first token before the given node or token. Use `sourceCode.getTokenBefore(nodeOrToken)` instead.
-* `getTokenByRangeStart(index)` - returns the token whose range starts at the given index in the source. Use `sourceCode.getTokenByRangeStart(index)` instead.
-* `getTokens(node)` - returns all tokens for the given node. Use `sourceCode.getTokens(node)` instead.
-* `getTokensAfter(nodeOrToken, count)` - returns `count` tokens after the given node or token. Use `sourceCode.getTokensAfter(nodeOrToken, count)` instead.
-* `getTokensBefore(nodeOrToken, count)` - returns `count` tokens before the given node or token. Use `sourceCode.getTokensBefore(nodeOrToken, count)` instead.
-* `getTokensBetween(node1, node2)` - returns the tokens between two nodes. Use `sourceCode.getTokensBetween(node1, node2)` instead.
-* `report(node, [location], message)` - reports a problem in the code.
-
-### context.report()
-
-The main method you'll use is `context.report()`, which publishes a warning or error (depending on the configuration being used). This method accepts a single argument, which is an object containing the following properties:
-
-* `message` - the problem message.
-* `node` - (optional) the AST node related to the problem. If present and `loc` is not specified, then the starting location of the node is used as the location of the problem.
-* `loc` - (optional) an object specifying the location of the problem. If both `loc` and `node` are specified, then the location is used from `loc` instead of `node`.
- * `line` - the 1-based line number at which the problem occurred.
- * `column` - the 0-based column number at which the problem occurred.
-* `data` - (optional) placeholder data for `message`.
-* `fix` - (optional) a function that applies a fix to resolve the problem.
-
-Note that at least one of `node` or `loc` is required.
-
-The simplest example is to use just `node` and `message`:
-
-```js
-context.report({
- node: node,
- message: "Unexpected identifier"
-});
-```
-
-The node contains all of the information necessary to figure out the line and column number of the offending text as well the source text representing the node.
-
-You can also use placeholders in the message and provide `data`:
-
-```js
-{% raw %}
-context.report({
- node: node,
- message: "Unexpected identifier: {{ identifier }}",
- data: {
- identifier: node.name
- }
-});
-{% endraw %}
-```
-
-Note that leading and trailing whitespace is optional in message parameters.
-
-The node contains all of the information necessary to figure out the line and column number of the offending text as well the source text representing the node.
-
-### Applying Fixes
-
-If you'd like ESLint to attempt to fix the problem you're reporting, you can do so by specifying the `fix` function when using `context.report()`. The `fix` function receives a single argument, a `fixer` object, that you can use to apply a fix. For example:
-
-```js
-context.report({
- node: node,
- message: "Missing semicolon".
- fix: function(fixer) {
- return fixer.insertTextAfter(node, ";");
- }
-});
-```
-
-Here, the `fix()` function is used to insert a semicolon after the node. Note that the fix is not immediately applied and may not be applied at all if there are conflicts with other fixes. If the fix cannot be applied, then the problem message is reported as usual; if the fix can be applied, then the problem message is not reported.
-
-The `fixer` object has the following methods:
-
-* `insertTextAfter(nodeOrToken, text)` - inserts text after the given node or token
-* `insertTextAfterRange(range, text)` - inserts text after the given range
-* `insertTextBefore(nodeOrToken, text)` - inserts text before the given node or token
-* `insertTextBeforeRange(range, text)` - inserts text before the given range
-* `remove(nodeOrToken)` - removes the given node or token
-* `removeRange(range)` - removes text in the given range
-* `replaceText(nodeOrToken, text)` - replaces the text in the given node or token
-* `replaceTextRange(range, text)` - replaces the text in the given range
-
-Best practices for fixes:
-
-1. Make fixes that are as small as possible. Anything more than a single character is risky and could prevent other, simpler fixes from being made.
-1. Only make one fix per message. This is enforced because you must return the result of the fixer operation from `fix()`.
-1. Fixes should not introduce clashes with other rules. You can accidentally introduce a new problem that won't be reported until ESLint is run again. Another good reason to make as small a fix as possible.
-
-### context.options
-
-Some rules require options in order to function correctly. These options appear in configuration (`.eslintrc`, command line, or in comments). For example:
-
-```json
-{
- "quotes": [2, "double"]
-}
-```
-
-The `quotes` rule in this example has one option, `"double"` (the `2` is the error level). You can retrieve the options for a rule by using `context.options`, which is an array containing every configured option for the rule. In this case, `context.options[0]` would contain `"double"`:
-
-```js
-module.exports = function(context) {
-
- var isDouble = (context.options[0] === "double");
-
- // ...
-}
-```
-
-Since `context.options` is just an array, you can use it to determine how many options have been passed as well as retrieving the actual options themselves. Keep in mind that the error level is not part of `context.options`, as the error level cannot be known or modified from inside a rule.
-
-When using options, make sure that your rule has some logic defaults in case the options are not provided.
-
-### context.getSourceCode()
-
-The `SourceCode` object is the main object for getting more information about the source code being linted. You can retrieve the `SourceCode` object at any time by using the `getSourceCode()` method:
-
-```js
-module.exports = function(context) {
-
- var sourceCode = context.getSourceCode();
-
- // ...
-}
-```
-
-Once you have an instance of `SourceCode`, you can use the methods on it to work with the code:
-
-* `getAllComments()` - returns an array of all comments in the source.
-* `getComments(node)` - returns the leading and trailing comments arrays for the given node.
-* `getFirstToken(node)` - returns the first token representing the given node.
-* `getFirstTokens(node, count)` - returns the first `count` tokens representing the given node.
-* `getJSDocComment(node)` - returns the JSDoc comment for a given node or `null` if there is none.
-* `getLastToken(node)` - returns the last token representing the given node.
-* `getLastTokens(node, count)` - returns the last `count` tokens representing the given node.
-* `getNodeByRangeIndex(index)` - returns the deepest node in the AST containing the given source index.
-* `isSpaceBetweenTokens(first, second)` - returns true if there is a whitespace character between the two tokens.
-* `getText(node)` - returns the source code for the given node. Omit `node` to get the whole source.
-* `getTokenAfter(nodeOrToken)` - returns the first token after the given node or token.
-* `getTokenBefore(nodeOrToken)` - returns the first token before the given node or token.
-* `getTokenByRangeStart(index)` - returns the token whose range starts at the given index in the source.
-* `getTokens(node)` - returns all tokens for the given node.
-* `getTokensAfter(nodeOrToken, count)` - returns `count` tokens after the given node or token.
-* `getTokensBefore(nodeOrToken, count)` - returns `count` tokens before the given node or token.
-* `getTokensBetween(node1, node2)` - returns the tokens between two nodes.
-
-There are also some properties you can access:
-
-* `hasBOM` - the flag to indicate whether or not the source code has Unicode BOM.
-* `text` - the full text of the code being linted. Unicode BOM has been stripped from this text.
-* `ast` - the `Program` node of the AST for the code being linted.
-* `lines` - an array of lines, split according to the specification's definition of line breaks.
-
-You should use a `SourceCode` object whenever you need to get more information about the code being linted.
-
-### Options Schemas
-
-Rules may export a `schema` property, which is a [JSON schema](http://json-schema.org/) format description of a rule's options which will be used by ESLint to validate configuration options and prevent invalid or unexpected inputs before they are passed to the rule in `context.options`.
-
-There are two formats for a rule's exported `schema`. The first is a full JSON Schema object describing all possible options the rule accepts, including the rule's error level as the first argument and any optional arguments thereafter.
-
-However, to simplify schema creation, rules may also export an array of schemas for each optional positional argument, and ESLint will automatically validate the required error level first. For example, the `yoda` rule accepts a primary mode argument, as well as an extra options object with named properties.
-
-```js
-// "yoda": [2, "never", { "exceptRange": true }]
-module.exports.schema = [
- {
- "enum": ["always", "never"]
- },
- {
- "type": "object",
- "properties": {
- "exceptRange": {
- "type": "boolean"
- }
- },
- "additionalProperties": false
- }
-];
-```
-
-In the preceding example, the error level is assumed to be the first argument. It is followed by the first optional argument, a string which may be either `"always"` or `"never"`. The final optional argument is an object, which may have a Boolean property named `exceptRange`.
-
-To learn more about JSON Schema, we recommend looking at some [examples](http://json-schema.org/examples.html) to start, and also reading [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/) (a free ebook).
-
-### Getting the Source
-
-If your rule needs to get the actual JavaScript source to work with, then use the `sourceCode.getText()` method. This method works as follows:
-
-```js
-
-// get all source
-var source = sourceCode.getText();
-
-// get source for just this AST node
-var nodeSource = sourceCode.getText(node);
-
-// get source for AST node plus previous two characters
-var nodeSourceWithPrev = sourceCode.getText(node, 2);
-
-// get source for AST node plus following two characters
-var nodeSourceWithFollowing = sourceCode.getText(node, 0, 2);
-```
-
-In this way, you can look for patterns in the JavaScript text itself when the AST isn't providing the appropriate data (such as location of commas, semicolons, parentheses, etc.).
-
-### Accessing comments
-
-If you need to access comments for a specific node you can use `sourceCode.getComments(node)`:
-
-```js
-// the "comments" variable has a "leading" and "trailing" property containing
-// its leading and trailing comments, respectively
-var comments = sourceCode.getComments(node);
-```
-
-Keep in mind that comments are technically not a part of the AST and are only attached to it on demand, i.e. when you call `getComments()`.
-
-**Note:** One of the libraries adds AST node properties for comments - do not use these properties. Always use `sourceCode.getComments()` as this is the only guaranteed API for accessing comments (we will likely change how comments are handled later).
-
-### Accessing Code Paths
-
-ESLint analyzes code paths while traversing AST.
-You can access that code path objects with five events related to code paths.
-
-[details here](code-path-analysis)
-
-## Rule Unit Tests
-
-Each rule must have a set of unit tests submitted with it to be accepted. The test file is named the same as the source file but lives in `tests/lib/`. For example, if your rule source file is `lib/rules/foo.js` then your test file should be `tests/lib/rules/foo.js`.
-
-For your rule, be sure to test:
-
-1. All instances that should be flagged as warnings.
-1. At least one pattern that should **not** be flagged as a warning.
-
-The basic pattern for a rule unit test file is:
-
-```js
-/**
- * @fileoverview Tests for no-with rule.
- * @author Nicholas C. Zakas
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-var rule = require("../../../lib/rules/no-with"),
- RuleTester = require("../../../lib/testers/rule-tester");
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-var ruleTester = new RuleTester();
-ruleTester.run("no-with", rule, {
- valid: [
- "foo.bar()"
- ],
- invalid: [
- {
- code: "with(foo) { bar() }",
- errors: [{ message: "Unexpected use of 'with' statement.", type: "WithStatement"}]
- }
- ]
-});
-```
-
-Be sure to replace the value of `"no-with"` with your rule's ID. There are plenty of examples in the `tests/lib/rules/` directory.
-
-### Valid Code
-
-Each valid case can be either a string or an object. The object form is used when you need to specify additional global variables or arguments for the rule. For example, the following defines `window` as a global variable for code that should not trigger the rule being tested:
-
-```js
-valid: [
- {
- code: "window.alert()",
- globals: [ "window" ]
- }
-]
-```
-
-You can also pass options to the rule (if it accepts them). These arguments are equivalent to how people can configure rules in their `.eslintrc` file. For example:
-
-```js
-valid: [
- {
- code: "var msg = 'Hello';",
- options: [ "single" ]
- }
-]
-```
-
-The `options` property must be an array of options. This gets passed through to `context.options` in the rule.
-
-### Invalid Code
-
-Each invalid case must be an object containing the code to test and at least one message that is produced by the rule. The `errors` key specifies an array of objects, each containing a message (your rule may trigger multiple messages for the same code). You should also specify the type of AST node you expect to receive back using the `type` key. The AST node should represent the actual spot in the code where there is a problem. For example:
-
-```js
-invalid: [
- {
- code: "function doSomething() { var f; if (true) { var build = true; } f = build; }",
- errors: [
- { message: "build used outside of binding context.", type: "Identifier" }
- ]
- }
-]
-```
-
-In this case, the message is specific to the variable being used and the AST node type is `Identifier`.
-
-Similar to the valid cases, you can also specify `options` to be passed to the rule:
-
-```js
-invalid: [
- {
- code: "function doSomething() { var f; if (true) { var build = true; } f = build; }",
- options: [ "double" ],
- errors: [
- { message: "build used outside of binding context.", type: "Identifier" }
- ]
- }
-]
-```
-
-For simpler cases where the only thing that really matters is the error message, you can also specify any `errors` as strings. You can also have some strings and some objects, if you like.
-
-```js
-invalid: [
- {
- code: "'single quotes'",
- options: ["double"],
- errors: ["Strings must use doublequote."]
- }
-]
-```
-
-### Specifying Parser Options
-
-Some tests require that a certain parser configuration must be used. This can be specified in test specifications via the `parserOptions` setting.
-
-For example, to set `ecmaVersion` to 6 (in order to use constructs like `for ... of`):
-
-```js
-valid: [
- {
- code: "for (x of a) doSomething();",
- parserOptions: { ecmaVersion: 6 }
- }
-]
-```
-
-If you are working with ES6 modules:
-
-```js
-valid: [
- {
- code: "export default function () {};",
- parserOptions: { ecmaVersion: 6, sourceType: "module" }
- }
-]
-```
-
-For non-version specific features such as JSX:
-
-```js
-valid: [
- {
- code: "var foo = {bar}",
- parserOptions: { ecmaFeatures: { jsx: true } }
- }
-]
-```
-
-The options available and the expected syntax for `parserOptions` is the same as those used in [configuration](../use/configure/language-options#specifying-parser-options).
-
-### Write Several Tests
-
-Provide as many unit tests as possible. Your pull request will never be turned down for having too many tests submitted with it!
-
-## Performance Testing
-
-To keep the linting process efficient and unobtrusive, it is useful to verify the performance impact of new rules or modifications to existing rules.
-
-### Overall Performance
-
-The `npm run perf` command gives a high-level overview of ESLint running time with default rules (`eslint:recommended`) enabled.
-
-```bash
-$ git checkout main
-Switched to branch 'main'
-
-$ npm run perf
-CPU Speed is 2200 with multiplier 7500000
-Performance Run #1: 1394.689313ms
-Performance Run #2: 1423.295351ms
-Performance Run #3: 1385.09515ms
-Performance Run #4: 1382.406982ms
-Performance Run #5: 1409.68566ms
-Performance budget ok: 1394.689313ms (limit: 3409.090909090909ms)
-
-$ git checkout my-rule-branch
-Switched to branch 'my-rule-branch'
-
-$ npm run perf
-CPU Speed is 2200 with multiplier 7500000
-Performance Run #1: 1443.736547ms
-Performance Run #2: 1419.193291ms
-Performance Run #3: 1436.018228ms
-Performance Run #4: 1473.605485ms
-Performance Run #5: 1457.455283ms
-Performance budget ok: 1443.736547ms (limit: 3409.090909090909ms)
-```
-
-### Per-rule Performance
-
-ESLint has a built-in method to track performance of individual rules. Setting the `TIMING` environment variable will trigger the display, upon linting completion, of the ten longest-running rules, along with their individual running time and relative performance impact as a percentage of total rule processing time.
-
-```bash
-$ TIMING=1 eslint lib
-Rule | Time (ms) | Relative
-:-----------------------|----------:|--------:
-no-multi-spaces | 52.472 | 6.1%
-camelcase | 48.684 | 5.7%
-no-irregular-whitespace | 43.847 | 5.1%
-valid-jsdoc | 40.346 | 4.7%
-handle-callback-err | 39.153 | 4.6%
-space-infix-ops | 35.444 | 4.1%
-no-undefined | 25.693 | 3.0%
-no-shadow | 22.759 | 2.7%
-no-empty-class | 21.976 | 2.6%
-semi | 19.359 | 2.3%
-```
-
-To test one rule explicitly, combine the `--no-eslintrc`, and `--rule` options:
-
-```bash
-$ TIMING=1 eslint --no-eslintrc --rule "quotes: [2, 'double']" lib
-Rule | Time (ms) | Relative
-:------|----------:|--------:
-quotes | 18.066 | 100.0%
-```
-
-## Rule Naming Conventions
-
-The rule naming conventions for ESLint are fairly simple:
-
-* If your rule is disallowing something, prefix it with `no-` such as `no-eval` for disallowing `eval()` and `no-debugger` for disallowing `debugger`.
-* If your rule is enforcing the inclusion of something, use a short name without a special prefix.
-* Keep your rule names as short as possible, use abbreviations where appropriate, and no more than four words.
-* Use dashes between words.
-
-## Rule Acceptance Criteria
-
-Because rules are highly personal (and therefore very contentious), accepted rules should:
-
-* Not be library-specific.
-* Demonstrate a possible issue that can be resolved by rewriting the code.
-* Be general enough so as to apply for a large number of developers.
-* Not be the opposite of an existing rule.
-* Not overlap with an existing rule.
-
-## Runtime Rules
-
-The thing that makes ESLint different from other linters is the ability to define custom rules at runtime. This is perfect for rules that are specific to your project or company and wouldn't make sense for ESLint to ship with. With runtime rules, you don't have to wait for the next version of ESLint or be disappointed that your rule isn't general enough to apply to the larger JavaScript community, just write your rules and include them at runtime.
-
-Runtime rules are written in the same format as all other rules. Create your rule as you would any other and then follow these steps:
+As of ESLint v9.0.0, the function-style rule format that was current in ESLint <= 2.13.1 is no longer supported.
-1. Place all of your runtime rules in the same directory (i.e., `eslint_rules`).
-2. Create a [configuration file](../use/configure/) and specify your rule ID error level under the `rules` key. Your rule will not run unless it has a value of `1` or `2` in the configuration file.
-3. Run the [command line interface](../use/command-line-interface) using the `--rulesdir` option to specify the location of your runtime rules.
+[This is the most recent rule format](./custom-rules).
diff --git a/docs/src/extend/custom-rules.md b/docs/src/extend/custom-rules.md
index 35df20541b6d..06db29a3a7a9 100644
--- a/docs/src/extend/custom-rules.md
+++ b/docs/src/extend/custom-rules.md
@@ -5,72 +5,76 @@ eleventyNavigation:
parent: create plugins
title: Custom Rules
order: 2
-
---
You can create custom rules to use with ESLint. You might want to create a custom rule if the [core rules](../rules/) do not cover your use case.
-**Note:** This page covers the most recent rule format for ESLint >= 3.0.0. There is also a [deprecated rule format](./custom-rules-deprecated).
-
Here's the basic format of a custom rule:
```js
// customRule.js
module.exports = {
- meta: {
- type: "suggestion",
- docs: {
- description: "Description of the rule",
- },
- fixable: "code",
- schema: [] // no options
- },
- create: function(context) {
- return {
- // callback functions
- };
- }
+ meta: {
+ type: "suggestion",
+ docs: {
+ description: "Description of the rule",
+ },
+ fixable: "code",
+ schema: [], // no options
+ },
+ create: function (context) {
+ return {
+ // callback functions
+ };
+ },
};
```
+::: warning
+The core rules shipped in the `eslint` package are not considered part of the public API and are not designed to be extended from. Building on top of these rules is fragile and will most likely result in your rules breaking completely at some point in the future. If you're interested in creating a rule that is similar to a core rule, you should first copy the rule file into your project and proceed from there.
+:::
+
## Rule Structure
The source file for a rule exports an object with the following properties. Both custom rules and core rules follow this format.
`meta`: (`object`) Contains metadata for the rule:
-* `type`: (`string`) Indicates the type of rule, which is one of `"problem"`, `"suggestion"`, or `"layout"`:
+- `type`: (`string`) Indicates the type of rule, which is one of `"problem"`, `"suggestion"`, or `"layout"`:
+
+ - `"problem"`: The rule is identifying code that either will cause an error or may cause a confusing behavior. Developers should consider this a high priority to resolve.
+ - `"suggestion"`: The rule is identifying something that could be done in a better way but no errors will occur if the code isn't changed.
+ - `"layout"`: The rule cares primarily about whitespace, semicolons, commas, and parentheses, all the parts of the program that determine how the code looks rather than how it executes. These rules work on parts of the code that aren't specified in the AST.
- * `"problem"`: The rule is identifying code that either will cause an error or may cause a confusing behavior. Developers should consider this a high priority to resolve.
- * `"suggestion"`: The rule is identifying something that could be done in a better way but no errors will occur if the code isn't changed.
- * `"layout"`: The rule cares primarily about whitespace, semicolons, commas, and parentheses, all the parts of the program that determine how the code looks rather than how it executes. These rules work on parts of the code that aren't specified in the AST.
+- `docs`: (`object`) Properties often used for documentation generation and tooling. Required for core rules and optional for custom rules. Custom rules can include additional properties here as needed.
-* `docs`: (`object`) Required for core rules and optional for custom rules. Core rules have specific entries inside of `docs` while custom rules can include any properties that you need. The following properties are only relevant when working on core rules.
+ - `description`: (`string`) Provides a short description of the rule. For core rules, this is used in [rules index](../rules/).
+ - `recommended`: (`boolean`) For core rules, this specifies whether the rule is enabled by the `recommended` config from `@eslint/js`.
+ - `url`: (`string`) Specifies the URL at which the full documentation can be accessed. Code editors often use this to provide a helpful link on highlighted rule violations.
- * `description`: (`string`) Provides the short description of the rule in the [rules index](../rules/).
- * `recommended`: (`boolean`) Specifies whether the `"extends": "eslint:recommended"` property in a [configuration file](../use/configure/configuration-files#extending-configuration-files) enables the rule.
- * `url`: (`string`) Specifies the URL at which the full documentation can be accessed (enabling code editors to provide a helpful link on highlighted rule violations).
+- `fixable`: (`string`) Either `"code"` or `"whitespace"` if the `--fix` option on the [command line](../use/command-line-interface#--fix) automatically fixes problems reported by the rule.
-* `fixable`: (`string`) Either `"code"` or `"whitespace"` if the `--fix` option on the [command line](../use/command-line-interface#--fix) automatically fixes problems reported by the rule.
+ **Important:** the `fixable` property is mandatory for fixable rules. If this property isn't specified, ESLint will throw an error whenever the rule attempts to produce a fix. Omit the `fixable` property if the rule is not fixable.
- **Important:** the `fixable` property is mandatory for fixable rules. If this property isn't specified, ESLint will throw an error whenever the rule attempts to produce a fix. Omit the `fixable` property if the rule is not fixable.
+- `hasSuggestions`: (`boolean`) Specifies whether rules can return suggestions (defaults to `false` if omitted).
-* `hasSuggestions`: (`boolean`) Specifies whether rules can return suggestions (defaults to `false` if omitted).
+ **Important:** the `hasSuggestions` property is mandatory for rules that provide suggestions. If this property isn't set to `true`, ESLint will throw an error whenever the rule attempts to produce a suggestion. Omit the `hasSuggestions` property if the rule does not provide suggestions.
- **Important:** the `hasSuggestions` property is mandatory for rules that provide suggestions. If this property isn't set to `true`, ESLint will throw an error whenever the rule attempts to produce a suggestion. Omit the `hasSuggestions` property if the rule does not provide suggestions.
+- `schema`: (`object | array | false`) Specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../use/configure/rules). Mandatory when the rule has options.
-* `schema`: (`object | array`) Specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../use/configure/rules).
+- `defaultOptions`: (`array`) Specifies [default options](#option-defaults) for the rule. If present, any user-provided options in their config will be merged on top of them recursively.
-* `deprecated`: (`boolean`) Indicates whether the rule has been deprecated. You may omit the `deprecated` property if the rule has not been deprecated.
+- `deprecated`: (`boolean | DeprecatedInfo`) Indicates whether the rule has been deprecated. You may omit the `deprecated` property if the rule has not been deprecated.
+ There is a dedicated page for the [DeprecatedInfo](./rule-deprecation)
-* `replacedBy`: (`array`) In the case of a deprecated rule, specify replacement rule(s).
+- `replacedBy`: (`array`, **Deprecated** Use `meta.deprecated.replacedBy` instead.) In the case of a deprecated rule, specify replacement rule(s).
`create()`: Returns an object with methods that ESLint calls to "visit" nodes while traversing the abstract syntax tree (AST as defined by [ESTree](https://github.com/estree/estree)) of JavaScript code:
-* If a key is a node type or a [selector](./selectors), ESLint calls that **visitor** function while going **down** the tree.
-* If a key is a node type or a [selector](./selectors) plus `:exit`, ESLint calls that **visitor** function while going **up** the tree.
-* If a key is an event name, ESLint calls that **handler** function for [code path analysis](code-path-analysis).
+- If a key is a node type or a [selector](./selectors), ESLint calls that **visitor** function while going **down** the tree.
+- If a key is a node type or a [selector](./selectors) plus `:exit`, ESLint calls that **visitor** function while going **up** the tree.
+- If a key is an event name, ESLint calls that **handler** function for [code path analysis](code-path-analysis).
A rule can use the current node and its surrounding tree to report or fix problems.
@@ -103,6 +107,10 @@ module.exports = {
};
```
+::: tip
+You can view the complete AST for any JavaScript code using [Code Explorer](http://explorer.eslint.org).
+:::
+
## The Context Object
The `context` object is the only argument of the `create` method in a rule. For example:
@@ -123,37 +131,29 @@ As the name implies, the `context` object contains information that is relevant
The `context` object has the following properties:
-* `id`: (`string`) The rule ID.
-* `filename`: (`string`) The filename associated with the source.
-* `physicalFilename`: (`string`) When linting a file, it provides the full path of the file on disk without any code block information. When linting text, it provides the value passed to `âstdin-filename` or `` if not specified.
-* `cwd`: (`string`) The `cwd` option passed to the [Linter](../integrate/nodejs-api#linter). It is a path to a directory that should be considered the current working directory.
-* `options`: (`array`) An array of the [configured options](../use/configure/rules) for this rule. This array does not include the rule severity (see the [dedicated section](#accessing-options-passed-to-a-rule)).
-* `sourceCode`: (`object`) A `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
-* `settings`: (`object`) The [shared settings](../use/configure/configuration-files#adding-shared-settings) from the configuration.
-* `parserPath`: (`string`) The name of the `parser` from the configuration.
-* `parserServices`: (**Deprecated:** Use `SourceCode#parserServices` instead.) Contains parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use `parserServices` to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)
-* `parserOptions`: The parser options configured for this run (more details [here](../use/configure/language-options#specifying-parser-options)).
+- `id`: (`string`) The rule ID.
+- `filename`: (`string`) The filename associated with the source.
+- `physicalFilename`: (`string`) When linting a file, it provides the full path of the file on disk without any code block information. When linting text, it provides the value passed to `âstdin-filename` or `` if not specified.
+- `cwd`: (`string`) The `cwd` option passed to the [Linter](../integrate/nodejs-api#linter). It is a path to a directory that should be considered the current working directory.
+- `options`: (`array`) An array of the [configured options](../use/configure/rules) for this rule. This array does not include the rule severity (see the [dedicated section](#accessing-options-passed-to-a-rule)).
+- `sourceCode`: (`object`) A `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
+- `settings`: (`object`) The [shared settings](../use/configure/configuration-files#configuring-shared-settings) from the configuration.
+- `languageOptions`: (`object`) more details for each property [here](../use/configure/language-options)
+ - `sourceType`: (`'script' | 'module' | 'commonjs'`) The mode for the current file.
+ - `ecmaVersion`: (`number`) The ECMA version used to parse the current file.
+ - `parser`: (`object`): The parser used to parse the current file.
+ - `parserOptions`: (`object`) The parser options configured for this file.
+ - `globals`: (`object`) The specified globals.
+- `parserPath`: (`string`, **Removed** Use `context.languageOptions.parser` instead.) The name of the `parser` from the configuration.
+- `parserOptions`: (**Deprecated** Use `context.languageOptions.parserOptions` instead.) The parser options configured for this run (more details [here](../use/configure/language-options#specifying-parser-options)).
Additionally, the `context` object has the following methods:
-* `getAncestors()`: (**Deprecated:** Use `SourceCode#getAncestors(node)` instead.) Returns an array of the ancestors of the currently-traversed node, starting at the root of the AST and continuing through the direct parent of the current node. This array does not include the currently-traversed node itself.
-* `getCwd()`: (**Deprecated:** Use `context.cwd` instead.) Returns the `cwd` option passed to the [Linter](../integrate/nodejs-api#linter). It is a path to a directory that should be considered the current working directory.
-* `getDeclaredVariables(node)`: (**Deprecated:** Use `SourceCode#getDeclaredVariables(node)` instead.) Returns a list of [variables](./scope-manager-interface#variable-interface) declared by the given node. This information can be used to track references to variables.
- * If the node is a `VariableDeclaration`, all variables declared in the declaration are returned.
- * If the node is a `VariableDeclarator`, all variables declared in the declarator are returned.
- * If the node is a `FunctionDeclaration` or `FunctionExpression`, the variable for the function name is returned, in addition to variables for the function parameters.
- * If the node is an `ArrowFunctionExpression`, variables for the parameters are returned.
- * If the node is a `ClassDeclaration` or a `ClassExpression`, the variable for the class name is returned.
- * If the node is a `CatchClause`, the variable for the exception is returned.
- * If the node is an `ImportDeclaration`, variables for all of its specifiers are returned.
- * If the node is an `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier`, the declared variable is returned.
- * Otherwise, if the node does not declare any variables, an empty array is returned.
-* `getFilename()`: (**Deprecated:** Use `context.filename` instead.) Returns the filename associated with the source.
-* `getPhysicalFilename()`: (**Deprecated:** Use `context.physicalFilename` instead.) When linting a file, it returns the full path of the file on disk without any code block information. When linting text, it returns the value passed to `âstdin-filename` or `` if not specified.
-* `getScope()`: (**Deprecated:** Use `SourceCode#getScope(node)` instead.) Returns the [scope](./scope-manager-interface#scope-interface) of the currently-traversed node. This information can be used to track references to variables.
-* `getSourceCode()`: (**Deprecated:** Use `context.sourceCode` instead.) Returns a `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
-* `markVariableAsUsed(name)`: (**Deprecated:** Use `SourceCode#markVariableAsUsed(name, node)` instead.) Marks a variable with the given name in the current scope as used. This affects the [no-unused-vars](../rules/no-unused-vars) rule. Returns `true` if a variable with the given name was found and marked as used, otherwise `false`.
-* `report(descriptor)`. Reports a problem in the code (see the [dedicated section](#reporting-problems)).
+- `getCwd()`: (**Deprecated:** Use `context.cwd` instead.) Returns the `cwd` option passed to the [Linter](../integrate/nodejs-api#linter). It is a path to a directory that should be considered the current working directory.
+- `getFilename()`: (**Deprecated:** Use `context.filename` instead.) Returns the filename associated with the source.
+- `getPhysicalFilename()`: (**Deprecated:** Use `context.physicalFilename` instead.) When linting a file, it returns the full path of the file on disk without any code block information. When linting text, it returns the value passed to `âstdin-filename` or `` if not specified.
+- `getSourceCode()`: (**Deprecated:** Use `context.sourceCode` instead.) Returns a `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
+- `report(descriptor)`. Reports a problem in the code (see the [dedicated section](#reporting-problems)).
**Note:** Earlier versions of ESLint supported additional methods on the `context` object. Those methods were removed in the new format and should not be relied upon.
@@ -161,17 +161,18 @@ Additionally, the `context` object has the following methods:
The main method you'll use when writing custom rules is `context.report()`, which publishes a warning or error (depending on the configuration being used). This method accepts a single argument, which is an object containing the following properties:
-* `message`: (`string`) The problem message.
-* `node`: (optional `object`) The AST node related to the problem. If present and `loc` is not specified, then the starting location of the node is used as the location of the problem.
-* `loc`: (optional `object`) Specifies the location of the problem. If both `loc` and `node` are specified, then the location is used from `loc` instead of `node`.
- * `start`: An object of the start location.
- * `line`: (`number`) The 1-based line number at which the problem occurred.
- * `column`: (`number`) The 0-based column number at which the problem occurred.
- * `end`: An object of the end location.
- * `line`: (`number`) The 1-based line number at which the problem occurred.
- * `column`: (`number`) The 0-based column number at which the problem occurred.
-* `data`: (optional `object`) [Placeholder](#using-message-placeholders) data for `message`.
-* `fix(fixer)`: (optional `function`) Applies a [fix](#applying-fixes) to resolve the problem.
+- `messageId`: (`string`) The ID of the message (see [messageIds](#messageids)) (recommended over `message`).
+- `message`: (`string`) The problem message (alternative to `messageId`).
+- `node`: (optional `object`) This can be an AST node, a token, or a comment related to the problem. If present and `loc` is not specified, then the starting location of the node is used as the location of the problem.
+- `loc`: (optional `object`) Specifies the location of the problem. If both `loc` and `node` are specified, then the location is used from `loc` instead of `node`.
+ - `start`: An object of the start location.
+ - `line`: (`number`) The 1-based line number at which the problem occurred.
+ - `column`: (`number`) The 0-based column number at which the problem occurred.
+ - `end`: An object of the end location.
+ - `line`: (`number`) The 1-based line number at which the problem occurred.
+ - `column`: (`number`) The 0-based column number at which the problem occurred.
+- `data`: (optional `object`) [Placeholder](#using-message-placeholders) data for `message`.
+- `fix(fixer)`: (optional `function`) Applies a [fix](#applying-fixes) to resolve the problem.
Note that at least one of `node` or `loc` is required.
@@ -179,8 +180,8 @@ The simplest example is to use just `node` and `message`:
```js
context.report({
- node: node,
- message: "Unexpected identifier"
+ node: node,
+ message: "Unexpected identifier",
});
```
@@ -208,9 +209,11 @@ The node contains all the information necessary to figure out the line and colum
#### `messageId`s
-Instead of typing out messages in both the `context.report()` call and your tests, you can use `messageId`s instead.
+`messageId`s are the recommended approach to reporting messages in `context.report()` calls because of the following benefits:
-This allows you to avoid retyping error messages. It also prevents errors reported in different sections of your rule from having out-of-date messages.
+- Rule violation messages can be stored in a central `meta.messages` object for convenient management.
+- Rule violation messages do not need to be repeated in both the rule file and rule test file.
+- As a result, the barrier for changing rule violation messages is lower, encouraging more frequent contributions to improve and optimize them for the greatest clarity and usefulness.
Rule file:
@@ -262,17 +265,17 @@ var RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run("avoid-name", rule, {
- valid: ["bar", "baz"],
- invalid: [
- {
- code: "foo",
- errors: [
- {
- messageId: "avoidName"
- }
- ]
- }
- ]
+ valid: ["bar", "baz"],
+ invalid: [
+ {
+ code: "foo",
+ errors: [
+ {
+ messageId: "avoidName",
+ },
+ ],
+ },
+ ],
});
```
@@ -282,11 +285,11 @@ If you'd like ESLint to attempt to fix the problem you're reporting, you can do
```js
context.report({
- node: node,
- message: "Missing semicolon",
- fix(fixer) {
- return fixer.insertTextAfter(node, ";");
- }
+ node: node,
+ message: "Missing semicolon",
+ fix(fixer) {
+ return fixer.insertTextAfter(node, ";");
+ },
});
```
@@ -296,23 +299,23 @@ Here, the `fix()` function is used to insert a semicolon after the node. Note th
The `fixer` object has the following methods:
-* `insertTextAfter(nodeOrToken, text)`: Insert text after the given node or token.
-* `insertTextAfterRange(range, text)`: Insert text after the given range.
-* `insertTextBefore(nodeOrToken, text)`: Insert text before the given node or token.
-* `insertTextBeforeRange(range, text)`: Insert text before the given range.
-* `remove(nodeOrToken)`: Remove the given node or token.
-* `removeRange(range)`: Remove text in the given range.
-* `replaceText(nodeOrToken, text)`: Replace the text in the given node or token.
-* `replaceTextRange(range, text)`: Replace the text in the given range.
+- `insertTextAfter(nodeOrToken, text)`: Insert text after the given node or token.
+- `insertTextAfterRange(range, text)`: Insert text after the given range.
+- `insertTextBefore(nodeOrToken, text)`: Insert text before the given node or token.
+- `insertTextBeforeRange(range, text)`: Insert text before the given range.
+- `remove(nodeOrToken)`: Remove the given node or token.
+- `removeRange(range)`: Remove text in the given range.
+- `replaceText(nodeOrToken, text)`: Replace the text in the given node or token.
+- `replaceTextRange(range, text)`: Replace the text in the given range.
A `range` is a two-item array containing character indices inside the source code. The first item is the start of the range (inclusive) and the second item is the end of the range (exclusive). Every node and token has a `range` property to identify the source code range they represent.
The above methods return a `fixing` object.
The `fix()` function can return the following values:
-* A `fixing` object.
-* An array which includes `fixing` objects.
-* An iterable object which enumerates `fixing` objects. Especially, the `fix()` function can be a generator.
+- A `fixing` object.
+- An array which includes `fixing` objects.
+- An iterable object which enumerates `fixing` objects. Especially, the `fix()` function can be a generator.
If you make a `fix()` function which returns multiple `fixing` objects, those `fixing` objects must not overlap.
@@ -322,21 +325,20 @@ Best practices for fixes:
1. Make fixes as small as possible. Fixes that are unnecessarily large could conflict with other fixes, and prevent them from being applied.
1. Only make one fix per message. This is enforced because you must return the result of the fixer operation from `fix()`.
1. Since all rules are run again after the initial round of fixes is applied, it's not necessary for a rule to check whether the code style of a fix will cause errors to be reported by another rule.
- * For example, suppose a fixer would like to surround an object key with quotes, but it's not sure whether the user would prefer single or double quotes.
+
+ - For example, suppose a fixer would like to surround an object key with quotes, but it's not sure whether the user would prefer single or double quotes.
```js
- ({ foo : 1 })
+ { foo: 1 }
// should get fixed to either
-
- ({ 'foo': 1 })
+ { 'foo': 1 }
// or
-
- ({ "foo": 1 })
+ { "foo": 1 }
```
- * This fixer can just select a quote type arbitrarily. If it guesses wrong, the resulting code will be automatically reported and fixed by the [`quotes`](../rules/quotes) rule.
+ - This fixer can just select a quote type arbitrarily. If it guesses wrong, the resulting code will be automatically reported and fixed by the [`quotes`](../rules/quotes) rule.
Note: Making fixes as small as possible is a best practice, but in some cases it may be correct to extend the range of the fix in order to intentionally prevent other rules from making fixes in a surrounding range in the same pass. For instance, if replacement text declares a new variable, it can be useful to prevent other changes in the scope of the variable as they might cause name collisions.
@@ -344,15 +346,15 @@ The following example replaces `node` and also ensures that no other fixes will
```js
context.report({
- node,
- message,
- *fix(fixer) {
- yield fixer.replaceText(node, replacementText);
-
- // extend range of the fix to the range of `node.parent`
- yield fixer.insertTextBefore(node.parent, "");
- yield fixer.insertTextAfter(node.parent, "");
- }
+ node,
+ message,
+ *fix(fixer) {
+ yield fixer.replaceText(node, replacementText);
+
+ // extend range of the fix to the range of `node.parent`
+ yield fixer.insertTextBefore(node.parent, "");
+ yield fixer.insertTextAfter(node.parent, "");
+ },
});
```
@@ -488,7 +490,7 @@ Some rules require options in order to function correctly. These options appear
```json
{
- "quotes": ["error", "double"]
+ "quotes": ["error", "double"]
}
```
@@ -496,11 +498,18 @@ The `quotes` rule in this example has one option, `"double"` (the `error` is the
```js
module.exports = {
- create: function(context) {
- var isDouble = (context.options[0] === "double");
-
- // ...
- }
+ meta: {
+ schema: [
+ {
+ enum: ["single", "double", "backtick"],
+ },
+ ],
+ },
+ create: function (context) {
+ var isDouble = context.options[0] === "double";
+
+ // ...
+ },
};
```
@@ -508,17 +517,19 @@ Since `context.options` is just an array, you can use it to determine how many o
When using options, make sure that your rule has some logical defaults in case the options are not provided.
+Rules with options must specify a [schema](#options-schemas).
+
### Accessing the Source Code
The `SourceCode` object is the main object for getting more information about the source code being linted. You can retrieve the `SourceCode` object at any time by using the `context.sourceCode` property:
```js
module.exports = {
- create: function(context) {
- var sourceCode = context.sourceCode;
+ create: function (context) {
+ var sourceCode = context.sourceCode;
- // ...
- }
+ // ...
+ },
};
```
@@ -526,57 +537,71 @@ module.exports = {
Once you have an instance of `SourceCode`, you can use the following methods on it to work with the code:
-* `getText(node)`: Returns the source code for the given node. Omit `node` to get the whole source (see the [dedicated section](#accessing-the-source-text)).
-* `getAllComments()`: Returns an array of all comments in the source (see the [dedicated section](#accessing-comments)).
-* `getCommentsBefore(nodeOrToken)`: Returns an array of comment tokens that occur directly before the given node or token (see the [dedicated section](#accessing-comments)).
-* `getCommentsAfter(nodeOrToken)`: Returns an array of comment tokens that occur directly after the given node or token (see the [dedicated section](#accessing-comments)).
-* `getCommentsInside(node)`: Returns an array of all comment tokens inside a given node (see the [dedicated section](#accessing-comments)).
-* `isSpaceBetween(nodeOrToken, nodeOrToken)`: Returns true if there is a whitespace character between the two tokens or, if given a node, the last token of the first node and the first token of the second node.
-* `getFirstToken(node, skipOptions)`: Returns the first token representing the given node.
-* `getFirstTokens(node, countOptions)`: Returns the first `count` tokens representing the given node.
-* `getLastToken(node, skipOptions)`: Returns the last token representing the given node.
-* `getLastTokens(node, countOptions)`: Returns the last `count` tokens representing the given node.
-* `getTokenAfter(nodeOrToken, skipOptions)`: Returns the first token after the given node or token.
-* `getTokensAfter(nodeOrToken, countOptions)`: Returns `count` tokens after the given node or token.
-* `getTokenBefore(nodeOrToken, skipOptions)`: Returns the first token before the given node or token.
-* `getTokensBefore(nodeOrToken, countOptions)`: Returns `count` tokens before the given node or token.
-* `getFirstTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)`: Returns the first token between two nodes or tokens.
-* `getFirstTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)`: Returns the first `count` tokens between two nodes or tokens.
-* `getLastTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)`: Returns the last token between two nodes or tokens.
-* `getLastTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)`: Returns the last `count` tokens between two nodes or tokens.
-* `getTokens(node)`: Returns all tokens for the given node.
-* `getTokensBetween(nodeOrToken1, nodeOrToken2)`: Returns all tokens between two nodes.
-* `getTokenByRangeStart(index, rangeOptions)`: Returns the token whose range starts at the given index in the source.
-* `getNodeByRangeIndex(index)`: Returns the deepest node in the AST containing the given source index.
-* `getLocFromIndex(index)`: Returns an object with `line` and `column` properties, corresponding to the location of the given source index. `line` is 1-based and `column` is 0-based.
-* `getIndexFromLoc(loc)`: Returns the index of a given location in the source code, where `loc` is an object with a 1-based `line` key and a 0-based `column` key.
-* `commentsExistBetween(nodeOrToken1, nodeOrToken2)`: Returns `true` if comments exist between two nodes.
+- `getText(node)`: Returns the source code for the given node. Omit `node` to get the whole source (see the [dedicated section](#accessing-the-source-text)).
+- `getAllComments()`: Returns an array of all comments in the source (see the [dedicated section](#accessing-comments)).
+- `getCommentsBefore(nodeOrToken)`: Returns an array of comment tokens that occur directly before the given node or token (see the [dedicated section](#accessing-comments)).
+- `getCommentsAfter(nodeOrToken)`: Returns an array of comment tokens that occur directly after the given node or token (see the [dedicated section](#accessing-comments)).
+- `getCommentsInside(node)`: Returns an array of all comment tokens inside a given node (see the [dedicated section](#accessing-comments)).
+- `isSpaceBetween(nodeOrToken, nodeOrToken)`: Returns true if there is a whitespace character between the two tokens or, if given a node, the last token of the first node and the first token of the second node.
+- `isGlobalReference(node)`: Returns true if the identifier references a global variable configured via `languageOptions.globals`, `/* global */` comments, or `ecmaVersion`, and not declared by a local binding.
+- `getFirstToken(node, skipOptions)`: Returns the first token representing the given node.
+- `getFirstTokens(node, countOptions)`: Returns the first `count` tokens representing the given node.
+- `getLastToken(node, skipOptions)`: Returns the last token representing the given node.
+- `getLastTokens(node, countOptions)`: Returns the last `count` tokens representing the given node.
+- `getTokenAfter(nodeOrToken, skipOptions)`: Returns the first token after the given node or token.
+- `getTokensAfter(nodeOrToken, countOptions)`: Returns `count` tokens after the given node or token.
+- `getTokenBefore(nodeOrToken, skipOptions)`: Returns the first token before the given node or token.
+- `getTokensBefore(nodeOrToken, countOptions)`: Returns `count` tokens before the given node or token.
+- `getFirstTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)`: Returns the first token between two nodes or tokens.
+- `getFirstTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)`: Returns the first `count` tokens between two nodes or tokens.
+- `getLastTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)`: Returns the last token between two nodes or tokens.
+- `getLastTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)`: Returns the last `count` tokens between two nodes or tokens.
+- `getTokens(node)`: Returns all tokens for the given node.
+- `getTokensBetween(nodeOrToken1, nodeOrToken2)`: Returns all tokens between two nodes.
+- `getTokenByRangeStart(index, rangeOptions)`: Returns the token whose range starts at the given index in the source.
+- `getNodeByRangeIndex(index)`: Returns the deepest node in the AST containing the given source index.
+- `getLocFromIndex(index)`: Returns an object with `line` and `column` properties, corresponding to the location of the given source index. `line` is 1-based and `column` is 0-based.
+- `getIndexFromLoc(loc)`: Returns the index of a given location in the source code, where `loc` is an object with a 1-based `line` key and a 0-based `column` key.
+- `commentsExistBetween(nodeOrToken1, nodeOrToken2)`: Returns `true` if comments exist between two nodes.
+- `getAncestors(node)`: Returns an array of the ancestors of the given node, starting at the root of the AST and continuing through the direct parent of the given node. This array does not include the given node itself.
+- `getDeclaredVariables(node)`: Returns a list of [variables](./scope-manager-interface#variable-interface) declared by the given node. This information can be used to track references to variables.
+ - If the node is a `VariableDeclaration`, all variables declared in the declaration are returned.
+ - If the node is a `VariableDeclarator`, all variables declared in the declarator are returned.
+ - If the node is a `FunctionDeclaration` or `FunctionExpression`, the variable for the function name is returned, in addition to variables for the function parameters.
+ - If the node is an `ArrowFunctionExpression`, variables for the parameters are returned.
+ - If the node is a `ClassDeclaration` or a `ClassExpression`, the variable for the class name is returned.
+ - If the node is a `CatchClause`, the variable for the exception is returned.
+ - If the node is an `ImportDeclaration`, variables for all of its specifiers are returned.
+ - If the node is an `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier`, the declared variable is returned.
+ - Otherwise, if the node does not declare any variables, an empty array is returned.
+- `getScope(node)`: Returns the [scope](./scope-manager-interface#scope-interface) of the given node. This information can be used to track references to variables.
+- `markVariableAsUsed(name, refNode)`: Marks a variable with the given name in a scope indicated by the given reference node as used. This affects the [no-unused-vars](../rules/no-unused-vars) rule. Returns `true` if a variable with the given name was found and marked as used, otherwise `false`.
`skipOptions` is an object which has 3 properties; `skip`, `includeComments`, and `filter`. Default is `{skip: 0, includeComments: false, filter: null}`.
-* `skip`: (`number`) Positive integer, the number of skipping tokens. If `filter` option is given at the same time, it doesn't count filtered tokens as skipped.
-* `includeComments`: (`boolean`) The flag to include comment tokens into the result.
-* `filter(token)`: Function which gets a token as the first argument. If the function returns `false` then the result excludes the token.
+- `skip`: (`number`) Positive integer, the number of skipping tokens. If `filter` option is given at the same time, it doesn't count filtered tokens as skipped.
+- `includeComments`: (`boolean`) The flag to include comment tokens into the result.
+- `filter(token)`: Function which gets a token as the first argument. If the function returns `false` then the result excludes the token.
`countOptions` is an object which has 3 properties; `count`, `includeComments`, and `filter`. Default is `{count: 0, includeComments: false, filter: null}`.
-* `count`: (`number`) Positive integer, the maximum number of returning tokens.
-* `includeComments`: (`boolean`) The flag to include comment tokens into the result.
-* `filter(token)`: Function which gets a token as the first argument, if the function returns `false` then the result excludes the token.
+- `count`: (`number`) Positive integer, the maximum number of returning tokens.
+- `includeComments`: (`boolean`) The flag to include comment tokens into the result.
+- `filter(token)`: Function which gets a token as the first argument, if the function returns `false` then the result excludes the token.
`rangeOptions` is an object that has 1 property, `includeComments`. Default is `{includeComments: false}`.
-* `includeComments`: (`boolean`) The flag to include comment tokens into the result.
+- `includeComments`: (`boolean`) The flag to include comment tokens into the result.
There are also some properties you can access:
-* `hasBOM`: (`boolean`) The flag to indicate whether the source code has Unicode BOM.
-* `text`: (`string`) The full text of the code being linted. Unicode BOM has been stripped from this text.
-* `ast`: (`object`) `Program` node of the AST for the code being linted.
-* `scopeManager`: [ScopeManager](./scope-manager-interface#scopemanager-interface) object of the code.
-* `visitorKeys`: (`object`) Visitor keys to traverse this AST.
-* `parserServices`: (`object`) Contains parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use `parserServices` to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)
-* `lines`: (`array`) Array of lines, split according to the specification's definition of line breaks.
+- `hasBOM`: (`boolean`) The flag to indicate whether the source code has Unicode BOM.
+- `text`: (`string`) The full text of the code being linted. Unicode BOM has been stripped from this text.
+- `ast`: (`object`) `Program` node of the AST for the code being linted.
+- `scopeManager`: [ScopeManager](./scope-manager-interface#scopemanager-interface) object of the code.
+- `visitorKeys`: (`object`) Visitor keys to traverse this AST.
+- `parserServices`: (`object`) Contains parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use `parserServices` to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)
+- `lines`: (`array`) Array of lines, split according to the specification's definition of line breaks.
You should use a `SourceCode` object whenever you need to get more information about the code being linted.
@@ -585,7 +610,6 @@ You should use a `SourceCode` object whenever you need to get more information a
If your rule needs to get the actual JavaScript source to work with, then use the `sourceCode.getText()` method. This method works as follows:
```js
-
// get all source
var source = sourceCode.getText();
@@ -613,9 +637,11 @@ You can also access comments through many of `sourceCode`'s methods using the `i
### Options Schemas
-Rules may specify a `schema` property, which is a [JSON Schema](https://json-schema.org/) format description of a rule's options which will be used by ESLint to validate configuration options and prevent invalid or unexpected inputs before they are passed to the rule in `context.options`.
+Rules with options must specify a `meta.schema` property, which is a [JSON Schema](https://json-schema.org/) format description of a rule's options which will be used by ESLint to validate configuration options and prevent invalid or unexpected inputs before they are passed to the rule in `context.options`.
-Note: Prior to ESLint v9.0.0, rules without a schema are passed their options directly from the config without any validation. In ESLint v9.0.0 and later, rules without schemas will throw errors when options are passed. See the [Require schemas and object-style rules](https://github.com/eslint/rfcs/blob/main/designs/2021-schema-object-rules/README.md) RFC for further details.
+If your rule has options, it is strongly recommended that you specify a schema for options validation. However, it is possible to opt-out of options validation by setting `schema: false`, but doing so is discouraged as it increases the chance of bugs and mistakes.
+
+For rules that don't specify a `meta.schema` property, ESLint throws errors when any options are passed. If your rule doesn't have options, do not set `schema: false`, but simply omit the schema property or use `schema: []`, both of which prevent any options from being passed.
When validating a rule's config, there are five steps:
@@ -629,18 +655,18 @@ Note: this means that the rule schema cannot validate the severity. The rule sch
There are two formats for a rule's `schema`:
-* An array of JSON Schema objects
- * Each element will be checked against the same position in the `context.options` array.
- * If the `context.options` array has fewer elements than there are schemas, then the unmatched schemas are ignored
- * If the `context.options` array has more elements than there are schemas, then the validation fails
- * There are two important consequences to using this format:
- * It is _always valid_ for a user to provide no options to your rule (beyond severity)
- * If you specify an empty array, then it is _always an error_ for a user to provide any options to your rule (beyond severity)
-* A full JSON Schema object that will validate the `context.options` array
- * The schema should assume an array of options to validate even if your rule only accepts one option.
- * The schema can be arbitrarily complex, so you can validate completely different sets of potential options via `oneOf`, `anyOf` etc.
- * The supported version of JSON Schemas is [Draft-04](http://json-schema.org/draft-04/schema), so some newer features such as `if` or `$data` are unavailable.
- * At present, it is explicitly planned to not update schema support beyond this level due to ecosystem compatibility concerns. See [this comment](https://github.com/eslint/eslint/issues/13888#issuecomment-872591875) for further context.
+- An array of JSON Schema objects
+ - Each element will be checked against the same position in the `context.options` array.
+ - If the `context.options` array has fewer elements than there are schemas, then the unmatched schemas are ignored.
+ - If the `context.options` array has more elements than there are schemas, then the validation fails.
+ - There are two important consequences to using this format:
+ - It is _always valid_ for a user to provide no options to your rule (beyond severity).
+ - If you specify an empty array, then it is _always an error_ for a user to provide any options to your rule (beyond severity).
+- A full JSON Schema object that will validate the `context.options` array
+ - The schema should assume an array of options to validate even if your rule only accepts one option.
+ - The schema can be arbitrarily complex, so you can validate completely different sets of potential options via `oneOf`, `anyOf` etc.
+ - The supported version of JSON Schemas is [Draft-04](http://json-schema.org/draft-04/schema), so some newer features such as `if` or `$data` are unavailable.
+ - At present, it is explicitly planned to not update schema support beyond this level due to ecosystem compatibility concerns. See [this comment](https://github.com/eslint/eslint/issues/13888#issuecomment-872591875) for further context.
For example, the `yoda` rule accepts a primary mode argument of `"always"` or `"never"`, as well as an extra options object with an optional property `exceptRange`:
@@ -654,20 +680,20 @@ For example, the `yoda` rule accepts a primary mode argument of `"always"` or `"
// "yoda": ["warn", "never", { "exceptRange": true }, 5]
// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
- meta: {
- schema: [
- {
- enum: ["always", "never"]
- },
- {
- type: "object",
- properties: {
- exceptRange: { type: "boolean" }
- },
- additionalProperties: false
- }
- ]
- }
+ meta: {
+ schema: [
+ {
+ enum: ["always", "never"],
+ },
+ {
+ type: "object",
+ properties: {
+ exceptRange: { type: "boolean" },
+ },
+ additionalProperties: false,
+ },
+ ],
+ },
};
```
@@ -683,25 +709,25 @@ And here is the equivalent object-based schema:
// "yoda": ["warn", "never", { "exceptRange": true }, 5]
// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
- meta: {
- schema: {
- type: "array",
- minItems: 0,
- maxItems: 2,
- items: [
- {
- enum: ["always", "never"]
- },
- {
- type: "object",
- properties: {
- exceptRange: { type: "boolean" }
- },
- additionalProperties: false
- }
- ]
- }
- }
+ meta: {
+ schema: {
+ type: "array",
+ minItems: 0,
+ maxItems: 2,
+ items: [
+ {
+ enum: ["always", "never"],
+ },
+ {
+ type: "object",
+ properties: {
+ exceptRange: { type: "boolean" },
+ },
+ additionalProperties: false,
+ },
+ ],
+ },
+ },
};
```
@@ -721,36 +747,36 @@ Object schemas can be more precise and restrictive in what is permitted. For exa
// "someRule": ["warn", 7, { someOtherProperty: 5 }]
// "someRule": ["warn", 7, { someNonOptionalProperty: false, someOtherProperty: 5 }]
module.exports = {
- meta: {
- schema: {
- type: "array",
- minItems: 1, // Can't specify only severity!
- maxItems: 2,
- items: [
- {
- type: "number",
- minimum: 0,
- maximum: 10
- },
- {
- anyOf: [
- {
- type: "object",
- properties: {
- someNonOptionalProperty: { type: "boolean" }
- },
- required: ["someNonOptionalProperty"],
- additionalProperties: false
- },
- {
- enum: ["off", "strict"]
- }
- ]
- }
- ]
- }
- }
-}
+ meta: {
+ schema: {
+ type: "array",
+ minItems: 1, // Can't specify only severity!
+ maxItems: 2,
+ items: [
+ {
+ type: "number",
+ minimum: 0,
+ maximum: 10,
+ },
+ {
+ anyOf: [
+ {
+ type: "object",
+ properties: {
+ someNonOptionalProperty: { type: "boolean" },
+ },
+ required: ["someNonOptionalProperty"],
+ additionalProperties: false,
+ },
+ {
+ enum: ["off", "strict"],
+ },
+ ],
+ },
+ ],
+ },
+ },
+};
```
Remember, rule options are always an array, so be careful not to specify a schema for a non-array type at the top level. If your schema does not specify an array at the top-level, users can _never_ enable your rule, as their configuration will always be invalid when the rule is enabled.
@@ -761,54 +787,107 @@ Here's an example schema that will always fail validation:
// Possibly trying to validate ["error", { someOptionalProperty: true }]
// but when the rule is enabled, config will always fail validation because the options are an array which doesn't match "object"
module.exports = {
- meta: {
- schema: {
- type: "object",
- properties: {
- someOptionalProperty: {
- type: "boolean"
- }
- },
- additionalProperties: false
- }
- }
-}
+ meta: {
+ schema: {
+ type: "object",
+ properties: {
+ someOptionalProperty: {
+ type: "boolean",
+ },
+ },
+ additionalProperties: false,
+ },
+ },
+};
```
**Note:** If your rule schema uses JSON schema [`$ref`](https://json-schema.org/understanding-json-schema/structuring.html#ref) properties, you must use the full JSON Schema object rather than the array of positional property schemas. This is because ESLint transforms the array shorthand into a single schema without updating references that makes them incorrect (they are ignored).
-To learn more about JSON Schema, we recommend looking at some examples on the [JSON Schema website](https://json-schema.org/learn/), or reading the free [Understanding JSON Schema](https://json-schema.org/understanding-json-schema/) ebook.
+To learn more about JSON Schema, we recommend looking at some examples on the [JSON Schema website](https://json-schema.org/learn/miscellaneous-examples), or reading the free [Understanding JSON Schema](https://json-schema.org/understanding-json-schema/) ebook.
+
+### Option Defaults
+
+Rules may specify a `meta.defaultOptions` array of default values for any options.
+When the rule is enabled in a user configuration, ESLint will recursively merge any user-provided option elements on top of the default elements.
+
+For example, given the following defaults:
+
+```js
+export default {
+ meta: {
+ defaultOptions: [
+ {
+ alias: "basic",
+ },
+ ],
+ schema: [
+ {
+ type: "object",
+ properties: {
+ alias: {
+ type: "string",
+ },
+ },
+ additionalProperties: false,
+ },
+ ],
+ },
+ create(context) {
+ const [{ alias }] = context.options;
+
+ return {
+ /* ... */
+ };
+ },
+};
+```
+
+The rule would have a runtime `alias` value of `"basic"` unless the user configuration specifies a different value, such as with `["error", { alias: "complex" }]`.
+
+Each element of the options array is merged according to the following rules:
+
+- Any missing value or explicit user-provided `undefined` will fall back to a default option
+- User-provided arrays and primitive values other than `undefined` override a default option
+- User-provided objects will merge into a default option object and replace a non-object default otherwise
+
+Option defaults will also be validated against the rule's `meta.schema`.
+
+**Note:** ESLint internally uses [Ajv](https://ajv.js.org) for schema validation with its [`useDefaults` option](https://ajv.js.org/guide/modifying-data.html#assigning-defaults) enabled.
+Both user-provided and `meta.defaultOptions` options will override any defaults specified in a rule's schema.
+ESLint may disable Ajv's `useDefaults` in a future major version.
### Accessing Shebangs
-[Shebangs (#!)](https://en.wikipedia.org/wiki/Shebang_(Unix)) are represented by the unique tokens of type `"Shebang"`. They are treated as comments and can be accessed by the methods outlined in the [Accessing Comments](#accessing-comments) section, such as `sourceCode.getAllComments()`.
+[Shebangs (#!)]() are represented by the unique tokens of type `"Shebang"`. They are treated as comments and can be accessed by the methods outlined in the [Accessing Comments](#accessing-comments) section, such as `sourceCode.getAllComments()`.
### Accessing Variable Scopes
The `SourceCode#getScope(node)` method returns the scope of the given node. It is a useful method for finding information about the variables in a given scope and how they are used in other scopes.
-**Deprecated:** The `context.getScope()` is deprecated; make sure to use `SourceCode#getScope(node)` instead.
+::: tip
+You can view scope information for any JavaScript code using [Code Explorer](http://explorer.eslint.org).
+:::
#### Scope types
The following table contains a list of AST node types and the scope type that they correspond to. For more information about the scope types, refer to the [`Scope` object documentation](./scope-manager-interface#scope-interface).
| AST Node Type | Scope Type |
-|:--------------------------|:-----------|
+| :------------------------ | :--------- |
| `Program` | `global` |
| `FunctionDeclaration` | `function` |
| `FunctionExpression` | `function` |
| `ArrowFunctionExpression` | `function` |
| `ClassDeclaration` | `class` |
| `ClassExpression` | `class` |
-| `BlockStatement` âģ1 | `block` |
-| `SwitchStatement` âģ1 | `switch` |
-| `ForStatement` âģ2 | `for` |
-| `ForInStatement` âģ2 | `for` |
-| `ForOfStatement` âģ2 | `for` |
+| `BlockStatement` âģ1 | `block` |
+| `SwitchStatement` âģ1 | `switch` |
+| `ForStatement` âģ2 | `for` |
+| `ForInStatement` âģ2 | `for` |
+| `ForOfStatement` âģ2 | `for` |
| `WithStatement` | `with` |
| `CatchClause` | `catch` |
-| others | âģ3 |
+| others | âģ3 |
**âģ1** Only if the configured parser provided the block-scope feature. The default parser provides the block-scope feature if `parserOptions.ecmaVersion` is not less than `6`.
**âģ2** Only if the `for` statement defines the iteration variable as a block-scoped variable (E.g., `for (let i = 0;;) {}`).
@@ -824,41 +903,38 @@ Also inside of each `Variable`, the `Variable#defs` property contains an array o
Global variables have the following additional properties:
-* `Variable#writeable` (`boolean | undefined`) ... If `true`, this global variable can be assigned arbitrary value. If `false`, this global variable is read-only.
-* `Variable#eslintExplicitGlobal` (`boolean | undefined`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file.
-* `Variable#eslintExplicitGlobalComments` (`Comment[] | undefined`) ... The array of `/* globals */` directive comments which defined this global variable in the source code file. This property is `undefined` if there are no `/* globals */` directive comments.
-* `Variable#eslintImplicitGlobalSetting` (`"readonly" | "writable" | undefined`) ... The configured value in config files. This can be different from `variable.writeable` if there are `/* globals */` directive comments.
+- `Variable#writeable` (`boolean | undefined`) ... If `true`, this global variable can be assigned arbitrary value. If `false`, this global variable is read-only.
+- `Variable#eslintExplicitGlobal` (`boolean | undefined`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file.
+- `Variable#eslintExplicitGlobalComments` (`Comment[] | undefined`) ... The array of `/* globals */` directive comments which defined this global variable in the source code file. This property is `undefined` if there are no `/* globals */` directive comments.
+- `Variable#eslintImplicitGlobalSetting` (`"readonly" | "writable" | undefined`) ... The configured value in config files. This can be different from `variable.writeable` if there are `/* globals */` directive comments.
For examples of using `SourceCode#getScope()` to track variables, refer to the source code for the following built-in rules:
-* [no-shadow](https://github.com/eslint/eslint/blob/main/lib/rules/no-shadow.js): Calls `sourceCode.getScope()` at the `Program` node and inspects all child scopes to make sure a variable name is not reused at a lower scope. ([no-shadow](../rules/no-shadow) documentation)
-* [no-redeclare](https://github.com/eslint/eslint/blob/main/lib/rules/no-redeclare.js): Calls `sourceCode.getScope()` at each scope to make sure that a variable is not declared twice in the same scope. ([no-redeclare](../rules/no-redeclare) documentation)
+- [no-shadow](https://github.com/eslint/eslint/blob/main/lib/rules/no-shadow.js): Calls `sourceCode.getScope()` at the `Program` node and inspects all child scopes to make sure a variable name is not reused at a lower scope. ([no-shadow](../rules/no-shadow) documentation)
+- [no-redeclare](https://github.com/eslint/eslint/blob/main/lib/rules/no-redeclare.js): Calls `sourceCode.getScope()` at each scope to make sure that a variable is not declared twice in the same scope. ([no-redeclare](../rules/no-redeclare) documentation)
### Marking Variables as Used
-**Deprecated:** The `context.markVariableAsUsed()` method is deprecated in favor of `sourceCode.markVariableAsUsed()`.
-
Certain ESLint rules, such as [`no-unused-vars`](../rules/no-unused-vars), check to see if a variable has been used. ESLint itself only knows about the standard rules of variable access and so custom ways of accessing variables may not register as "used".
To help with this, you can use the `sourceCode.markVariableAsUsed()` method. This method takes two arguments: the name of the variable to mark as used and an option reference node indicating the scope in which you are working. Here's an example:
```js
module.exports = {
- create: function(context) {
- var sourceCode = context.sourceCode;
-
- return {
- ReturnStatement(node) {
-
- // look in the scope of the function for myCustomVar and mark as used
- sourceCode.markVariableAsUsed("myCustomVar", node);
-
- // or: look in the global scope for myCustomVar and mark as used
- sourceCode.markVariableAsUsed("myCustomVar");
- }
- }
- // ...
- }
+ create: function (context) {
+ var sourceCode = context.sourceCode;
+
+ return {
+ ReturnStatement(node) {
+ // look in the scope of the function for myCustomVar and mark as used
+ sourceCode.markVariableAsUsed("myCustomVar", node);
+
+ // or: look in the global scope for myCustomVar and mark as used
+ sourceCode.markVariableAsUsed("myCustomVar");
+ },
+ };
+ // ...
+ },
};
```
@@ -866,17 +942,16 @@ Here, the `myCustomVar` variable is marked as used relative to a `ReturnStatemen
### Accessing Code Paths
-ESLint analyzes code paths while traversing AST. You can access code path objects with five events related to code paths. For more information, refer to [Code Path Analysis](code-path-analysis).
+ESLint analyzes code paths while traversing AST. You can access code path objects with seven events related to code paths. For more information, refer to [Code Path Analysis](code-path-analysis).
### Deprecated `SourceCode` Methods
Please note that the following `SourceCode` methods have been deprecated and will be removed in a future version of ESLint:
-* `getComments()`: Replaced by `SourceCode#getCommentsBefore()`, `SourceCode#getCommentsAfter()`, and `SourceCode#getCommentsInside()`.
-* `getTokenOrCommentBefore()`: Replaced by `SourceCode#getTokenBefore()` with the `{ includeComments: true }` option.
-* `getTokenOrCommentAfter()`: Replaced by `SourceCode#getTokenAfter()` with the `{ includeComments: true }` option.
-* `isSpaceBetweenTokens()`: Replaced by `SourceCode#isSpaceBetween()`
-* `getJSDocComment()`
+- `getTokenOrCommentBefore()`: Replaced by `SourceCode#getTokenBefore()` with the `{ includeComments: true }` option.
+- `getTokenOrCommentAfter()`: Replaced by `SourceCode#getTokenAfter()` with the `{ includeComments: true }` option.
+- `isSpaceBetweenTokens()`: Replaced by `SourceCode#isSpaceBetween()`
+- `getJSDocComment()`
## Rule Unit Tests
@@ -926,3 +1001,5 @@ quotes | 18.066 | 100.0%
```
To see a longer list of results (more than 10), set the environment variable to another value such as `TIMING=50` or `TIMING=all`.
+
+For more granular timing information (per file per rule), use the [`stats`](./stats) option instead.
diff --git a/docs/src/extend/index.md b/docs/src/extend/index.md
index 815a42b457bd..85d5967f32ef 100644
--- a/docs/src/extend/index.md
+++ b/docs/src/extend/index.md
@@ -4,16 +4,15 @@ eleventyNavigation:
key: extend eslint
title: Extend ESLint
order: 2
-
---
This guide is intended for those who wish to extend the functionality of ESLint.
In order to extend ESLint, it's recommended that:
-* You know JavaScript, since ESLint is written in JavaScript.
-* You have some familiarity with Node.js, since ESLint runs on it.
-* You're comfortable with command-line programs.
+- You know JavaScript, since ESLint is written in JavaScript.
+- You have some familiarity with Node.js, since ESLint runs on it.
+- You're comfortable with command-line programs.
If that sounds like you, then continue reading to get started.
diff --git a/docs/src/extend/languages.md b/docs/src/extend/languages.md
new file mode 100644
index 000000000000..4588d8380711
--- /dev/null
+++ b/docs/src/extend/languages.md
@@ -0,0 +1,141 @@
+---
+title: Languages
+eleventyNavigation:
+ key: languages
+ parent: create plugins
+ title: Languages
+ order: 4
+---
+
+Starting with ESLint v9.7.0, you can extend ESLint with additional languages through plugins. While ESLint began as a linter strictly for JavaScript, the ESLint core is generic and can be used to lint any programming language. Each language is defined as an object that contains all of the parsing, evaluating, and traversal functionality required to lint a file. These languages are then distributed in plugins for use in user configurations.
+
+## Language Requirements
+
+In order to create a language, you need:
+
+1. **A parser.** The parser is the piece that converts plain text into a data structure. There is no specific format that ESLint requires the data structure to be in, so you can use any already-existing parser, or write your own.
+1. **A `SourceCode` object.** The way ESLint works with an AST is through a `SourceCode` object. There are some required methods on each `SourceCode`, and you can also add more methods or properties that you'd like to expose to rules.
+1. **A `Language` object.** The `Language` object contains information about the language itself along with methods for parsing and creating the `SourceCode` object.
+
+### Parser Requirements for Languages
+
+To get started, make sure you have a parser that can be called from JavaScript. The parser must return a data structure representing the code that was parsed. Most parsers return an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST) to represent the code, but they can also return a [concrete syntax tree](https://en.wikipedia.org/wiki/Parse_tree) (CST). Whether an AST or CST is returned doesn't matter to ESLint, it only matters that there is a data structure to traverse.
+
+While there is no specific structure an AST or CST must follow, it's easier to integrate with ESLint when each node in the tree contains the following information:
+
+1. **Type** - A property on each node representing the node type is required. For example, in JavaScript, the `type` property contains this information for each node. ESLint rules use node types to define the visitor methods, so it's important that each node can be identified by a string. The name of the property doesn't matter (discussed further below) so long as one exists. This property is typically named `type` or `kind` by most parsers.
+1. **Location** - A property on each node representing the location of the node in the original source code is required. The location must contain:
+
+ - The line on which the node starts
+ - The column on which the node starts
+ - The line on which the node ends
+ - The column on which the node ends
+
+ As with the node type, the property name doesn't matter. Two common property names are `loc` (as in [ESTree](https://github.com/estree/estree/blob/3851d4a6eae5e5473371893959b88b62007469e8/es5.md#node-objects)) and `position` (as in [Unist](https://github.com/syntax-tree/unist?tab=readme-ov-file#node)). This information is used by ESLint to report errors and rule violations.
+
+1. **Range** - A property on each node representing the location of the node's source inside the source code is required. The range indicates the index at which the first character is found and the index after the last character, such that calling `code.slice(start, end)` returns the text that the node represents. Once again, no specific property name is required, and this information may even be merged with location information. ESTree uses the `range` property while Unist includes this information on `position` along with the location information. This information is used by ESLint to apply autofixes.
+
+### The `SourceCode` Object
+
+ESLint holds information about source code in a `SourceCode` object. This object is the API used both by ESLint internally and by rules written to work on the code (via `context.sourceCode`). The `SourceCode` object must implement the `TextSourceCode` interface as defined in the [`@eslint/core`](https://npmjs.com/package/@eslint/core) package.
+
+A basic `SourceCode` object must implement the following:
+
+- `ast` - a property containing the AST or CST for the source code.
+- `text` - the text of the source code.
+- `getLoc(nodeOrToken)` - a method that returns the location of a given node or token. This must match the `loc` structure that ESTree uses.
+- `getRange(nodeOrToken)` - a method that returns the range of a given node or token. This must return an array where the first item is the start index and the second is the end index.
+- `traverse()` - a method that returns an iterable for traversing the AST or CST. The iterator must return objects that implement either `VisitTraversalStep` or `CallTraversalStep` from `@eslint/core`.
+
+The following optional members allow you to customize how ESLint interacts with the object:
+
+- `visitorKeys` - visitor keys that are specific to just this `SourceCode` object. Typically not necessary as `Language#visitorKeys` is used most of the time.
+- `applyLanguageOptions(languageOptions)` - if you have specific language options that need to be applied after parsing, you can do so in this method.
+- `getDisableDirectives()` - returns any disable directives in the code. ESLint uses this to apply disable directives and track unused directives.
+- `getInlineConfigNodes()` - returns any inline config nodes. ESLint uses this to report errors when `noInlineConfig` is enabled.
+- `applyInlineConfig()` - returns inline configuration elements to ESLint. ESLint uses this to alter the configuration of the file being linted.
+- `finalize()` - this method is called just before linting begins and is your last chance to modify `SourceCode`. If you've defined `applyLanguageOptions()` or `applyInlineConfig()`, then you may have additional changes to apply before the `SourceCode` object is ready.
+
+Additionally, the following members are common on `SourceCode` objects and are recommended to implement:
+
+- `lines` - the individual lines of the source code as an array of strings.
+- `getParent(node)` - returns the parent of the given node or `undefined` if the node is the root.
+- `getAncestors(node)` - returns an array of the ancestry of the node with the first item as the root of the tree and each subsequent item as the descendants of the root that lead to `node`.
+- `getText(node, beforeCount, afterCount)` - returns the string that represents the given node, and optionally, a specified number of characters before and after the node's range.
+
+See [`JSONSourceCode`](https://github.com/eslint/json/blob/main/src/languages/json-source-code.js) as an example of a basic `SourceCode` class.
+
+::: tip
+The [`@eslint/plugin-kit`](https://npmjs.com/package/@eslint/plugin-kit) package contains multiple classes that aim to make creating a `SourceCode` object easier. The `TextSourceCodeBase` class, in particular, implements the `TextSourceCode` interface and provides some basic functionality typically found in `SourceCode` objects.
+:::
+
+### The `Language` Object
+
+The `Language` object contains all of the information about the programming language as well as methods for interacting with code written in that language. ESLint uses this object to determine how to deal with a particular file. The `Language` object must implement the `Language` interface as defined in the [`@eslint/core`](https://npmjs.com/package/@eslint/core) package.
+
+A basic `Language` object must implement the following:
+
+- `fileType` - should be `"text"` (in the future, we will also support `"binary"`)
+- `lineStart` - either 0 or 1 to indicate how the AST represents the first line in the file. ESLint uses this to correctly display error locations.
+- `columnStart` - either 0 or 1 to indicate how the AST represents the first column in each line. ESLint uses this to correctly display error locations.
+- `nodeTypeKey` - the name of the property that indicates the node type (usually `"type"` or `"kind"`).
+- `validateLanguageOptions(languageOptions)` - validates language options for the language. This method is expected to throw a validation error when an expected language option doesn't have the correct type or value. Unexpected language options should be silently ignored and no error should be thrown. This method is required even if the language doesn't specify any options.
+- `parse(file, context)` - parses the given file into an AST or CST, and can also include additional values meant for use in rules. Called internally by ESLint.
+- `createSourceCode(file, parseResult, context)` - creates a `SourceCode` object. Call internally by ESLint after `parse()`, and the second argument is the exact return value from `parse()`.
+
+The following optional members allow you to customize how ESLint interacts with the object:
+
+- `visitorKeys` - visitor keys that are specific to the AST or CST. This is used to optimize traversal of the AST or CST inside of ESLint. While not required, it is strongly recommended, especially for AST or CST formats that deviate significantly from ESTree format.
+- `defaultLanguageOptions` - default `languageOptions` when the language is used. User-specified `languageOptions` are merged with this object when calculating the config for the file being linted.
+- `matchesSelectorClass(className, node, ancestry)` - allows you to specify selector classes, such as `:expression`, that match more than one node. This method is called whenever an [esquery](https://github.com/estools/esquery) selector contains a `:` followed by an identifier.
+- `normalizeLanguageOptions(languageOptions)` - takes a validated language options object and normalizes its values. This is helpful for backwards compatibility when language options properties change and also to add custom serialization with a `toJSON()` method.
+
+See [`JSONLanguage`](https://github.com/eslint/json/blob/main/src/languages/json-language.js) as an example of a basic `Language` class.
+
+## Publish a Language in a Plugin
+
+Languages are published in plugins similar to processors and rules. Define the `languages` key in your plugin as an object whose names are the language names and the values are the language objects. Here's an example:
+
+```js
+import { myLanguage } from "../languages/my.js";
+
+const plugin = {
+ // preferred location of name and version
+ meta: {
+ name: "eslint-plugin-example",
+ version: "1.2.3",
+ },
+ languages: {
+ my: myLanguage,
+ },
+ rules: {
+ // add rules here
+ },
+};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
+```
+
+In order to use a language from a plugin in a configuration file, import the plugin and include it in the `plugins` key, specifying a namespace. Then, use that namespace to reference the language in the `language` configuration, like this:
+
+```js
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import example from "eslint-plugin-example";
+
+export default defineConfig([
+ {
+ files: ["**/*.my"],
+ plugins: {
+ example,
+ },
+ language: "example/my",
+ },
+]);
+```
+
+See [Specify a Language](../use/configure/plugins#specify-a-language) in the Plugin Configuration documentation for more details.
diff --git a/docs/src/extend/plugin-migration-flat-config.md b/docs/src/extend/plugin-migration-flat-config.md
index 135e039c305f..bda8adf27dad 100644
--- a/docs/src/extend/plugin-migration-flat-config.md
+++ b/docs/src/extend/plugin-migration-flat-config.md
@@ -4,8 +4,7 @@ eleventyNavigation:
key: plugin flat config
parent: create plugins
title: Migration to Flat Config
- order: 4
-
+ order: 5
---
Beginning in ESLint v9.0.0, the default configuration system will be the new flat config system. In order for your plugins to work with flat config files, you'll need to make some changes to your existing plugins.
@@ -16,10 +15,10 @@ To make it easier to work with your plugin in the flat config system, it's recom
```js
const plugin = {
- meta: {},
- configs: {},
- rules: {},
- processors: {}
+ meta: {},
+ configs: {},
+ rules: {},
+ processors: {},
};
// for ESM
@@ -37,13 +36,13 @@ With the old eslintrc configuration system, ESLint could pull information about
```js
const plugin = {
- meta: {
- name: "eslint-plugin-example",
- version: "1.0.0"
- },
- configs: {},
- rules: {},
- processors: {}
+ meta: {
+ name: "eslint-plugin-example",
+ version: "1.0.0",
+ },
+ configs: {},
+ rules: {},
+ processors: {},
};
// for ESM
@@ -63,20 +62,21 @@ No changes are necessary for the `rules` key in your plugin. Everything works th
## Migrating Processors for Flat Config
-No changes are necessary for the `processors` key in your plugin as long as you aren't using file extension-named processors. If you have any [file extension-named processors](custom-processors#file-extension-named-processor), you must update the name to a valid identifier (numbers and letters). File extension-named processors were automatically applied in the old configuration system but are not automatically applied when using flat config. Here is an example of a file extension-named processor:
+Each processor should specify a `meta` object. For more information, see the [full documentation](custom-processors).
+
+No other changes are necessary for the `processors` key in your plugin as long as you aren't using file extension-named processors. If you have any [file extension-named processors](custom-processors-deprecated#file-extension-named-processor), you must update the name to a valid identifier (numbers and letters). File extension-named processors were automatically applied in the old configuration system but are not automatically applied when using flat config. Here is an example of a file extension-named processor:
```js
const plugin = {
- configs: {},
- rules: {},
- processors: {
-
- // no longer supported
- ".md": {
- preprocess() {},
- postprocess() {}
- }
- }
+ configs: {},
+ rules: {},
+ processors: {
+ // no longer supported
+ ".md": {
+ preprocess() {},
+ postprocess() {},
+ },
+ },
};
// for ESM
@@ -90,16 +90,15 @@ The name `".md"` is no longer valid for a processor, so it must be replaced with
```js
const plugin = {
- configs: {},
- rules: {},
- processors: {
-
- // works in both old and new config systems
- "markdown": {
- preprocess() {},
- postprocess() {}
- }
- }
+ configs: {},
+ rules: {},
+ processors: {
+ // works in both old and new config systems
+ markdown: {
+ preprocess() {},
+ postprocess() {},
+ },
+ },
};
// for ESM
@@ -112,16 +111,18 @@ module.exports = plugin;
In order to use this renamed processor, you'll also need to manually specify it inside of a config, such as:
```js
+import { defineConfig } from "eslint/config";
import example from "eslint-plugin-example";
-export default [
- {
- plugins: {
- example
- },
- processor: "example/markdown"
- }
-];
+export default defineConfig([
+ {
+ files: ["**/*.md"],
+ plugins: {
+ example,
+ },
+ processor: "example/markdown",
+ },
+]);
```
You should update your plugin's documentation to advise your users if you have renamed a file extension-named processor.
@@ -155,23 +156,23 @@ To migrate to flat config format, you'll need to move the configs to after the d
```js
const plugin = {
- configs: {},
- rules: {},
- processors: {}
+ configs: {},
+ rules: {},
+ processors: {},
};
// assign configs here so we can reference `plugin`
Object.assign(plugin.configs, {
- recommended: {
- plugins: {
- example: plugin
- },
- rules: {
- "example/rule1": "error",
- "example/rule2": "error"
- }
- }
-})
+ recommended: {
+ plugins: {
+ example: plugin,
+ },
+ rules: {
+ "example/rule1": "error",
+ "example/rule2": "error",
+ },
+ },
+});
// for ESM
export default plugin;
@@ -183,24 +184,48 @@ module.exports = plugin;
Your users can then use this exported config like this:
```js
+import { defineConfig } from "eslint/config";
import example from "eslint-plugin-example";
-export default [
+export default defineConfig([
+ // use recommended config and provide your own overrides
+ {
+ files: ["**/*.js"],
+ plugins: {
+ example,
+ },
+ extends: ["example/recommended"],
+ rules: {
+ "example/rule1": "warn",
+ },
+ },
+]);
+```
- // use recommended config
- example.configs.recommended,
+If your config extends other configs, you can export an array:
- // and provide your own overrides
- {
- rules: {
- "example/rule1": "warn"
- }
- }
-];
+```js
+const baseConfig = require("./base-config");
+
+module.exports = {
+ configs: {
+ extendedConfig: [
+ baseConfig,
+ {
+ rules: {
+ "example/rule1": "error",
+ "example/rule2": "error",
+ },
+ },
+ ],
+ },
+};
```
You should update your documentation so your plugin users know how to reference the exported configs.
+For more information, see the [full documentation](https://eslint.org/docs/latest/extend/plugins#configs-in-plugins).
+
## Migrating Environments for Flat Config
Environments are no longer supported in flat config, and so we recommend transitioning your environments into exported configs. For example, suppose you export a `mocha` environment like this:
@@ -229,24 +254,24 @@ To migrate this environment into a config, you need to add a new key in the `plu
```js
const plugin = {
- configs: {},
- rules: {},
- processors: {}
+ configs: {},
+ rules: {},
+ processors: {},
};
// assign configs here so we can reference `plugin`
Object.assign(plugin.configs, {
- mocha: {
- languageOptions: {
- globals: {
- it: "writeable",
- xit: "writeable",
- describe: "writeable",
- xdescribe: "writeable"
- }
- }
- }
-})
+ mocha: {
+ languageOptions: {
+ globals: {
+ it: "writeable",
+ xit: "writeable",
+ describe: "writeable",
+ xdescribe: "writeable",
+ },
+ },
+ },
+});
// for ESM
export default plugin;
@@ -258,22 +283,27 @@ module.exports = plugin;
Your users can then use this exported config like this:
```js
+import { defineConfig } from "eslint/config";
import example from "eslint-plugin-example";
-export default [
-
- // use the mocha globals
- example.configs.mocha,
-
- // and provide your own overrides
- {
- languageOptions: {
- globals: {
- it: "readonly"
- }
- }
- }
-];
+export default defineConfig([
+ {
+ files: ["**/tests/*.js"],
+ plugins: {
+ example,
+ },
+
+ // use the mocha globals
+ extends: ["example/mocha"],
+
+ // and provide your own overrides
+ languageOptions: {
+ globals: {
+ it: "readonly",
+ },
+ },
+ },
+]);
```
You should update your documentation so your plugin users know how to reference the exported configs.
@@ -288,6 +318,6 @@ If your plugin needs to work with both the old and new configuration systems, th
## Further Reading
-* [Overview of the flat config file format blog post](https://eslint.org/blog/2022/08/new-config-system-part-2/)
-* [API usage of new configuration system blog post](https://eslint.org/blog/2022/08/new-config-system-part-3/)
-* [Background to new configuration system blog post](https://eslint.org/blog/2022/08/new-config-system-part-1/)
+- [Overview of the flat config file format blog post](https://eslint.org/blog/2022/08/new-config-system-part-2/)
+- [API usage of new configuration system blog post](https://eslint.org/blog/2022/08/new-config-system-part-3/)
+- [Background to new configuration system blog post](https://eslint.org/blog/2022/08/new-config-system-part-1/)
diff --git a/docs/src/extend/plugins.md b/docs/src/extend/plugins.md
index 5374a0f50946..53c78b14ac7d 100644
--- a/docs/src/extend/plugins.md
+++ b/docs/src/extend/plugins.md
@@ -5,195 +5,388 @@ eleventyNavigation:
parent: extend eslint
title: Create Plugins
order: 2
-
---
-An ESLint plugin is an extension for ESLint that adds additional rules and configuration options. Plugins let you customize your ESLint configuration to enforce rules that are not included in the core ESLint package. Plugins can also provide additional environments, custom processors, and configurations.
-
-## Name a Plugin
+ESLint plugins extend ESLint with additional functionality. In most cases, you'll extend ESLint by creating plugins that encapsulate the additional functionality you want to share across multiple projects.
-Each plugin is an npm module with a name in the format of `eslint-plugin-`, such as `eslint-plugin-jquery`. You can also use scoped packages in the format of `@/eslint-plugin-` such as `@jquery/eslint-plugin-jquery` or even `@/eslint-plugin` such as `@jquery/eslint-plugin`.
+## Creating a plugin
-## Create a Plugin
+A plugin is a JavaScript object that exposes certain properties to ESLint:
-The easiest way to start creating a plugin is to use the [Yeoman generator](https://www.npmjs.com/package/generator-eslint). The generator will guide you through setting up the skeleton of a plugin.
-
-### Rules in Plugins
+- `meta` - information about the plugin.
+- `configs` - an object containing named configurations.
+- `rules` - an object containing the definitions of custom rules.
+- `processors` - an object containing named processors.
-Plugins can expose custom rules for use in ESLint. To do so, the plugin must export a `rules` object containing a key-value mapping of rule ID to rule. The rule ID does not have to follow any naming convention (so it can just be `dollar-sign`, for instance). To learn more about creating custom rules in plugins, refer to [Custom Rules](custom-rules).
+To get started, create a JavaScript file and export an object containing the properties you'd like ESLint to use. To make your plugin as easy to maintain as possible, we recommend that you format your plugin entrypoint file to look like this:
```js
-module.exports = {
- rules: {
- "dollar-sign": {
- create: function (context) {
- // rule implementation ...
- }
- }
- }
+const plugin = {
+ meta: {},
+ configs: {},
+ rules: {},
+ processors: {},
};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
```
-To use the rule in ESLint, you would use the unprefixed plugin name, followed by a slash, followed by the rule name. So if this plugin were named `eslint-plugin-myplugin`, then in your configuration you'd refer to the rule by the name `myplugin/dollar-sign`. Example: `"rules": {"myplugin/dollar-sign": 2}`.
+If you plan to distribute your plugin as an npm package, make sure that the module that exports the plugin object is the default export of your package. This will enable ESLint to import the plugin when it is specified in the command line in the [`--plugin` option](../use/command-line-interface#--plugin).
-### Environments in Plugins
+### Meta Data in Plugins
-Plugins can expose additional environments for use in ESLint. To do so, the plugin must export an `environments` object. The keys of the `environments` object are the names of the different environments provided and the values are the environment settings. For example:
+For easier debugging and more effective caching of plugins, it's recommended to provide a `name`, `version`, and `namespace` in a `meta` object at the root of your plugin, like this:
```js
-module.exports = {
- environments: {
- jquery: {
- globals: {
- $: false
- }
- }
- }
+const plugin = {
+ // preferred location of name and version
+ meta: {
+ name: "eslint-plugin-example",
+ version: "1.2.3",
+ namespace: "example",
+ },
+ rules: {
+ // add rules here
+ },
};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
```
-There's a `jquery` environment defined in this plugin. To use the environment in ESLint, you would use the unprefixed plugin name, followed by a slash, followed by the environment name. So if this plugin were named `eslint-plugin-myplugin`, then you would set the environment in your configuration to be `"myplugin/jquery"`.
+The `meta.name` property should match the npm package name for your plugin and the `meta.version` property should match the npm package version for your plugin. The `meta.namespace` property should match the prefix you'd like users to use for accessing the plugin's rules, processors, languages, and configs. The namespace is typically what comes after `eslint-plugin-` in your package name, which is why this example uses `"example"`. Providing a namespace allows the `defineConfig()` function to find your plugin even when a user assigns a different namespace in their config file.
-Plugin environments can define the following objects:
+The easiest way to add the name and version is by reading this information from your `package.json`, as in this example:
-1. `globals` - acts the same `globals` in a configuration file. The keys are the names of the globals and the values are `true` to allow the global to be overwritten and `false` to disallow.
-1. `parserOptions` - acts the same as `parserOptions` in a configuration file.
+```js
+import fs from "fs";
+
+const pkg = JSON.parse(
+ fs.readFileSync(new URL("./package.json", import.meta.url), "utf8"),
+);
+
+const plugin = {
+ // preferred location of name and version
+ meta: {
+ name: pkg.name,
+ version: pkg.version,
+ namespace: "example",
+ },
+ rules: {
+ // add rules here
+ },
+};
-### Processors in Plugins
+export default plugin;
+```
+
+::: tip
+While there are no restrictions on plugin names, it helps others to find your plugin on [npm](https://npmjs.com) when you follow these naming conventions:
+
+- **Unscoped:** If your npm package name won't be scoped (doesn't begin with `@`), then the plugin name should begin with `eslint-plugin-`, such as `eslint-plugin-example`.
+- **Scoped:** If your npm package name will be scoped, then the plugin name should be in the format of `@/eslint-plugin-` such as `@jquery/eslint-plugin-jquery` or even `@/eslint-plugin` such as `@jquery/eslint-plugin`.
-::: warning
-File extension-named processors are deprecated and only work in eslintrc-style configuration files. Flat config files do not automatically apply processors; you need to explicitly set the `processor` property.
:::
-You can add processors to plugins by including the processor functions in the `processors` key. For more information on defining custom processors, refer to [Custom Processors](custom-processors).
+As an alternative, you can also expose `name` and `version` properties at the root of your plugin, such as:
```js
-module.exports = {
- processors: {
- // This processor will be applied to `*.md` files automatically.
- ".md": {
- preprocess(text, filename) { /* ... */ },
- postprocess(messages, filename) { /* ... */ }
- }
- "processor-name": {
- preprocess: function(text, filename) {/* ... */},
-
- postprocess: function(messages, filename) { /* ... */ },
- }
- }
-}
+const plugin = {
+ // alternate location of name and version
+ name: "eslint-plugin-example",
+ version: "1.2.3",
+ rules: {
+ // add rules here
+ },
+};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
```
-### Configs in Plugins
+::: important
+While the `meta` object is the preferred way to provide the plugin name and version, this format is also acceptable and is provided for backward compatibility.
+:::
-You can bundle configurations inside a plugin by specifying them under the `configs` key. This can be useful when you want to bundle a set of custom rules with additional configuration. Multiple configurations are supported per plugin.
+### Rules in Plugins
-You can include individual rules from a plugin in a config that's also included in the plugin. In the config, you must specify your plugin name in the `plugins` array as well as any rules you want to enable that are part of the plugin. Any plugin rules must be prefixed with the short or long plugin name.
+Plugins can expose custom rules for use in ESLint. To do so, the plugin must export a `rules` object containing a key-value mapping of rule ID to rule. The rule ID does not have to follow any naming convention except that it should not contain a `/` character (so it can just be `dollar-sign` but not `foo/dollar-sign`, for instance). To learn more about creating custom rules in plugins, refer to [Custom Rules](custom-rules).
```js
-// eslint-plugin-myPlugin
-
-module.exports = {
- configs: {
- myConfig: {
- plugins: ["myPlugin"],
- env: ["browser"],
- rules: {
- semi: "error",
- "myPlugin/my-rule": "error",
- "eslint-plugin-myPlugin/another-rule": "error"
- }
- },
- myOtherConfig: {
- plugins: ["myPlugin"],
- env: ["node"],
- rules: {
- "myPlugin/my-rule": "off",
- "eslint-plugin-myPlugin/another-rule": "off",
- "eslint-plugin-myPlugin/yet-another-rule": "error"
- }
- }
- },
- rules: {
- "my-rule": {/* rule definition */},
- "another-rule": {/* rule definition */},
- "yet-another-rule": {/* rule definition */}
- }
+const plugin = {
+ meta: {
+ name: "eslint-plugin-example",
+ version: "1.2.3",
+ },
+ rules: {
+ "dollar-sign": {
+ create(context) {
+ // rule implementation ...
+ },
+ },
+ },
};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
```
-Plugins cannot force a specific configuration to be used. Users must manually include a plugin's configurations in their configuration file.
+In order to use a rule from a plugin in a configuration file, import the plugin and include it in the `plugins` key, specifying a namespace. Then, use that namespace to reference the rule in the `rules` configuration, like this:
-If the example plugin above were called `eslint-plugin-myPlugin`, the `myConfig` and `myOtherConfig` configurations would then be usable in a configuration file by extending `"plugin:myPlugin/myConfig"` and `"plugin:myPlugin/myOtherConfig"`, respectively.
+```js
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import example from "eslint-plugin-example";
+
+export default defineConfig([
+ {
+ plugins: {
+ example,
+ },
+ rules: {
+ "example/dollar-sign": "error",
+ },
+ },
+]);
+```
+
+::: warning
+Namespaces that don't begin with `@` may not contain a `/`; namespaces that begin with `@` may contain a `/`. For example, `eslint/plugin` is not a valid namespace but `@eslint/plugin` is valid. This restriction is for backwards compatibility with eslintrc plugin naming restrictions.
+:::
+
+### Processors in Plugins
+
+Plugins can expose [processors](custom-processors) for use in configuration file by providing a `processors` object. Similar to rules, each key in the `processors` object is the name of a processor and each value is the processor object itself. Here's an example:
-```json
-// .eslintrc.json
+```js
+const plugin = {
+ meta: {
+ name: "eslint-plugin-example",
+ version: "1.2.3",
+ },
+ processors: {
+ "processor-name": {
+ preprocess(text, filename) {
+ /* ... */
+ },
+ postprocess(messages, filename) {
+ /* ... */
+ },
+ },
+ },
+};
-{
- "extends": ["plugin:myPlugin/myConfig"]
-}
+// for ESM
+export default plugin;
+// OR for CommonJS
+module.exports = plugin;
```
-### Meta Data in Plugins
+In order to use a processor from a plugin in a configuration file, import the plugin and include it in the `plugins` key, specifying a namespace. Then, use that namespace to reference the processor in the `processor` configuration, like this:
+
+```js
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import example from "eslint-plugin-example";
+
+export default defineConfig([
+ {
+ files: ["**/*.txt"],
+ plugins: {
+ example,
+ },
+ processor: "example/processor-name",
+ },
+]);
+```
+
+### Configs in Plugins
-For easier debugging and more effective caching of plugins, it's recommended to provide a name and version in a `meta` object at the root of your plugin, like this:
+You can bundle configurations inside a plugin by specifying them under the `configs` key. This can be useful when you want to bundle a set of custom rules with a configuration that enables the recommended options. Multiple configurations are supported per plugin.
+
+You can include individual rules from a plugin in a config that's also included in the plugin. In the config, you must specify your plugin name in the `plugins` object as well as any rules you want to enable that are part of the plugin. Any plugin rules must be prefixed with the plugin namespace. Here's an example:
```js
-// preferred location of name and version
-module.exports = {
- meta: {
- name: "eslint-plugin-custom",
- version: "1.2.3"
- }
+const plugin = {
+ meta: {
+ name: "eslint-plugin-example",
+ version: "1.2.3",
+ },
+ configs: {},
+ rules: {
+ "dollar-sign": {
+ create(context) {
+ // rule implementation ...
+ },
+ },
+ },
};
+
+// assign configs here so we can reference `plugin`
+Object.assign(plugin.configs, {
+ recommended: [
+ {
+ plugins: {
+ example: plugin,
+ },
+ rules: {
+ "example/dollar-sign": "error",
+ },
+ languageOptions: {
+ globals: {
+ myGlobal: "readonly",
+ },
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ },
+ },
+ ],
+});
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
```
-The `meta.name` property should match the npm package name for your plugin and the `meta.version` property should match the npm package version for your plugin. The easiest way to accomplish this is by reading this information from your `package.json`.
+This plugin exports a `recommended` config that is an array with one config object. When there is just one config object, you can also export just the object without an enclosing array.
-As an alternative, you can also expose `name` and `version` properties at the root of your plugin, such as:
+In order to use a config from a plugin in a configuration file, import the plugin and use the `extends` key to reference the name of the config, like this:
```js
-// alternate location of name and version
-module.exports = {
- name: "eslint-plugin-custom",
- version: "1.2.3"
-};
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import example from "eslint-plugin-example";
+
+export default defineConfig([
+ {
+ files: ["**/*.js"], // any patterns you want to apply the config to
+ plugins: {
+ example,
+ },
+ extends: ["example/recommended"],
+ },
+]);
```
-While the `meta` object is the preferred way to provide the plugin name and version, this format is also acceptable and is provided for backward compatibility.
+::: important
+Plugins cannot force a specific configuration to be used. Users must manually include a plugin's configurations in their configuration file.
+:::
-### Peer Dependency
+#### Backwards Compatibility for Legacy Configs
-To make clear that the plugin requires ESLint to work correctly, you must declare ESLint as a peer dependency by mentioning it in the `peerDependencies` field of your plugin's `package.json`.
+If your plugin needs to export configs that work both with the current (flat config) system and the old (eslintrc) system, you can export both config types from the `configs` key. When exporting legacy configs, we recommend prefixing the name with `"legacy-"` (for example, `"legacy-recommended"`) to make it clear how the config should be used.
-Plugin support was introduced in ESLint version `0.8.0`. Ensure the `peerDependencies` points to ESLint `0.8.0` or later.
+If you're working on a plugin that has existed prior to ESLint v9.0.0, then you may already have legacy configs with names such as `"recommended"`. If you don't want to update the config name, you can also create an additional entry in the `configs` object prefixed with `"flat/"` (for example, `"flat/recommended"`). Here's an example:
-```json
-{
- "peerDependencies": {
- "eslint": ">=0.8.0"
- }
-}
+```js
+const plugin = {
+ meta: {
+ name: "eslint-plugin-example",
+ version: "1.2.3",
+ },
+ configs: {},
+ rules: {
+ "dollar-sign": {
+ create(context) {
+ // rule implementation ...
+ },
+ },
+ },
+};
+
+// assign configs here so we can reference `plugin`
+Object.assign(plugin.configs, {
+ // flat config format
+ "flat/recommended": [
+ {
+ plugins: {
+ example: plugin,
+ },
+ rules: {
+ "example/dollar-sign": "error",
+ },
+ languageOptions: {
+ globals: {
+ myGlobal: "readonly",
+ },
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ },
+ },
+ ],
+
+ // eslintrc format
+ recommended: {
+ plugins: ["example"],
+ rules: {
+ "example/dollar-sign": "error",
+ },
+ globals: {
+ myGlobal: "readonly",
+ },
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ },
+});
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
```
-## Testing
+With this approach, both configuration systems recognize `"recommended"`. The old config system uses the `recommended` key while the current config system uses the `flat/recommended` key. The `defineConfig()` helper first looks at the `recommended` key, and if that is not in the correct format, it looks for the `flat/recommended` key. This allows you an upgrade path if you'd later like to rename `flat/recommended` to `recommended` when you no longer need to support the old config system.
+
+## Testing a Plugin
ESLint provides the [`RuleTester`](../integrate/nodejs-api#ruletester) utility to make it easy to test the rules of your plugin.
-## Linting
+## Linting a Plugin
ESLint plugins should be linted too! It's suggested to lint your plugin with the `recommended` configurations of:
-* [eslint](https://www.npmjs.com/package/eslint)
-* [eslint-plugin-eslint-plugin](https://www.npmjs.com/package/eslint-plugin-eslint-plugin)
-* [eslint-plugin-n](https://www.npmjs.com/package/eslint-plugin-n)
+- [eslint](https://www.npmjs.com/package/eslint)
+- [eslint-plugin-eslint-plugin](https://www.npmjs.com/package/eslint-plugin-eslint-plugin)
+- [eslint-plugin-n](https://www.npmjs.com/package/eslint-plugin-n)
## Share Plugins
-In order to make your plugin available to the community you have to publish it on npm.
+In order to make your plugin available publicly, you have to publish it on npm. When doing so, please be sure to:
+
+1. **List ESLint as a peer dependency.** Because plugins are intended for use with ESLint, it's important to add the `eslint` package as a peer dependency. To do so, manually edit your `package.json` file to include a `peerDependencies` block, like this:
-To make it easy for others to find your plugin, add these [keywords](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#keywords) to your `package.json` file:
+ ```json
+ {
+ "peerDependencies": {
+ "eslint": ">=9.0.0"
+ }
+ }
+ ```
-* `eslint`
-* `eslintplugin`
+2. **Specify keywords.** ESLint plugins should specify `eslint`, `eslintplugin` and `eslint-plugin` as [keywords](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#keywords) in your `package.json` file.
diff --git a/docs/src/extend/rule-deprecation.md b/docs/src/extend/rule-deprecation.md
new file mode 100644
index 000000000000..c6f542264a73
--- /dev/null
+++ b/docs/src/extend/rule-deprecation.md
@@ -0,0 +1,52 @@
+---
+title: Rule Deprecation
+---
+
+The rule deprecation metadata describes whether a rule is deprecated and how the rule can be replaced if there is a replacement.
+The legacy format used the two top-level [rule meta](./custom-rules#rule-structure) properties `deprecated` (as a boolean only) and `replacedBy`.
+In the new format, `deprecated` is a boolean or an object of type `DeprecatedInfo`, and `replacedBy` should be defined inside `deprecated` instead of at the top-level.
+
+## â DeprecatedInfo type
+
+This type represents general information about a rule deprecation.
+Every property is optional.
+
+- `message` (`string`)
+ A general message presentable to the user. May contain why this rule is deprecated or how to replace the rule.
+- `url` (`string`)
+ An URL with more information about this rule deprecation.
+- `replacedBy` (`ReplacedByInfo[]`)
+ Information about the available replacements for the rule.
+ This may be an empty array to explicitly state there is no replacement.
+- `deprecatedSince` (`string`)
+ [Semver](https://semver.org/) of the version deprecating the rule.
+- `availableUntil` (`string | null`)
+ [Semver](https://semver.org/) of the version likely to remove the rule, e.g. the next major version.
+ The special value `null` means the rule will no longer be changed but will be kept available.
+
+## â ReplacedByInfo type
+
+The type describes a single possible replacement of a rule.
+Every property is optional.
+
+- `message` (`string`)
+ A general message about this rule replacement, e.g.
+- `url` (`string`)
+ An URL with more information about this rule replacement.
+- `plugin` (`ExternalSpecifier`)
+ Specifies which plugin has the replacement rule.
+ The name should be the package name and should be "eslint" if the replacement is an ESLint core rule.
+ This property should be omitted if the replacement is in the same plugin.
+- `rule` (`ExternalSpecifier`)
+ Specifies the replacement rule.
+ May be omitted if the plugin only contains a single rule or has the same name as the rule.
+
+### â ExternalSpecifier type
+
+This type represents an external resource.
+Every property is optional.
+
+- `name` (`string`)
+ The package name for `plugin` and the rule id for `rule`.
+- `url` (`string`)
+ An URL pointing to documentation for the plugin / rule..
diff --git a/docs/src/extend/scope-manager-interface.md b/docs/src/extend/scope-manager-interface.md
index 60e26431c0c9..612a6dc4f5bb 100644
--- a/docs/src/extend/scope-manager-interface.md
+++ b/docs/src/extend/scope-manager-interface.md
@@ -1,11 +1,10 @@
---
title: ScopeManager
-
---
-This document was written based on the implementation of [eslint-scope](https://github.com/eslint/eslint-scope), a fork of [escope](https://github.com/estools/escope), and deprecates some members ESLint is not using.
+This document was written based on the implementation of [eslint-scope](https://github.com/eslint/js/tree/main/packages/eslint-scope), a fork of [escope](https://github.com/estools/escope), and deprecates some members ESLint is not using.
-----
+---
## ScopeManager interface
@@ -15,30 +14,30 @@ This document was written based on the implementation of [eslint-scope](https://
#### scopes
-* **Type:** `Scope[]`
-* **Description:** All scopes.
+- **Type:** `Scope[]`
+- **Description:** All scopes.
#### globalScope
-* **Type:** `Scope`
-* **Description:** The root scope.
+- **Type:** `Scope`
+- **Description:** The root scope.
### Methods
#### acquire(node, inner = false)
-* **Parameters:**
- * `node` (`ASTNode`) ... An AST node to get their scope.
- * `inner` (`boolean`) ... If the node has multiple scope, this returns the outermost scope normally. If `inner` is `true` then this returns the innermost scope. Default is `false`.
-* **Return type:** `Scope | null`
-* **Description:** Get the scope of a given AST node. The gotten scope's `block` property is the node. This method never returns `function-expression-name` scope. If the node does not have their scope, this returns `null`.
+- **Parameters:**
+ - `node` (`ASTNode`) ... An AST node to get their scope.
+ - `inner` (`boolean`) ... If the node has multiple scope, this returns the outermost scope normally. If `inner` is `true` then this returns the innermost scope. Default is `false`.
+- **Return type:** `Scope | null`
+- **Description:** Get the scope of a given AST node. The gotten scope's `block` property is the node. This method never returns `function-expression-name` scope. If the node does not have their scope, this returns `null`.
#### getDeclaredVariables(node)
-* **Parameters:**
- * `node` (`ASTNode`) ... An AST node to get their variables.
-* **Return type:** `Variable[]`
-* **Description:** Get the variables that a given AST node defines. The gotten variables' `def[].node`/`def[].parent` property is the node. If the node does not define any variable, this returns an empty array.
+- **Parameters:**
+ - `node` (`ASTNode`) ... An AST node to get their variables.
+- **Return type:** `Variable[]`
+- **Description:** Get the variables that a given AST node defines. The gotten variables' `def[].node`/`def[].parent` property is the node. If the node does not define any variable, this returns an empty array.
### Deprecated members
@@ -46,30 +45,30 @@ Those members are defined but not used in ESLint.
#### isModule()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this program is module.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this program is module.
#### isImpliedStrict()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this program is strict mode implicitly. I.e., `options.impliedStrict === true`.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this program is strict mode implicitly. I.e., `options.impliedStrict === true`.
#### isStrictModeSupported()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this program supports strict mode. I.e., `options.ecmaVersion >= 5`.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this program supports strict mode. I.e., `options.ecmaVersion >= 5`.
#### acquireAll(node)
-* **Parameters:**
- * `node` (`ASTNode`) ... An AST node to get their scope.
-* **Return type:** `Scope[] | null`
-* **Description:** Get the scopes of a given AST node. The gotten scopes' `block` property is the node. If the node does not have their scope, this returns `null`.
+- **Parameters:**
+ - `node` (`ASTNode`) ... An AST node to get their scope.
+- **Return type:** `Scope[] | null`
+- **Description:** Get the scopes of a given AST node. The gotten scopes' `block` property is the node. If the node does not have their scope, this returns `null`.
-----
+---
## Scope interface
@@ -79,64 +78,80 @@ Those members are defined but not used in ESLint.
#### type
-* **Type:** `string`
-* **Description:** The type of this scope. This is one of `"block"`, `"catch"`, `"class"`, `"class-field-initializer"`, `"class-static-block"`, `"for"`, `"function"`, `"function-expression-name"`, `"global"`, `"module"`, `"switch"`, `"with"`.
+- **Type:** `string`
+- **Description:** The type of this scope. This is one of `"block"`, `"catch"`, `"class"`, `"class-field-initializer"`, `"class-static-block"`, `"for"`, `"function"`, `"function-expression-name"`, `"global"`, `"module"`, `"switch"`, `"with"`.
#### isStrict
-* **Type:** `boolean`
-* **Description:** `true` if this scope is strict mode.
+- **Type:** `boolean`
+- **Description:** `true` if this scope is strict mode.
#### upper
-* **Type:** `Scope | null`
-* **Description:** The parent scope. If this is the global scope then this property is `null`.
+- **Type:** `Scope | null`
+- **Description:** The parent scope. If this is the global scope then this property is `null`.
#### childScopes
-* **Type:** `Scope[]`
-* **Description:** The array of child scopes. This does not include grandchild scopes.
+- **Type:** `Scope[]`
+- **Description:** The array of child scopes. This does not include grandchild scopes.
#### variableScope
-* **Type:** `Scope`
-* **Description:** The nearest ancestor whose `type` is one of `"class-field-initializer"`, `"class-static-block"`, `"function"`, `"global"`, or `"module"`. For the aforementioned scopes this is a self-reference.
+- **Type:** `Scope`
+- **Description:** The nearest ancestor whose `type` is one of `"class-field-initializer"`, `"class-static-block"`, `"function"`, `"global"`, or `"module"`. For the aforementioned scopes this is a self-reference.
> This represents the lowest enclosing function or top-level scope. Class field initializers and class static blocks are implicit functions. Historically, this was the scope which hosts variables that are defined by `var` declarations, and thus the name `variableScope`.
#### block
-* **Type:** `ASTNode`
-* **Description:** The AST node which created this scope.
+- **Type:** `ASTNode`
+- **Description:** The AST node which created this scope.
#### variables
-* **Type:** `Variable[]`
-* **Description:** The array of all variables which are defined on this scope. This does not include variables which are defined in child scopes.
+- **Type:** `Variable[]`
+- **Description:** The array of all variables which are defined on this scope. This does not include variables which are defined in child scopes.
#### set
-* **Type:** `Map`
-* **Description:** The map from variable names to variable objects.
-
-> I hope to rename `set` field or replace by a method.
+- **Type:** `Map`
+- **Description:** The map from variable names to variable objects.
#### references
-* **Type:** `Reference[]`
-* **Description:** The array of all references on this scope. This does not include references in child scopes.
+- **Type:** `Reference[]`
+- **Description:** The array of all references on this scope. This does not include references in child scopes.
#### through
-* **Type:** `Reference[]`
-* **Description:** The array of references which could not be resolved in this scope.
+- **Type:** `Reference[]`
+- **Description:** The array of references which could not be resolved in this scope.
#### functionExpressionScope
-* **Type:** `boolean`
-* **Description:** `true` if this scope is `"function-expression-name"` scope.
+- **Type:** `boolean`
+- **Description:** `true` if this scope is `"function-expression-name"` scope.
+
+#### implicit
+
+This field exists only in the root `Scope` object (the global scope). It provides information about implicit global variables. Implicit global variables are variables that are neither built-in nor explicitly declared, but created implicitly by assigning values to undeclared variables in non-strict code. `Variable` objects for these variables are not present in the root `Scope` object's fields `variables` and `set`.
+
+The value of the `implicit` field is an object with two properties.
+
+##### variables
-> I hope to deprecate `functionExpressionScope` field as replacing by `scope.type === "function-expression-name"`.
+- **Type:** `Variable[]`
+- **Description:** The array of all implicit global variables.
+
+##### set
+
+- **Type:** `Map`
+- **Description:** The map from variable names to variable objects for implicit global variables.
+
+::: tip
+In `Variable` objects that represent implicit global variables, `references` is always an empty array. You can find references to these variables in the `through` field of the root `Scope` object (the global scope), among other unresolved references.
+:::
### Deprecated members
@@ -144,57 +159,57 @@ Those members are defined but not used in ESLint.
#### taints
-* **Type:** `Map`
-* **Description:** The map from variable names to `tainted` flag.
+- **Type:** `Map`
+- **Description:** The map from variable names to `tainted` flag.
#### dynamic
-* **Type:** `boolean`
-* **Description:** `true` if this scope is dynamic. I.e., the type of this scope is `"global"` or `"with"`.
+- **Type:** `boolean`
+- **Description:** `true` if this scope is dynamic. I.e., the type of this scope is `"global"` or `"with"`.
#### directCallToEvalScope
-* **Type:** `boolean`
-* **Description:** `true` if this scope contains `eval()` invocations.
+- **Type:** `boolean`
+- **Description:** `true` if this scope contains `eval()` invocations.
#### thisFound
-* **Type:** `boolean`
-* **Description:** `true` if this scope contains `this`.
+- **Type:** `boolean`
+- **Description:** `true` if this scope contains `this`.
#### resolve(node)
-* **Parameters:**
- * `node` (`ASTNode`) ... An AST node to get their reference object. The type of the node must be `"Identifier"`.
-* **Return type:** `Reference | null`
-* **Description:** Returns `this.references.find(r => r.identifier === node)`.
+- **Parameters:**
+ - `node` (`ASTNode`) ... An AST node to get their reference object. The type of the node must be `"Identifier"`.
+- **Return type:** `Reference | null`
+- **Description:** Returns `this.references.find(r => r.identifier === node)`.
#### isStatic()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** Returns `!this.dynamic`.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** Returns `!this.dynamic`.
#### isArgumentsMaterialized()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this is a `"function"` scope which has used `arguments` variable.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this is a `"function"` scope which has used `arguments` variable.
#### isThisMaterialized()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** Returns `this.thisFound`.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** Returns `this.thisFound`.
#### isUsedName(name)
-* **Parameters:**
- * `name` (`string`) ... The name to check.
-* **Return type:** `boolean`
-* **Description:** `true` if a given name is used in variable names or reference names.
+- **Parameters:**
+ - `name` (`string`) ... The name to check.
+- **Return type:** `boolean`
+- **Description:** `true` if a given name is used in variable names or reference names.
-----
+---
## Variable interface
@@ -204,30 +219,28 @@ Those members are defined but not used in ESLint.
#### name
-* **Type:** `string`
-* **Description:** The name of this variable.
+- **Type:** `string`
+- **Description:** The name of this variable.
#### scope
-* **Type:** `Scope`
-* **Description:** The scope in which this variable is defined.
+- **Type:** `Scope`
+- **Description:** The scope in which this variable is defined.
#### identifiers
-* **Type:** `ASTNode[]`
-* **Description:** The array of `Identifier` nodes which define this variable. If this variable is redeclared, this array includes two or more nodes.
-
-> I hope to deprecate `identifiers` field as replacing by `defs[].name` field.
+- **Type:** `ASTNode[]`
+- **Description:** The array of `Identifier` nodes which define this variable. If this variable is redeclared, this array includes two or more nodes.
#### references
-* **Type:** `Reference[]`
-* **Description:** The array of the references of this variable.
+- **Type:** `Reference[]`
+- **Description:** The array of the references of this variable.
#### defs
-* **Type:** `Definition[]`
-* **Description:** The array of the definitions of this variable.
+- **Type:** `Definition[]`
+- **Description:** The array of the definitions of this variable.
### Deprecated members
@@ -235,15 +248,15 @@ Those members are defined but not used in ESLint.
#### tainted
-* **Type:** `boolean`
-* **Description:** The `tainted` flag. (always `false`)
+- **Type:** `boolean`
+- **Description:** The `tainted` flag. (always `false`)
#### stack
-* **Type:** `boolean`
-* **Description:** The `stack` flag. (I'm not sure what this means.)
+- **Type:** `boolean`
+- **Description:** The `stack` flag. (I'm not sure what this means.)
-----
+---
## Reference interface
@@ -253,60 +266,60 @@ Those members are defined but not used in ESLint.
#### identifier
-* **Type:** `ASTNode`
-* **Description:** The `Identifier` node of this reference.
+- **Type:** `ASTNode`
+- **Description:** The `Identifier` node of this reference.
#### from
-* **Type:** `Scope`
-* **Description:** The `Scope` object that this reference is on.
+- **Type:** `Scope`
+- **Description:** The `Scope` object that this reference is on.
#### resolved
-* **Type:** `Variable | null`
-* **Description:** The `Variable` object that this reference refers. If such variable was not defined, this is `null`.
+- **Type:** `Variable | null`
+- **Description:** The `Variable` object that this reference refers. If such variable was not defined, this is `null`.
#### writeExpr
-* **Type:** `ASTNode | null`
-* **Description:** The ASTNode object which is right-hand side.
+- **Type:** `ASTNode | null`
+- **Description:** The ASTNode object which is right-hand side.
#### init
-* **Type:** `boolean`
-* **Description:** `true` if this writing reference is a variable initializer or a default value.
+- **Type:** `boolean`
+- **Description:** `true` if this writing reference is a variable initializer or a default value.
### Methods
#### isWrite()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this reference is writing.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this reference is writing.
#### isRead()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this reference is reading.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this reference is reading.
#### isWriteOnly()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this reference is writing but not reading.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this reference is writing but not reading.
#### isReadOnly()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this reference is reading but not writing.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this reference is reading but not writing.
#### isReadWrite()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this reference is reading and writing.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this reference is reading and writing.
### Deprecated members
@@ -314,26 +327,26 @@ Those members are defined but not used in ESLint.
#### tainted
-* **Type:** `boolean`
-* **Description:** The `tainted` flag. (always `false`)
+- **Type:** `boolean`
+- **Description:** The `tainted` flag. (always `false`)
#### flag
-* **Type:** `number`
-* **Description:** `1` is reading, `2` is writing, `3` is reading/writing.
+- **Type:** `number`
+- **Description:** `1` is reading, `2` is writing, `3` is reading/writing.
#### partial
-* **Type:** `boolean`
-* **Description:** The `partial` flag.
+- **Type:** `boolean`
+- **Description:** The `partial` flag.
#### isStatic()
-* **Parameters:**
-* **Return type:** `boolean`
-* **Description:** `true` if this reference is resolved statically.
+- **Parameters:**
+- **Return type:** `boolean`
+- **Description:** `true` if this reference is resolved statically.
-----
+---
## Definition interface
@@ -343,43 +356,43 @@ Those members are defined but not used in ESLint.
#### type
-* **Type:** `string`
-* **Description:** The type of this definition. One of `"CatchClause"`, `"ClassName"`, `"FunctionName"`, `"ImplicitGlobalVariable"`, `"ImportBinding"`, `"Parameter"`, and `"Variable"`.
+- **Type:** `string`
+- **Description:** The type of this definition. One of `"CatchClause"`, `"ClassName"`, `"FunctionName"`, `"ImplicitGlobalVariable"`, `"ImportBinding"`, `"Parameter"`, and `"Variable"`.
#### name
-* **Type:** `ASTNode`
-* **Description:** The `Identifier` node of this definition.
+- **Type:** `ASTNode`
+- **Description:** The `Identifier` node of this definition.
#### node
-* **Type:** `ASTNode`
-* **Description:** The enclosing node of the name.
+- **Type:** `ASTNode`
+- **Description:** The enclosing node of the name.
-| type | node |
-|:---------------------------|:-----|
-| `"CatchClause"` | `CatchClause`
-| `"ClassName"` | `ClassDeclaration` or `ClassExpression`
-| `"FunctionName"` | `FunctionDeclaration` or `FunctionExpression`
-| `"ImplicitGlobalVariable"` | `Program`
-| `"ImportBinding"` | `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier`
-| `"Parameter"` | `FunctionDeclaration`, `FunctionExpression`, or `ArrowFunctionExpression`
-| `"Variable"` | `VariableDeclarator`
+| type | node |
+| :------------------------- | :------------------------------------------------------------------------- |
+| `"CatchClause"` | `CatchClause` |
+| `"ClassName"` | `ClassDeclaration` or `ClassExpression` |
+| `"FunctionName"` | `FunctionDeclaration` or `FunctionExpression` |
+| `"ImplicitGlobalVariable"` | `AssignmentExpression` or `ForInStatement` or `ForOfStatement` |
+| `"ImportBinding"` | `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier` |
+| `"Parameter"` | `FunctionDeclaration`, `FunctionExpression`, or `ArrowFunctionExpression` |
+| `"Variable"` | `VariableDeclarator` |
#### parent
-* **Type:** `ASTNode | undefined | null`
-* **Description:** The enclosing statement node of the name.
+- **Type:** `ASTNode | undefined | null`
+- **Description:** The enclosing statement node of the name.
-| type | parent |
-|:---------------------------|:-------|
-| `"CatchClause"` | `null`
-| `"ClassName"` | `null`
-| `"FunctionName"` | `null`
-| `"ImplicitGlobalVariable"` | `null`
-| `"ImportBinding"` | `ImportDeclaration`
-| `"Parameter"` | `null`
-| `"Variable"` | `VariableDeclaration`
+| type | parent |
+| :------------------------- | :-------------------- |
+| `"CatchClause"` | `null` |
+| `"ClassName"` | `null` |
+| `"FunctionName"` | `null` |
+| `"ImplicitGlobalVariable"` | `null` |
+| `"ImportBinding"` | `ImportDeclaration` |
+| `"Parameter"` | `null` |
+| `"Variable"` | `VariableDeclaration` |
### Deprecated members
@@ -387,10 +400,10 @@ Those members are defined but not used in ESLint.
#### index
-* **Type:** `number | undefined | null`
-* **Description:** The index in the declaration statement.
+- **Type:** `number | undefined | null`
+- **Description:** The index in the declaration statement.
#### kind
-* **Type:** `string | undefined | null`
-* **Description:** The kind of the declaration statement.
+- **Type:** `string | undefined | null`
+- **Description:** The kind of the declaration statement.
diff --git a/docs/src/extend/selectors.md b/docs/src/extend/selectors.md
index 5ea700dead10..f622ed80ced0 100644
--- a/docs/src/extend/selectors.md
+++ b/docs/src/extend/selectors.md
@@ -1,6 +1,5 @@
---
title: Selectors
-
---
Some rules and APIs allow the use of selectors to query an AST. This page is intended to:
@@ -30,24 +29,24 @@ Selectors are not limited to matching against single node types. For example, th
The following selectors are supported:
-* AST node type: `ForStatement`
-* wildcard (matches all nodes): `*`
-* attribute existence: `[attr]`
-* attribute value: `[attr="foo"]` or `[attr=123]`
-* attribute regex: `[attr=/foo.*/]` (with some [known issues](#known-issues))
-* attribute conditions: `[attr!="foo"]`, `[attr>2]`, `[attr<3]`, `[attr>=2]`, or `[attr<=3]`
-* nested attribute: `[attr.level2="foo"]`
-* field: `FunctionDeclaration > Identifier.id`
-* First or last child: `:first-child` or `:last-child`
-* nth-child (no ax+b support): `:nth-child(2)`
-* nth-last-child (no ax+b support): `:nth-last-child(1)`
-* descendant: `FunctionExpression ReturnStatement`
-* child: `UnaryExpression > Literal`
-* following sibling: `VariableDeclaration ~ VariableDeclaration`
-* adjacent sibling: `ArrayExpression > Literal + SpreadElement`
-* negation: `:not(ForStatement)`
-* matches-any: `:matches([attr] > :first-child, :last-child)`
-* class of AST node: `:statement`, `:expression`, `:declaration`, `:function`, or `:pattern`
+- AST node type: `ForStatement`
+- wildcard (matches all nodes): `*`
+- attribute existence: `[attr]`
+- attribute value: `[attr="foo"]` or `[attr=123]`
+- attribute regex: `[attr=/foo.*/]` (with some [known issues](#known-issues))
+- attribute conditions: `[attr!="foo"]`, `[attr>2]`, `[attr<3]`, `[attr>=2]`, or `[attr<=3]`
+- nested attribute: `[attr.level2="foo"]`
+- field: `FunctionDeclaration > Identifier.id`
+- First or last child: `:first-child` or `:last-child`
+- nth-child (no ax+b support): `:nth-child(2)`
+- nth-last-child (no ax+b support): `:nth-last-child(1)`
+- descendant: `FunctionExpression ReturnStatement`
+- child: `UnaryExpression > Literal`
+- following sibling: `VariableDeclaration ~ VariableDeclaration`
+- adjacent sibling: `ArrayExpression > Literal + SpreadElement`
+- negation: `:not(ForStatement)`
+- matches-any: `:matches([attr] > :first-child, :last-child)`
+- class of AST node: `:statement`, `:expression`, `:declaration`, `:function`, or `:pattern`
This syntax is very powerful, and can be used to precisely select many syntactic patterns in your code.
@@ -63,22 +62,23 @@ When writing a custom ESLint rule, you can listen for nodes that match a particu
```js
module.exports = {
- create(context) {
- // ...
-
- return {
-
- // This listener will be called for all IfStatement nodes with blocks.
- "IfStatement > BlockStatement": function(blockStatementNode) {
- // ...your logic here
- },
-
- // This listener will be called for all function declarations with more than 3 parameters.
- "FunctionDeclaration[params.length>3]": function(functionDeclarationNode) {
- // ...your logic here
- }
- };
- }
+ create(context) {
+ // ...
+
+ return {
+ // This listener will be called for all IfStatement nodes with blocks.
+ "IfStatement > BlockStatement": function (blockStatementNode) {
+ // ...your logic here
+ },
+
+ // This listener will be called for all function declarations with more than 3 parameters.
+ "FunctionDeclaration[params.length>3]": function (
+ functionDeclarationNode,
+ ) {
+ // ...your logic here
+ },
+ };
+ },
};
```
@@ -86,8 +86,8 @@ Adding `:exit` to the end of a selector will cause the listener to be called whe
If two or more selectors match the same node, their listeners will be called in order of increasing specificity. The specificity of an AST selector is similar to the specificity of a CSS selector:
-* When comparing two selectors, the selector that contains more class selectors, attribute selectors, and pseudo-class selectors (excluding `:not()`) has higher specificity.
-* If the class/attribute/pseudo-class count is tied, the selector that contains more node type selectors has higher specificity.
+- When comparing two selectors, the selector that contains more class selectors, attribute selectors, and pseudo-class selectors (excluding `:not()`) has higher specificity.
+- If the class/attribute/pseudo-class count is tied, the selector that contains more node type selectors has higher specificity.
If multiple selectors have equal specificity, their listeners will be called in alphabetical order for that node.
@@ -97,9 +97,12 @@ With the [no-restricted-syntax](../rules/no-restricted-syntax) rule, you can res
```json
{
- "rules": {
- "no-restricted-syntax": ["error", "IfStatement > :not(BlockStatement).consequent"]
- }
+ "rules": {
+ "no-restricted-syntax": [
+ "error",
+ "IfStatement > :not(BlockStatement).consequent"
+ ]
+ }
}
```
@@ -107,9 +110,12 @@ With the [no-restricted-syntax](../rules/no-restricted-syntax) rule, you can res
```json
{
- "rules": {
- "no-restricted-syntax": ["error", "IfStatement[consequent.type!='BlockStatement']"]
- }
+ "rules": {
+ "no-restricted-syntax": [
+ "error",
+ "IfStatement[consequent.type!='BlockStatement']"
+ ]
+ }
}
```
@@ -117,9 +123,12 @@ As another example, you can disallow calls to `require()`:
```json
{
- "rules": {
- "no-restricted-syntax": ["error", "CallExpression[callee.name='require']"]
- }
+ "rules": {
+ "no-restricted-syntax": [
+ "error",
+ "CallExpression[callee.name='require']"
+ ]
+ }
}
```
@@ -127,9 +136,12 @@ Or you can enforce that calls to `setTimeout` always have two arguments:
```json
{
- "rules": {
- "no-restricted-syntax": ["error", "CallExpression[callee.name='setTimeout'][arguments.length!=2]"]
- }
+ "rules": {
+ "no-restricted-syntax": [
+ "error",
+ "CallExpression[callee.name='setTimeout'][arguments.length!=2]"
+ ]
+ }
}
```
@@ -143,9 +155,12 @@ For example, the following configuration disallows importing from `some/path`:
```json
{
- "rules": {
- "no-restricted-syntax": ["error", "ImportDeclaration[source.value=/^some\\u002Fpath$/]"]
- }
+ "rules": {
+ "no-restricted-syntax": [
+ "error",
+ "ImportDeclaration[source.value=/^some\\u002Fpath$/]"
+ ]
+ }
}
```
diff --git a/docs/src/extend/shareable-configs-deprecated.md b/docs/src/extend/shareable-configs-deprecated.md
new file mode 100644
index 000000000000..3dc180d6a6f5
--- /dev/null
+++ b/docs/src/extend/shareable-configs-deprecated.md
@@ -0,0 +1,233 @@
+---
+title: Share Configurations (Deprecated)
+---
+
+::: warning
+This documentation is for shareable configs using the deprecated eslintrc configuration format. [View the updated documentation](shareable-configs).
+:::
+
+To share your ESLint configuration, create a **shareable config**. You can publish your shareable config on [npm](https://www.npmjs.com/) so that others can download and use it in their ESLint projects.
+
+This page explains how to create and publish a shareable config.
+
+## Creating a Shareable Config
+
+Shareable configs are simply npm packages that export a configuration object. To start, [create a Node.js module](https://docs.npmjs.com/getting-started/creating-node-modules) like you normally would.
+
+The module name must take one of the following forms:
+
+- Begin with `eslint-config-`, such as `eslint-config-myconfig`.
+- Be an npm [scoped module](https://docs.npmjs.com/misc/scope). To create a scoped module, name or prefix the module with `@scope/eslint-config`, such as `@scope/eslint-config` or `@scope/eslint-config-myconfig`.
+
+In your module, export the shareable config from the module's [`main`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#main) entry point file. The default main entry point is `index.js`. For example:
+
+```js
+// index.js
+module.exports = {
+ globals: {
+ MyGlobal: true,
+ },
+
+ rules: {
+ semi: [2, "always"],
+ },
+};
+```
+
+Since the `index.js` file is just JavaScript, you can read these settings from a file or generate them dynamically.
+
+## Publishing a Shareable Config
+
+Once your shareable config is ready, you can [publish it to npm](https://docs.npmjs.com/getting-started/publishing-npm-packages) to share it with others. We recommend using the `eslint` and `eslintconfig` [keywords](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#keywords) in the `package.json` file so others can easily find your module.
+
+You should declare your dependency on ESLint in the `package.json` using the [peerDependencies](https://docs.npmjs.com/files/package.json#peerdependencies) field. The recommended way to declare a dependency for future-proof compatibility is with the ">=" range syntax, using the lowest required ESLint version. For example:
+
+```json
+{
+ "peerDependencies": {
+ "eslint": ">= 3"
+ }
+}
+```
+
+If your shareable config depends on a plugin, you should also specify it as a `peerDependency` (plugins will be loaded relative to the end user's project, so the end user is required to install the plugins they need). However, if your shareable config depends on a [custom parser](custom-parsers) or another shareable config, you can specify these packages as `dependencies` in the `package.json`.
+
+You can also test your shareable config on your computer before publishing by linking your module globally. Type:
+
+```bash
+npm link
+```
+
+Then, in your project that wants to use your shareable config, type:
+
+```bash
+npm link eslint-config-myconfig
+```
+
+Be sure to replace `eslint-config-myconfig` with the actual name of your module.
+
+## Using a Shareable Config
+
+To use a shareable config, include the config name in the `extends` field of a configuration file. For the value, use your module name. For example:
+
+```json
+{
+ "extends": "eslint-config-myconfig"
+}
+```
+
+You can also omit the `eslint-config-` and it is automatically assumed by ESLint:
+
+```json
+{
+ "extends": "myconfig"
+}
+```
+
+You cannot use shareable configs with the ESLint CLI [`--config`](../use/command-line-interface#-c---config) flag.
+
+### npm Scoped Modules
+
+npm [scoped modules](https://docs.npmjs.com/misc/scope) are also supported in a number of ways.
+
+You can use the module name:
+
+```json
+{
+ "extends": "@scope/eslint-config"
+}
+```
+
+You can also omit the `eslint-config` and it is automatically assumed by ESLint:
+
+```json
+{
+ "extends": "@scope"
+}
+```
+
+The module name can also be customized. For example, if you have a package named `@scope/eslint-config-myconfig`, the configuration can be specified as:
+
+```json
+{
+ "extends": "@scope/eslint-config-myconfig"
+}
+```
+
+You could also omit `eslint-config` to specify the configuration as:
+
+```json
+{
+ "extends": "@scope/myconfig"
+}
+```
+
+### Overriding Settings from Shareable Configs
+
+You can override settings from the shareable config by adding them directly into your `.eslintrc` file.
+
+## Sharing Multiple Configs
+
+You can share multiple configs in the same npm package. Specify a default config for the package by following the directions in the [Creating a Shareable Config](#creating-a-shareable-config) section. You can specify additional shareable configs by adding a new file to your npm package and then referencing it from your ESLint config.
+
+As an example, you can create a file called `my-special-config.js` in the root of your npm package and export a config, such as:
+
+```js
+// my-special-config.js
+module.exports = {
+ rules: {
+ quotes: [2, "double"],
+ },
+};
+```
+
+Then, assuming you're using the package name `eslint-config-myconfig`, you can access the additional config via:
+
+```json
+{
+ "extends": "myconfig/my-special-config"
+}
+```
+
+When using [scoped modules](https://docs.npmjs.com/misc/scope) it is not possible to omit the `eslint-config` namespace. Doing so would result in resolution errors as explained above. Assuming the package name is `@scope/eslint-config`, the additional config can be accessed as:
+
+```json
+{
+ "extends": "@scope/eslint-config/my-special-config"
+}
+```
+
+Note that you can leave off the `.js` from the filename.
+
+**Important:** We strongly recommend always including a default config for your plugin to avoid errors.
+
+## Local Config File Resolution
+
+If you need to make multiple configs that can extend each other and live in different directories, you can create a single shareable config that handles this scenario.
+
+As an example, let's assume you're using the package name `eslint-config-myconfig` and your package looks something like this:
+
+```text
+myconfig
+âââ index.js
+âââŦ lib
+ âââ defaults.js
+ âââ dev.js
+ âââ ci.js
+ âââŦ ci
+ âââ frontend.js
+ âââ backend.js
+ âââ common.js
+```
+
+In the `index.js` file, you can do something like this:
+
+```js
+module.exports = require("./lib/ci.js");
+```
+
+Now inside the package you have `/lib/defaults.js`, which contains:
+
+```js
+module.exports = {
+ rules: {
+ "no-console": 1,
+ },
+};
+```
+
+Inside `/lib/ci.js` you have:
+
+```js
+module.exports = require("./ci/backend");
+```
+
+Inside `/lib/ci/common.js`:
+
+```js
+module.exports = {
+ rules: {
+ "no-alert": 2,
+ },
+ extends: "myconfig/lib/defaults",
+};
+```
+
+Despite being in an entirely different directory, you'll see that all `extends` must use the full package path to the config file you wish to extend.
+
+Now inside `/lib/ci/backend.js`:
+
+```js
+module.exports = {
+ rules: {
+ "no-console": 1,
+ },
+ extends: "myconfig/lib/ci/common",
+};
+```
+
+In the last file, once again see that to properly resolve your config, you need to include the full package path.
+
+## Further Reading
+
+- [npm Developer Guide](https://docs.npmjs.com/misc/developers)
diff --git a/docs/src/extend/shareable-configs.md b/docs/src/extend/shareable-configs.md
index 60b35321efd4..8ecc0477f600 100644
--- a/docs/src/extend/shareable-configs.md
+++ b/docs/src/extend/shareable-configs.md
@@ -5,40 +5,49 @@ eleventyNavigation:
parent: extend eslint
title: Share Configurations
order: 3
-
---
To share your ESLint configuration, create a **shareable config**. You can publish your shareable config on [npm](https://www.npmjs.com/) so that others can download and use it in their ESLint projects.
This page explains how to create and publish a shareable config.
+::: tip
+This page explains how to create a shareable config using the flat config format. For the deprecated eslintrc format, [see the deprecated documentation](shareable-configs-deprecated).
+:::
+
## Creating a Shareable Config
-Shareable configs are simply npm packages that export a configuration object. To start, [create a Node.js module](https://docs.npmjs.com/getting-started/creating-node-modules) like you normally would.
+Shareable configs are simply npm packages that export a configuration object or array. To start, [create a Node.js module](https://docs.npmjs.com/getting-started/creating-node-modules) like you normally would.
-The module name must take one of the following forms:
+While you can name the package in any way that you'd like, we recommend using one of the following conventions to make your package easier to identify:
-* Begin with `eslint-config-`, such as `eslint-config-myconfig`.
-* Be an npm [scoped module](https://docs.npmjs.com/misc/scope). To create a scoped module, name or prefix the module with `@scope/eslint-config`, such as `@scope/eslint-config` or `@scope/eslint-config-myconfig`.
+- Begin with `eslint-config-`, such as `eslint-config-myconfig`.
+- For an npm [scoped module](https://docs.npmjs.com/misc/scope), name or prefix the module with `@scope/eslint-config`, such as `@scope/eslint-config` or `@scope/eslint-config-myconfig`.
In your module, export the shareable config from the module's [`main`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#main) entry point file. The default main entry point is `index.js`. For example:
```js
// index.js
-module.exports = {
-
- globals: {
- MyGlobal: true
- },
+export default [
+ {
+ languageOptions: {
+ globals: {
+ MyGlobal: true,
+ },
+ },
- rules: {
- semi: [2, "always"]
- }
-
-};
+ rules: {
+ semi: [2, "always"],
+ },
+ },
+];
```
-Since the `index.js` file is just JavaScript, you can read these settings from a file or generate them dynamically.
+Because the `index.js` file is just JavaScript, you can read these settings from a file or generate them dynamically.
+
+::: tip
+Most of the time, you'll want to export an array of config objects from your shareable config. However, you can also export a single config object. Make sure your documentation clearly shows an example of how to use your shareable config inside of an `eslint.config.js` file to avoid user confusion.
+:::
## Publishing a Shareable Config
@@ -48,190 +57,97 @@ You should declare your dependency on ESLint in the `package.json` using the [pe
```json
{
- "peerDependencies": {
- "eslint": ">= 3"
- }
+ "peerDependencies": {
+ "eslint": ">= 9"
+ }
}
```
-If your shareable config depends on a plugin, you should also specify it as a `peerDependency` (plugins will be loaded relative to the end user's project, so the end user is required to install the plugins they need). However, if your shareable config depends on a [custom parser](custom-parsers) or another shareable config, you can specify these packages as `dependencies` in the `package.json`.
-
-You can also test your shareable config on your computer before publishing by linking your module globally. Type:
-
-```bash
-npm link
-```
-
-Then, in your project that wants to use your shareable config, type:
-
-```bash
-npm link eslint-config-myconfig
-```
-
-Be sure to replace `eslint-config-myconfig` with the actual name of your module.
+If your shareable config depends on a plugin or a custom parser, you should specify these packages as `dependencies` in your `package.json`.
## Using a Shareable Config
-To use a shareable config, include the config name in the `extends` field of a configuration file. For the value, use your module name. For example:
-
-```json
-{
- "extends": "eslint-config-myconfig"
-}
-```
-
-You can also omit the `eslint-config-` and it is automatically assumed by ESLint:
-
-```json
-{
- "extends": "myconfig"
-}
-```
-
-You cannot use shareable configs with the ESLint CLI [`--config`](../use/command-line-interface#-c---config) flag.
-
-### npm Scoped Modules
-
-npm [scoped modules](https://docs.npmjs.com/misc/scope) are also supported in a number of ways.
+To use a shareable config, import the package inside of an `eslint.config.js` file and add it into the exported array using `extends`, like this:
-You can use the module name:
+```js
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import myconfig from "eslint-config-myconfig";
-```json
-{
- "extends": "@scope/eslint-config"
-}
+export default defineConfig([
+ {
+ files: ["**/*.js"],
+ extends: [myconfig],
+ },
+]);
```
-You can also omit the `eslint-config` and it is automatically assumed by ESLint:
+::: warning
+It's not possible to use shareable configs with the ESLint CLI [`--config`](../use/command-line-interface#-c---config) flag.
+:::
-```json
-{
- "extends": "@scope"
-}
-```
+### Overriding Settings from Shareable Configs
-The module name can also be customized. For example, if you have a package named `@scope/eslint-config-myconfig`, the configuration can be specified as:
+You can override settings from the shareable config by adding them directly into your `eslint.config.js` file after importing the shareable config. For example:
-```json
-{
- "extends": "@scope/eslint-config-myconfig"
-}
-```
+```js
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import myconfig from "eslint-config-myconfig";
-You could also omit `eslint-config` to specify the configuration as:
+export default defineConfig([
+ {
+ files: ["**/*.js"],
+ extends: [myconfig],
-```json
-{
- "extends": "@scope/myconfig"
-}
+ // anything from here will override myconfig
+ rules: {
+ "no-unused-vars": "warn",
+ },
+ },
+]);
```
-### Overriding Settings from Shareable Configs
-
-You can override settings from the shareable config by adding them directly into your `.eslintrc` file.
-
## Sharing Multiple Configs
-You can share multiple configs in the same npm package. Specify a default config for the package by following the directions in the [Creating a Shareable Config](#creating-a-shareable-config) section. You can specify additional shareable configs by adding a new file to your npm package and then referencing it from your ESLint config.
+Because shareable configs are just npm packages, you can export as many configs as you'd like from the same package. In addition to specifying a default config using the `main` entry in your `package.json`, you can specify additional shareable configs by adding a new file to your npm package and then referencing it from your `eslint.config.js` file.
As an example, you can create a file called `my-special-config.js` in the root of your npm package and export a config, such as:
```js
// my-special-config.js
-module.exports = {
- rules: {
- quotes: [2, "double"]
- }
+export default {
+ rules: {
+ quotes: [2, "double"],
+ },
};
```
Then, assuming you're using the package name `eslint-config-myconfig`, you can access the additional config via:
-```json
-{
- "extends": "myconfig/my-special-config"
-}
-```
-
-When using [scoped modules](https://docs.npmjs.com/misc/scope) it is not possible to omit the `eslint-config` namespace. Doing so would result in resolution errors as explained above. Assuming the package name is `@scope/eslint-config`, the additional config can be accessed as:
-
-```json
-{
- "extends": "@scope/eslint-config/my-special-config"
-}
-```
-
-Note that you can leave off the `.js` from the filename.
-
-**Important:** We strongly recommend always including a default config for your plugin to avoid errors.
-
-## Local Config File Resolution
-
-If you need to make multiple configs that can extend each other and live in different directories, you can create a single shareable config that handles this scenario.
-
-As an example, let's assume you're using the package name `eslint-config-myconfig` and your package looks something like this:
-
-```text
-myconfig
-âââ index.js
-âââŦ lib
- âââ defaults.js
- âââ dev.js
- âââ ci.js
- âââŦ ci
- âââ frontend.js
- âââ backend.js
- âââ common.js
-```
-
-In the `index.js` file, you can do something like this:
-
```js
-module.exports = require('./lib/ci.js');
-```
+// eslint.config.js
+import { defineConfig } from "eslint/config";
+import myconfig from "eslint-config-myconfig";
+import mySpecialConfig from "eslint-config-myconfig/my-special-config.js";
-Now inside the package you have `/lib/defaults.js`, which contains:
+export default defineConfig([
+ {
+ files: ["**/*.js"],
+ extends: [myconfig, mySpecialConfig],
-```js
-module.exports = {
- rules: {
- 'no-console': 1
- }
-};
-```
-
-Inside `/lib/ci.js` you have:
-
-```js
-module.exports = require('./ci/backend');
-```
-
-Inside `/lib/ci/common.js`:
-
-```js
-module.exports = {
- rules: {
- 'no-alert': 2
- },
- extends: 'myconfig/lib/defaults'
-};
-```
-
-Despite being in an entirely different directory, you'll see that all `extends` must use the full package path to the config file you wish to extend.
-
-Now inside `/lib/ci/backend.js`:
-
-```js
-module.exports = {
- rules: {
- 'no-console': 1
- },
- extends: 'myconfig/lib/ci/common'
-};
+ // anything from here will override myconfig
+ rules: {
+ "no-unused-vars": "warn",
+ },
+ },
+]);
```
-In the last file, once again see that to properly resolve your config, you need to include the full package path.
+::: important
+We strongly recommend always including a default export for your package to avoid confusion.
+:::
## Further Reading
-* [npm Developer Guide](https://docs.npmjs.com/misc/developers)
+- [npm Developer Guide](https://docs.npmjs.com/misc/developers)
diff --git a/docs/src/extend/stats.md b/docs/src/extend/stats.md
new file mode 100644
index 000000000000..e39d944ada89
--- /dev/null
+++ b/docs/src/extend/stats.md
@@ -0,0 +1,142 @@
+---
+title: Stats Data
+eleventyNavigation:
+ key: stats data
+ parent: extend eslint
+ title: Stats Data
+ order: 6
+---
+
+{%- from 'components/npx_tabs.macro.html' import npx_tabs %}
+
+While an analysis of the overall rule performance for an ESLint run can be carried out by setting the [TIMING](./custom-rules#profile-rule-performance) environment variable, it can sometimes be useful to acquire more _granular_ timing data (lint time per file per rule) or collect other measures of interest. In particular, when developing new [custom plugins](./plugins) and evaluating/benchmarking new languages or rule sets. For these use cases, you can optionally collect runtime statistics from ESLint.
+
+## Enable stats collection
+
+To enable collection of statistics, you can either:
+
+1. Use the `--stats` CLI option. This will pass the stats data into the formatter used to output results from ESLint. (Note: not all formatters output stats data.)
+1. Set `stats: true` as an option on the `ESLint` constructor.
+
+Enabling stats data adds a new `stats` key to each [LintResult](../integrate/nodejs-api#-lintresult-type) object containing data such as parse times, fix times, lint times per rule.
+
+As such, it is not available via stdout but made easily ingestible via a formatter using the CLI or via the Node.js API to cater to your specific needs.
+
+## â Stats type
+
+The `Stats` value is the timing information of each lint run. The `stats` property of the [LintResult](../integrate/nodejs-api#-lintresult-type) type contains it. It has the following properties:
+
+- `fixPasses` (`number`)
+ The number of times ESLint has applied at least one fix after linting.
+- `times` (`{ passes: TimePass[] }`)
+ The times spent on (parsing, fixing, linting) a file, where the linting refers to the timing information for each rule.
+ - `TimePass` (`{ parse: ParseTime, rules?: Record, fix: FixTime, total: number }`)
+ An object containing the times spent on (parsing, fixing, linting)
+ - `ParseTime` (`{ total: number }`)
+ The total time that is spent when parsing a file.
+ - `RuleTime` (`{ total: number }`)
+ The total time that is spent on a rule.
+ - `FixTime` (`{ total: number }`)
+ The total time that is spent on applying fixes to the code.
+
+### CLI usage
+
+Let's consider the following example:
+
+```js [file-to-fix.js]
+/*eslint no-regex-spaces: "error", wrap-regex: "error"*/
+
+function a() {
+ return / foo/.test("bar");
+}
+```
+
+Run ESLint with `--stats` and output to JSON via the built-in [`json` formatter](../use/formatters/):
+
+{{ npx_tabs({
+ package: "eslint",
+ args: ["file-to-fix.js", "--fix", "--stats", "-f", "json"]
+}) }}
+
+This yields the following `stats` entry as part of the formatted lint results object:
+
+```json
+{
+ "times": {
+ "passes": [
+ {
+ "parse": {
+ "total": 3.975959
+ },
+ "rules": {
+ "no-regex-spaces": {
+ "total": 0.160792
+ },
+ "wrap-regex": {
+ "total": 0.422626
+ }
+ },
+ "fix": {
+ "total": 0.080208
+ },
+ "total": 12.765959
+ },
+ {
+ "parse": {
+ "total": 0.623542
+ },
+ "rules": {
+ "no-regex-spaces": {
+ "total": 0.043084
+ },
+ "wrap-regex": {
+ "total": 0.007959
+ }
+ },
+ "fix": {
+ "total": 0
+ },
+ "total": 1.148875
+ }
+ ]
+ },
+ "fixPasses": 1
+}
+```
+
+Note, that for the simple example above, the sum of all rule times should be directly comparable to the first column of the TIMING output. Running the same command with `TIMING=all`, you can verify this:
+
+```bash
+$ TIMING=all npx eslint file-to-fix.js --fix --stats -f json
+...
+Rule | Time (ms) | Relative
+:---------------|----------:|--------:
+wrap-regex | 0.431 | 67.9%
+no-regex-spaces | 0.204 | 32.1%
+```
+
+### API Usage
+
+You can achieve the same thing using the Node.js API by passing`stats: true` as an option to the `ESLint` constructor. For example:
+
+```js
+const { ESLint } = require("eslint");
+
+(async function main() {
+ // 1. Create an instance.
+ const eslint = new ESLint({ stats: true, fix: true });
+
+ // 2. Lint files.
+ const results = await eslint.lintFiles(["file-to-fix.js"]);
+
+ // 3. Format the results.
+ const formatter = await eslint.loadFormatter("json");
+ const resultText = formatter.format(results);
+
+ // 4. Output it.
+ console.log(resultText);
+})().catch(error => {
+ process.exitCode = 1;
+ console.error(error);
+});
+```
diff --git a/docs/src/extend/ways-to-extend.md b/docs/src/extend/ways-to-extend.md
index 3bf8314e1afa..2cadfd50737d 100644
--- a/docs/src/extend/ways-to-extend.md
+++ b/docs/src/extend/ways-to-extend.md
@@ -15,7 +15,7 @@ This page explains the ways to extend ESLint, and how these extensions all fit t
Plugins let you add your own ESLint custom rules and custom processors to a project. You can publish a plugin as an npm module.
-Plugins are useful because your project may require some ESLint configuration that isn't included in the core `eslint` package. For example, if you're using a frontend JavaScript library like React or framework like Vue, these tools have some features that require custom rules outside the scope of the ESLint core rules.
+Plugins are useful because your project may require some ESLint configuration that isn't included in the core `eslint` package. For example, if you're using a frontend JavaScript library like [React](https://react.dev/) or framework like [Vue](https://vuejs.org/), these tools have some features that require custom rules outside the scope of the ESLint core rules.
Often a plugin is paired with a configuration for ESLint that applies a set of features from the plugin to a project. You can include configurations in a plugin as well.
@@ -23,9 +23,9 @@ For example, [`eslint-plugin-react`](https://www.npmjs.com/package/eslint-plugin
To learn more about creating the extensions you can include in a plugin, refer to the following documentation:
-* [Custom Rules](custom-rules)
-* [Custom Processors](custom-processors)
-* [Configs in Plugins](plugins#configs-in-plugins)
+- [Custom Rules](custom-rules)
+- [Custom Processors](custom-processors)
+- [Configs in Plugins](plugins#configs-in-plugins)
To learn more about bundling these extensions into a plugin, refer to [Plugins](plugins).
@@ -35,15 +35,15 @@ ESLint shareable configs are pre-defined configurations for ESLint that you can
You can either publish a shareable config independently or as part of a plugin.
-For example, a popular shareable config is [eslint-config-airbnb](https://www.npmjs.com/package/eslint-config-airbnb), which contains a variety of rules in addition to some [parser options](../use/configure/language-options#specifying-parser-options). This is a set of rules for ESLint that is designed to match the style guide used by the [Airbnb JavaScript style guide](https://github.com/airbnb/javascript). By using the `eslint-config-airbnb` shareable config, you can automatically enforce the Airbnb style guide in your project without having to manually configure each rule.
+For example, a popular shareable config is [`eslint-config-airbnb`](https://www.npmjs.com/package/eslint-config-airbnb), which contains a variety of rules in addition to some [parser options](../use/configure/language-options#specifying-parser-options). This is a set of rules for ESLint that is designed to match the style guide used by the [Airbnb JavaScript style guide](https://github.com/airbnb/javascript). By using the `eslint-config-airbnb` shareable config, you can automatically enforce the Airbnb style guide in your project without having to manually configure each rule.
-To learn more about creating a shareable config, refer to [Share Configuration](shareable-configs).
+To learn more about creating a shareable config, refer to [Share Configurations](shareable-configs).
## Custom Formatters
Custom formatters take ESLint linting results and output the results in a format that you define. Custom formatters let you display linting results in a format that best fits your needs, whether that's in a specific file format, a certain display style, or a format optimized for a particular tool. You only need to create a custom formatter if the [built-in formatters](../use/formatters/) don't serve your use case.
-For example, the custom formatter [eslint-formatter-gitlab](https://www.npmjs.com/package/eslint-formatter-gitlab) can be used to display ESLint results in GitLab code quality reports.
+For example, the custom formatter [eslint-formatter-gitlab](https://www.npmjs.com/package/eslint-formatter-gitlab) can be used to display ESLint results in [GitLab](https://about.gitlab.com/) code quality reports.
To learn more about creating a custom formatter, refer to [Custom Formatters](custom-formatters).
@@ -55,6 +55,6 @@ ESLint ships with a built-in JavaScript parser (Espree), but custom parsers allo
For example, the custom parser [@typescript-eslint/parser](https://typescript-eslint.io/packages/parser) extends ESLint to lint TypeScript code.
-Custom parsers **cannot** be included in a plugin, unlike the other extension types.
+Custom parsers can be also included in a plugin.
To learn more about creating a custom parser, refer to [Custom Parsers](custom-parsers).
diff --git a/docs/src/integrate/index.md b/docs/src/integrate/index.md
index 19164a585308..3b241ddaff98 100644
--- a/docs/src/integrate/index.md
+++ b/docs/src/integrate/index.md
@@ -4,15 +4,14 @@ eleventyNavigation:
key: integrate eslint
title: integrate ESLint
order: 3
-
---
This guide is intended for those who wish to integrate the functionality of ESLint into other applications by using the ESLint API.
In order to integrate ESLint, it's recommended that:
-* You know JavaScript since ESLint is written in JavaScript.
-* You have some familiarity with Node.js since ESLint runs on it.
+- You know JavaScript since ESLint is written in JavaScript.
+- You have some familiarity with Node.js since ESLint runs on it.
If that sounds like you, then continue reading to get started.
diff --git a/docs/src/integrate/integration-tutorial.md b/docs/src/integrate/integration-tutorial.md
index 08d77c7253b8..691075574926 100644
--- a/docs/src/integrate/integration-tutorial.md
+++ b/docs/src/integrate/integration-tutorial.md
@@ -7,25 +7,44 @@ eleventyNavigation:
order: 1
---
-This guide walks you through integrating the `ESLint` class to lint files and retrieve results, which can be useful for creating integrations with other projects.
+{%- from 'components/npm_tabs.macro.html' import npm_tabs with context %}
+
+This guide walks you through integrating the `ESLint` class to lint files and
+retrieve results, which can be useful for creating integrations with other
+projects.
## Why Create an Integration?
-You might want to create an ESLint integration if you're creating developer tooling, such as the following:
+You might want to create an ESLint integration if you're creating developer
+tooling, such as the following:
-* **Code editors and IDEs**: Integrating ESLint with code editors and IDEs can provide real-time feedback on code quality and automatically highlight potential issues as you type. Many editors already have ESLint plugins available, but you may need to create a custom integration if the existing plugins do not meet your specific requirements.
+- **Code editors and IDEs**: Integrating ESLint with code editors and IDEs can
+ provide real-time feedback on code quality and automatically highlight
+ potential issues as you type. Many editors already have ESLint plugins
+ available, but you may need to create a custom integration if the existing
+ plugins do not meet your specific requirements.
-* **Custom linter tools**: If you're building a custom linter tool that combines multiple linters or adds specific functionality, you may want to integrate ESLint into your tool to provide JavaScript linting capabilities.
+- **Custom linter tools**: If you're building a custom linter tool that combines
+ multiple linters or adds specific functionality, you may want to integrate
+ ESLint into your tool to provide JavaScript linting capabilities.
-* **Code review tools**: Integrating ESLint with code review tools can help automate the process of identifying potential issues in the codebase.
+- **Code review tools**: Integrating ESLint with code review tools can help
+ automate the process of identifying potential issues in the codebase.
-* **Learning platforms**: If you are developing a learning platform or coding tutorial, integrating ESLint can provide real-time feedback to users as they learn JavaScript, helping them improve their coding skills and learn best practices.
+- **Learning platforms**: If you are developing a learning platform or coding
+ tutorial, integrating ESLint can provide real-time feedback to users as they
+ learn JavaScript, helping them improve their coding skills and learn best
+ practices.
-* **Developer tool integration**: If you're creating or extending a developer tool, such as a bundler or testing framework, you may want to integrate ESLint to provide linting capabilities. You can integrate ESLint directly into the tool or as a plugin.
+- **Developer tool integration**: If you're creating or extending a developer
+ tool, such as a bundler or testing framework, you may want to integrate ESLint
+ to provide linting capabilities. You can integrate ESLint directly into the
+ tool or as a plugin.
## What You'll Build
-In this guide, you'll create a simple Node.js project that uses the `ESLint` class to lint files and retrieve results.
+In this guide, you'll create a simple Node.js project that uses the `ESLint`
+class to lint files and retrieve results.
## Requirements
@@ -33,9 +52,9 @@ This tutorial assumes you are familiar with JavaScript and Node.js.
To follow this tutorial, you'll need to have the following:
-* Node.js (v12.22.0 or higher)
-* npm
-* A text editor
+- Node.js (`^18.18.0`, `^20.9.0`, or `>=21.1.0`)
+- npm
+- A text editor
## Step 1: Setup
@@ -48,15 +67,19 @@ cd eslint-integration
Initialize the project with a `package.json` file:
-```shell
-npm init -y
-```
+{{ npm_tabs({
+ command: "init",
+ packages: [],
+ args: ["-y"]
+}) }}
Install the `eslint` package as a dependency (**not** as a dev dependency):
-```shell
-npm install eslint
-```
+{{ npm_tabs({
+ command: "install",
+ packages: ["eslint"],
+ args: []
+}) }}
Create a new file called `example-eslint-integration.js` in the project root:
@@ -68,7 +91,8 @@ touch example-eslint-integration.js
Import the `ESLint` class from the `eslint` package and create a new instance.
-You can customize the ESLint configuration by passing an options object to the `ESLint` constructor:
+You can customize the ESLint configuration by passing an options object to the
+`ESLint` constructor:
```javascript
// example-eslint-integration.js
@@ -76,16 +100,25 @@ You can customize the ESLint configuration by passing an options object to the `
const { ESLint } = require("eslint");
// Create an instance of ESLint with the configuration passed to the function
-function createESLintInstance(overrideConfig){
- return new ESLint({ useEslintrc: false, overrideConfig: overrideConfig, fix: true });
+function createESLintInstance(overrideConfig) {
+ return new ESLint({
+ overrideConfigFile: true,
+ overrideConfig,
+ fix: true,
+ });
}
```
## Step 3: Lint and Fix Files
-To lint a file, use the `lintFiles` method of the `ESLint` instance. The `filePaths` argument passed to `ESLint#lintFiles()` can be a string or an array of strings, representing the file path(s) you want to lint. The file paths can be globs or filenames.
+To lint a file, use the `lintFiles` method of the `ESLint` instance. The
+`filePaths` argument passed to `ESLint#lintFiles()` can be a string or an array
+of strings, representing the file path(s) you want to lint. The file paths can
+be globs or filenames.
-The static method `ESLint.outputFixes()` takes the linting results from the call to `ESLint#lintFiles()`, and then writes the fixed code back to the source files.
+The static method `ESLint.outputFixes()` takes the linting results from the call
+to `ESLint#lintFiles()`, and then writes the fixed code back to the source
+files.
```javascript
// example-eslint-integration.js
@@ -94,18 +127,20 @@ The static method `ESLint.outputFixes()` takes the linting results from the call
// Lint the specified files and return the results
async function lintAndFix(eslint, filePaths) {
- const results = await eslint.lintFiles(filePaths);
+ const results = await eslint.lintFiles(filePaths);
- // Apply automatic fixes and output fixed code
- await ESLint.outputFixes(results);
+ // Apply automatic fixes and output fixed code
+ await ESLint.outputFixes(results);
- return results;
+ return results;
}
```
## Step 4: Output Results
-Define a function to output the linting results to the console. This should be specific to your integration's needs. For example, you could report the linting results to a user interface.
+Define a function to output the linting results to the console. This should be
+specific to your integration's needs. For example, you could report the linting
+results to a user interface.
In this example, we'll simply log the results to the console:
@@ -117,52 +152,52 @@ In this example, we'll simply log the results to the console:
// Log results to console if there are any problems
function outputLintingResults(results) {
- // Identify the number of problems found
- const problems = results.reduce((acc, result) => acc + result.errorCount + result.warningCount, 0);
-
- if (problems > 0) {
- console.log("Linting errors found!");
- console.log(results);
- } else {
- console.log("No linting errors found.");
- }
- return results;
+ // Identify the number of problems found
+ const problems = results.reduce(
+ (acc, result) => acc + result.errorCount + result.warningCount,
+ 0,
+ );
+
+ if (problems > 0) {
+ console.log("Linting errors found!");
+ console.log(results);
+ } else {
+ console.log("No linting errors found.");
+ }
+ return results;
}
```
## Step 5: Put It All Together
-Put the above functions together in a new function called `lintFiles`. This function will be the main entry point for your integration:
+Put the above functions together in a new function called `lintFiles`. This
+function will be the main entry point for your integration:
```javascript
// example-eslint-integration.js
// Put previous functions all together
async function lintFiles(filePaths) {
-
- // The ESLint configuration. Alternatively, you could load the configuration
- // from a .eslintrc file or just use the default config.
- const overrideConfig = {
- env: {
- es6: true,
- node: true,
- },
- parserOptions: {
- ecmaVersion: 2018,
- },
- rules: {
- "no-console": "error",
- "no-unused-vars": "warn",
- },
- };
-
- const eslint = createESLintInstance(overrideConfig);
- const results = await lintAndFix(eslint, filePaths);
- return outputLintingResults(results);
+ // The ESLint configuration. Alternatively, you could load the configuration
+ // from a .eslintrc file or just use the default config.
+ const overrideConfig = {
+ languageOptions: {
+ ecmaVersion: 2018,
+ sourceType: "commonjs",
+ },
+ rules: {
+ "no-console": "error",
+ "no-unused-vars": "warn",
+ },
+ };
+
+ const eslint = createESLintInstance(overrideConfig);
+ const results = await lintAndFix(eslint, filePaths);
+ return outputLintingResults(results);
}
// Export integration
-module.exports = { lintFiles }
+module.exports = { lintFiles };
```
Here's the complete code example for `example-eslint-integration.js`:
@@ -171,66 +206,73 @@ Here's the complete code example for `example-eslint-integration.js`:
const { ESLint } = require("eslint");
// Create an instance of ESLint with the configuration passed to the function
-function createESLintInstance(overrideConfig){
- return new ESLint({ useEslintrc: false, overrideConfig: overrideConfig, fix: true });
+function createESLintInstance(overrideConfig) {
+ return new ESLint({
+ overrideConfigFile: true,
+ overrideConfig,
+ fix: true,
+ });
}
// Lint the specified files and return the results
async function lintAndFix(eslint, filePaths) {
- const results = await eslint.lintFiles(filePaths);
+ const results = await eslint.lintFiles(filePaths);
- // Apply automatic fixes and output fixed code
- await ESLint.outputFixes(results);
+ // Apply automatic fixes and output fixed code
+ await ESLint.outputFixes(results);
- return results;
+ return results;
}
// Log results to console if there are any problems
function outputLintingResults(results) {
- // Identify the number of problems found
- const problems = results.reduce((acc, result) => acc + result.errorCount + result.warningCount, 0);
-
- if (problems > 0) {
- console.log("Linting errors found!");
- console.log(results);
- } else {
- console.log("No linting errors found.");
- }
- return results;
+ // Identify the number of problems found
+ const problems = results.reduce(
+ (acc, result) => acc + result.errorCount + result.warningCount,
+ 0,
+ );
+
+ if (problems > 0) {
+ console.log("Linting errors found!");
+ console.log(results);
+ } else {
+ console.log("No linting errors found.");
+ }
+ return results;
}
// Put previous functions all together
async function lintFiles(filePaths) {
-
- // The ESLint configuration. Alternatively, you could load the configuration
- // from a .eslintrc file or just use the default config.
- const overrideConfig = {
- env: {
- es6: true,
- node: true,
- },
- parserOptions: {
- ecmaVersion: 2018,
- },
- rules: {
- "no-console": "error",
- "no-unused-vars": "warn",
- },
- };
-
- const eslint = createESLintInstance(overrideConfig);
- const results = await lintAndFix(eslint, filePaths);
- return outputLintingResults(results);
+ // The ESLint configuration. Alternatively, you could load the configuration
+ // from an eslint.config.js file or just use the default config.
+ const overrideConfig = {
+ languageOptions: {
+ ecmaVersion: 2018,
+ sourceType: "commonjs",
+ },
+ rules: {
+ "no-console": "error",
+ "no-unused-vars": "warn",
+ },
+ };
+
+ const eslint = createESLintInstance(overrideConfig);
+ const results = await lintAndFix(eslint, filePaths);
+ return outputLintingResults(results);
}
// Export integration
-module.exports = { lintFiles }
+module.exports = { lintFiles };
```
## Conclusion
-In this tutorial, we have covered the essentials of using the `ESLint` class to lint files and retrieve results in your projects. This knowledge can be applied to create custom integrations, such as code editor plugins, to provide real-time feedback on code quality.
+In this tutorial, we have covered the essentials of using the `ESLint` class to
+lint files and retrieve results in your projects. This knowledge can be applied
+to create custom integrations, such as code editor plugins, to provide real-time
+feedback on code quality.
## View the Tutorial Code
-You can view the annotated source code for the tutorial [here](https://github.com/eslint/eslint/tree/main/docs/_examples/integration-tutorial-code).
+You can view the annotated source code for the tutorial
+[here](https://github.com/eslint/eslint/tree/main/docs/_examples/integration-tutorial-code).
diff --git a/docs/src/integrate/nodejs-api.md b/docs/src/integrate/nodejs-api.md
index 744f98295f49..2494ae8d9115 100644
--- a/docs/src/integrate/nodejs-api.md
+++ b/docs/src/integrate/nodejs-api.md
@@ -15,7 +15,7 @@ While ESLint is designed to be run on the command line, it's possible to use ESL
The `ESLint` class is the primary class to use in Node.js applications.
-This class depends on the Node.js `fs` module and the file system, so you cannot use it in browsers. If you want to lint code on browsers, use the [Linter](#linter) class instead.
+This class depends on the Node.js [`fs`](https://nodejs.org/api/fs.html) module and the file system, so you cannot use it in browsers. If you want to lint code on browsers, use the [`Linter`](#linter) class instead.
Here's a simple example of using the `ESLint` class:
@@ -23,21 +23,21 @@ Here's a simple example of using the `ESLint` class:
const { ESLint } = require("eslint");
(async function main() {
- // 1. Create an instance.
- const eslint = new ESLint();
+ // 1. Create an instance.
+ const eslint = new ESLint();
- // 2. Lint files.
- const results = await eslint.lintFiles(["lib/**/*.js"]);
+ // 2. Lint files.
+ const results = await eslint.lintFiles(["lib/**/*.js"]);
- // 3. Format the results.
- const formatter = await eslint.loadFormatter("stylish");
- const resultText = formatter.format(results);
+ // 3. Format the results.
+ const formatter = await eslint.loadFormatter("stylish");
+ const resultText = formatter.format(results);
- // 4. Output it.
- console.log(resultText);
-})().catch((error) => {
- process.exitCode = 1;
- console.error(error);
+ // 4. Output it.
+ console.log(resultText);
+})().catch(error => {
+ process.exitCode = 1;
+ console.error(error);
});
```
@@ -47,28 +47,28 @@ Here's an example that autofixes lint problems:
const { ESLint } = require("eslint");
(async function main() {
- // 1. Create an instance with the `fix` option.
- const eslint = new ESLint({ fix: true });
+ // 1. Create an instance with the `fix` option.
+ const eslint = new ESLint({ fix: true });
- // 2. Lint files. This doesn't modify target files.
- const results = await eslint.lintFiles(["lib/**/*.js"]);
+ // 2. Lint files. This doesn't modify target files.
+ const results = await eslint.lintFiles(["lib/**/*.js"]);
- // 3. Modify the files with the fixed code.
- await ESLint.outputFixes(results);
+ // 3. Modify the files with the fixed code.
+ await ESLint.outputFixes(results);
- // 4. Format the results.
- const formatter = await eslint.loadFormatter("stylish");
- const resultText = formatter.format(results);
+ // 4. Format the results.
+ const formatter = await eslint.loadFormatter("stylish");
+ const resultText = formatter.format(results);
- // 5. Output it.
- console.log(resultText);
-})().catch((error) => {
- process.exitCode = 1;
- console.error(error);
+ // 5. Output it.
+ console.log(resultText);
+})().catch(error => {
+ process.exitCode = 1;
+ console.error(error);
});
```
-And here is an example of using the `ESLint` class with `lintText` API:
+And here is an example of using the `ESLint` class with [`lintText`](#-eslintlinttextcode-options) API:
```js
const { ESLint } = require("eslint");
@@ -81,34 +81,29 @@ const testCode = `
`;
(async function main() {
- // 1. Create an instance
- const eslint = new ESLint({
- useEslintrc: false,
- overrideConfig: {
- extends: ["eslint:recommended"],
- parserOptions: {
- sourceType: "module",
- ecmaVersion: "latest",
- },
- env: {
- es2022: true,
- node: true,
- },
- },
- });
-
- // 2. Lint text.
- const results = await eslint.lintText(testCode);
-
- // 3. Format the results.
- const formatter = await eslint.loadFormatter("stylish");
- const resultText = formatter.format(results);
-
- // 4. Output it.
- console.log(resultText);
-})().catch((error) => {
- process.exitCode = 1;
- console.error(error);
+ // 1. Create an instance
+ const eslint = new ESLint({
+ overrideConfigFile: true,
+ overrideConfig: {
+ languageOptions: {
+ ecmaVersion: 2018,
+ sourceType: "commonjs",
+ },
+ },
+ });
+
+ // 2. Lint text.
+ const results = await eslint.lintText(testCode);
+
+ // 3. Format the results.
+ const formatter = await eslint.loadFormatter("stylish");
+ const resultText = formatter.format(results);
+
+ // 4. Output it.
+ console.log(resultText);
+})().catch(error => {
+ process.exitCode = 1;
+ console.error(error);
});
```
@@ -126,56 +121,59 @@ The `ESLint` constructor takes an `options` object. If you omit the `options` ob
##### File Enumeration
-* `options.cwd` (`string`)
+- `options.cwd` (`string`)
Default is `process.cwd()`. The working directory. This must be an absolute path.
-* `options.errorOnUnmatchedPattern` (`boolean`)
+- `options.errorOnUnmatchedPattern` (`boolean`)
Default is `true`. Unless set to `false`, the [`eslint.lintFiles()`][eslint-lintfiles] method will throw an error when no target files are found.
-* `options.extensions` (`string[] | null`)
- Default is `null`. If you pass directory paths to the [`eslint.lintFiles()`][eslint-lintfiles] method, ESLint checks the files in those directories that have the given extensions. For example, when passing the `src/` directory and `extensions` is `[".js", ".ts"]`, ESLint will lint `*.js` and `*.ts` files in `src/`. If `extensions` is `null`, ESLint checks `*.js` files and files that match `overrides[].files` patterns in your configuration.
**Note:** This option only applies when you pass directory paths to the [`eslint.lintFiles()`][eslint-lintfiles] method. If you pass glob patterns like `lib/**/*`, ESLint will lint all files matching the glob pattern regardless of extension.
-* `options.globInputPaths` (`boolean`)
+- `options.globInputPaths` (`boolean`)
Default is `true`. If `false` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method doesn't interpret glob patterns.
-* `options.ignore` (`boolean`)
- Default is `true`. If `false` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method doesn't respect `.eslintignore` files or `ignorePatterns` in your configuration.
-* `options.ignorePath` (`string | null`)
- Default is `null`. The path to a file ESLint uses instead of `$CWD/.eslintignore`. If a path is present and the file doesn't exist, this constructor will throw an error.
+- `options.ignore` (`boolean`)
+ Default is `true`. If `false` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method doesn't respect `ignorePatterns` in your configuration.
+- `options.ignorePatterns` (`string[] | null`)
+ Default is `null`. Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`.
+- `options.passOnNoPatterns` (`boolean`)
+ Default is `false`. When set to `true`, missing patterns cause the linting operation to short circuit and not report any failures.
+- `options.warnIgnored` (`boolean`)
+ Default is `true`. Show warnings when the file list includes ignored files.
##### Linting
-* `options.allowInlineConfig` (`boolean`)
+- `options.allowInlineConfig` (`boolean`)
Default is `true`. If `false` is present, ESLint suppresses directive comments in source code. If this option is `false`, it overrides the `noInlineConfig` setting in your configurations.
-* `options.baseConfig` (`ConfigData | null`)
+- `options.baseConfig` (`Config | Config[] | null`)
Default is `null`. [Configuration object], extended by all configurations used with this instance. You can use this option to define the default settings that will be used if your configuration files don't configure it.
-* `options.overrideConfig` (`ConfigData | null`)
- Default is `null`. [Configuration object], overrides all configurations used with this instance. You can use this option to define the settings that will be used even if your configuration files configure it.
-* `options.overrideConfigFile` (`string | null`)
- Default is `null`. The path to a configuration file, overrides all configurations used with this instance. The `options.overrideConfig` option is applied after this option is applied.
-* `options.plugins` (`Record | null`)
+- `options.overrideConfig` (`Config | Config[] | null`)
+ Default is `null`. [Configuration object], added after any existing configuration and therefore applies after what's contained in your configuration file (if used).
+- `options.overrideConfigFile` (`null | true | string`)
+ Default is `null`. By default, ESLint searches for a configuration file. When this option is set to `true`, ESLint does not search for a configuration file. When this option is set to a `string` value, ESLint does not search for a configuration file, and uses the provided value as the path to the configuration file.
+- `options.plugins` (`Record | null`)
Default is `null`. The plugin implementations that ESLint uses for the `plugins` setting of your configuration. This is a map-like object. Those keys are plugin IDs and each value is implementation.
-* `options.reportUnusedDisableDirectives` (`"error" | "warn" | "off" | null`)
- Default is `null`. The severity to report unused eslint-disable and eslint-enable directives. If this option is a severity, it overrides the `reportUnusedDisableDirectives` setting in your configurations.
-* `options.resolvePluginsRelativeTo` (`string` | `null`)
- Default is `null`. The path to a directory where plugins should be resolved from. If `null` is present, ESLint loads plugins from the location of the configuration file that contains the plugin setting. If a path is present, ESLint loads all plugins from there.
-* `options.rulePaths` (`string[]`)
- Default is `[]`. An array of paths to directories to load custom rules from.
-* `options.useEslintrc` (`boolean`)
- Default is `true`. If `false` is present, ESLint doesn't load configuration files (`.eslintrc.*` files). Only the configuration of the constructor options is valid.
+- `options.ruleFilter` (`({ruleId: string, severity: number}) => boolean`)
+ Default is `() => true`. A predicate function that filters rules to be run. This function is called with an object containing `ruleId` and `severity`, and returns `true` if the rule should be run.
+- `options.stats` (`boolean`)
+ Default is `false`. When set to `true`, additional statistics are added to the lint results (see [Stats type](../extend/stats#-stats-type)).
##### Autofix
-* `options.fix` (`boolean | (message: LintMessage) => boolean`)
+- `options.fix` (`boolean | (message: LintMessage) => boolean`)
Default is `false`. If `true` is present, the [`eslint.lintFiles()`][eslint-lintfiles] and [`eslint.lintText()`][eslint-linttext] methods work in autofix mode. If a predicate function is present, the methods pass each lint message to the function, then use only the lint messages for which the function returned `true`.
-* `options.fixTypes` (`("directive" | "problem" | "suggestion" | "layout")[] | null`)
+- `options.fixTypes` (`("directive" | "problem" | "suggestion" | "layout")[] | null`)
Default is `null`. The types of the rules that the [`eslint.lintFiles()`][eslint-lintfiles] and [`eslint.lintText()`][eslint-linttext] methods use for autofix.
##### Cache-related
-* `options.cache` (`boolean`)
+- `options.cache` (`boolean`)
Default is `false`. If `true` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method caches lint results and uses it if each target file is not changed. Please mind that ESLint doesn't clear the cache when you upgrade ESLint plugins. In that case, you have to remove the cache file manually. The [`eslint.lintText()`][eslint-linttext] method doesn't use caches even if you pass the `options.filePath` to the method.
-* `options.cacheLocation` (`string`)
+- `options.cacheLocation` (`string`)
Default is `.eslintcache`. The [`eslint.lintFiles()`][eslint-lintfiles] method writes caches into this file.
-* `options.cacheStrategy` (`string`)
+- `options.cacheStrategy` (`string`)
Default is `"metadata"`. Strategy for the cache to use for detecting changed files. Can be either `"metadata"` or `"content"`.
+##### Other Options
+
+- `options.flags` (`string[]`)
+ Default is `[]`. The feature flags to enable for this instance.
+
### â eslint.lintFiles(patterns)
```js
@@ -186,12 +184,12 @@ This method lints the files that match the glob patterns and then returns the re
#### Parameters
-* `patterns` (`string | string[]`)
+- `patterns` (`string | string[]`)
The lint target files. This can contain any of file paths, directory paths, and glob patterns.
#### Return Value
-* (`Promise`)
+- (`Promise`)
The promise that will be fulfilled with an array of [LintResult] objects.
### â eslint.lintText(code, options)
@@ -210,16 +208,16 @@ If the `options.filePath` value is configured to be ignored, this method returns
The second parameter `options` is omittable.
-* `code` (`string`)
+- `code` (`string`)
The source code text to check.
-* `options.filePath` (`string`)
+- `options.filePath` (`string`)
Optional. The path to the file of the source code text. If omitted, the `result.filePath` becomes the string `""`.
-* `options.warnIgnored` (`boolean`)
- Optional. If `true` is present and the `options.filePath` is a file ESLint should ignore, this method returns a lint result contains a warning message.
+- `options.warnIgnored` (`boolean`)
+ Optional, defaults to `options.warnIgnored` passed to the constructor. If `true` is present and the `options.filePath` is a file ESLint should ignore, this method returns a lint result contains a warning message.
#### Return Value
-* (`Promise`)
+- (`Promise`)
The promise that will be fulfilled with an array of [LintResult] objects. This is an array (despite there being only one lint result) in order to keep the interfaces between this and the [`eslint.lintFiles()`][eslint-lintfiles] method similar.
### â eslint.getRulesMetaForResults(results)
@@ -233,12 +231,12 @@ This method returns an object containing meta information for each rule that tri
#### Parameters
-* `results` (`LintResult[]`)
+- `results` (`LintResult[]`)
An array of [LintResult] objects returned from a call to `ESLint#lintFiles()` or `ESLint#lintText()`.
#### Return Value
-* (`Object`)
+- (`Object`)
An object whose property names are the rule IDs from the `results` and whose property values are the rule's meta information (if available).
### â eslint.calculateConfigForFile(filePath)
@@ -249,20 +247,14 @@ const config = await eslint.calculateConfigForFile(filePath);
This method calculates the configuration for a given file, which can be useful for debugging purposes.
-* It resolves and merges `extends` and `overrides` settings into the top level configuration.
-* It resolves the `parser` setting to absolute paths.
-* It normalizes the `plugins` setting to align short names. (e.g., `eslint-plugin-foo` â `foo`)
-* It adds the `processor` setting if a legacy file extension processor is matched.
-* It doesn't interpret the `env` setting to the `globals` and `parserOptions` settings, so the result object contains the `env` setting as is.
-
#### Parameters
-* `filePath` (`string`)
+- `filePath` (`string`)
The path to the file whose configuration you would like to calculate. Directory paths are forbidden because ESLint cannot handle the `overrides` setting.
#### Return Value
-* (`Promise