Repository: eslint/eslint
Pull request #15671 (2022-03-03 13:42:50)

feat: Add favicon to the HTML document
### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/HEAD/CONTRIBUTING.md).

### What is the purpose of this pull request? (put an "X" next to an item)

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-change-proposal.md))
[ ] Add autofix to a rule
[ ] Add a CLI option
[ ] Add something to the core
[X] Other, please explain:

Update HTML format metadata (favicon).
[Use SVG as the main favicon](https://css-tricks.com/svg-favicons-and-all-the-fun-things-we-can-do-with-them/) and a fallback PNG favicon if the browser does not support [SVG favicons](https://caniuse.com/?search=svg%20favicon).

I Grabbed the vector (SVG) logo from [ESLint website repository](https://github.com/eslint/website/blob/master/assets/img/logo.svg).

[Encoded the SVG](https://css-tricks.com/probably-dont-base64-svg/) to base64 with [this tool](https://yoksel.github.io/url-encoder/).
Converted the vector logo to PNG with [Inkscape](https://inkscape.org/) and encoded the PNG to base64 with [this tool](https://www.base64-image.de/).

Tested with Chrome, Firefox, and Edge.
Here is a [demo HTML file](https://github.com/eslint/eslint/files/8178271/test-favicon.zip) to see the favicon in action.

I did NOT run any unit tests etc. for the project.

### What changes did you make? (Give an overview)
I added a favicon to the HTML report document.

### Is there anything you'd like reviewers to focus on?
No.


linux-foundation-easycla[bot]:  <a href="https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/19114526/11061773/15671/#/?version=2"><img src="https://s3.amazonaws.com/cla-project-logo-prod/cla-not-signed.svg" alt="CLA Not Signed" align="left" height="28" width="328"></a><br/><br /><ul><li><a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/19114526/11061773/15671/#/?version=2' target='_blank'>:x:</a> - Mahdi Hosseinzadeh The commit (48fb53c02527253010a8b0e9e217e3e91a66e6e2) is not authorized under a signed CLA. <a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/19114526/11061773/15671/#/?version=2' target='_blank'>Please click here to be authorized</a>. For further assistance with EasyCLA, <a href='https://jira.linuxfoundation.org/servicedesk/customer/portal/4' target='_blank'>please submit a support request ticket</a>.</li></ul>
eslint-github-bot[bot]: Hi @mahozad!, thanks for the Pull Request

The **first commit message** isn't properly formatted. We ask that you update the message to match this format, as we use it to generate changelogs and automate releases.

- The commit message tag wasn't recognized. Did you mean "docs", "fix", or "feat"?
- There should be a space following the initial tag and colon, for example 'feat: Message'.
- The first letter of the tag should be in lowercase

**To Fix:** You can fix this problem by running `git commit --amend`, editing your commit message, and then running `git push -f` to update this pull request.

Read more about contributing to ESLint [here](https://eslint.org/docs/developer-guide/contributing/)

eslint-github-bot[bot]: Hi @mahozad!, thanks for the Pull Request

The **pull request title** isn't properly formatted. We ask that you update the message to match this format, as we use it to generate changelogs and automate releases.

- The commit message tag wasn't recognized. Did you mean "docs", "fix", or "feat"?
- There should be a space following the initial tag and colon, for example 'feat: Message'.
- The first letter of the tag should be in lowercase

**To Fix:** You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page.

Read more about contributing to ESLint [here](https://eslint.org/docs/developer-guide/contributing/)


Patch:
diff --git a/docs/rules/computed-property-spacing.md b/docs/rules/computed-property-spacing.md
index 9f60ab804..beae78394 100644
--- a/docs/rules/computed-property-spacing.md
+++ b/docs/rules/computed-property-spacing.md
@@ -16,6 +16,10 @@ var a = "prop";
 var obj = {
   [a]: "value" // computed property key in object literal (ECMAScript 6)
 };
+
+var obj = { prop: "value" };
+var a = "prop";
+var { [a]: x } = obj; // computed property key in object destructuring pattern (ECMAScript 6)
 ```
 
 ## Rule Details
diff --git a/docs/user-guide/configuring/configuration-files.md b/docs/user-guide/configuring/configuration-files.md
index cc1d5bf3c..9066e1ef7 100644
--- a/docs/user-guide/configuring/configuration-files.md
+++ b/docs/user-guide/configuring/configuration-files.md
@@ -358,6 +358,8 @@ module.exports = {
 
 <b>v4.1.0+.</b> Sometimes a more fine-controlled configuration is necessary, for example, if the configuration for files within the same directory has to be different. Therefore you can provide configurations under the `overrides` key that will only apply to files that match specific glob patterns, using the same format you would pass on the command line (e.g., `app/**/*.test.js`).
 
+Glob patterns in overrides use [minimatch syntax](https://github.com/isaacs/minimatch). 
+
 ### How do overrides work?
 
 It is possible to override settings based on file glob patterns in your configuration by using the `overrides` key. An example of using the `overrides` key is as follows:
diff --git a/lib/cli-engine/formatters/html.js b/lib/cli-engine/formatters/html.js
index e28996f6c..b63659971 100644
--- a/lib/cli-engine/formatters/html.js
+++ b/lib/cli-engine/formatters/html.js
@@ -39,6 +39,8 @@ function pageTemplate(it) {
     <head>
         <meta charset="UTF-8">
         <title>ESLint Report</title>
+        <link rel="icon" type="image/png" sizes="any" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAHaAAAB2gGFomX7AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAABD1JREFUWMPFl11sk2UUx3/nbYtjxS1MF7MLMTECMgSTtSSyrQkLhAj7UBPnDSEGoxegGzMwojhXVpmTAA5iYpSoMQa8GBhFOrMFk03buei6yRAlcmOM0SEmU9d90b19jxcM1o5+sGnsc/e+z/l6ztf/HFFVMnns6QieeOCHBePGsHM+wrOtvLG2C4WRVDSSygNV7sCjlspxwDnPB44aols/DXk+mbMBmx/6OseITF1CuOtfevkPh2Uu+/jbdX8lujSScRlT5r7/QDlAfsRmfzmpnkQ/H3H13gf6bBrBn1uqK8WylgEnU8eZmk1repbfchJG1TyKyIKEwuBHFd3lD3naY3O1siiwXsVoBV2VgM1ht/QQUJk2ByqKghsQziYQ8ifKgexIXmuyzC4r67Y7R+xPAfuB/Nn3Cpva+0s7khpQVtZtd4bt51BWxtBYAiciprG7c7D4SixzU9PYalDL6110Ifb/w8W9eY7JqFeFHbO8fPGyLHwwFHJNJTSgwtVTB9oaw9BlQ+tO93vOxypoaQnfEYlI43SeCHDC4TDq9+51/h5fxr33q0ZfV9g04wat9Q943rjJgCp3952W2i8Bi6eDvdsfKj0cK/DYMRyXL4/sUJUmIHd2zYMezsvLaamp4WpcWN3BXSiHpuMwbGbZlnZ8tXY4rgosy+G7oRwQ0cAsd28YGgqfU5UjCZQDLALxDg+Hv/P5Rqvj4hwrS8izXzWb4spwc1GgENFnkpWRzxeuB+ssUHgLdb9UVdt8vpGdKQpze7n7y1U3DBChNRUuqOo9c+0+qpKKxyZqtAIYla7gY4JszAAQri93BSsMRZoyBcUC+w3Q3AyOA4sNhAOZ0q7Iq0b2vUNvK5zPgP+/H8+Zetdoa6uOikhdGurxebwvJY8Iz3V1rTMNAH+opEuQj5KTT/qA1yC+wyUjBm12OidaUtCcPNNX2h0Hx2JG69VulANZAJZJwfU7rzd/FHixuXniTdM0m4GtSQT7bTartqEh9yfImUEzkwKZmTwmo5a5JwkYBfcDL01/RkR5y8iWhtPBknB8ZxwtU9UjwOrrKCeizzc25nTGg1F/turEHoU9wMLpDvWKf8DTmNCAKnd/tqUTF4ElMXJ+A5rWDJS+41WsGWzALhJ+ErBWrLj9g+pqojHxlXJX8HGUg0BsR/x1yhxf3jm4cSzpQFLp6tmi6PEE7g1ZhtZ91ufpSZUAFa6gC+UoQslNaSmypT1U8mHKiUgEKS8KfgF4EpYunFI16tsHin+OG0LcgQK7yj7g6cSzpva2D3hKVNG0Y3mVO1BkqfSlmJrHBQ4uvM12gJHc6ETW8HZVfMRmXvyxxNC1Z/o839zyXlDuCr4nsC11J+MXueaVJWn6yPv+/pJtc9oLTNN4AeTvNGByd3rlhE2x9s5pLwDoHCy+grDzWmOZ95lUtLYj5Bma126Y8eX0/zj/ADxGyViSg4BXAAAAAElFTkSuQmCC">
+        <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMjk0LjgyNSAyNTguOTgyJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPg0KPHBhdGggZmlsbD0nIzgwODBGMicgZD0nTTk3LjAyMSw5OS4wMTZsNDguNDMyLTI3Ljk2MmMxLjIxMi0wLjcsMi43MDYtMC43LDMuOTE4LDBsNDguNDMzLDI3Ljk2MiBjMS4yMTEsMC43LDEuOTU5LDEuOTkzLDEuOTU5LDMuMzkzdjU1LjkyNGMwLDEuMzk5LTAuNzQ4LDIuNjkzLTEuOTU5LDMuMzk0bC00OC40MzMsMjcuOTYyYy0xLjIxMiwwLjctMi43MDYsMC43LTMuOTE4LDAgbC00OC40MzItMjcuOTYyYy0xLjIxMi0wLjctMS45NTktMS45OTQtMS45NTktMy4zOTR2LTU1LjkyNEM5NS4wNjMsMTAxLjAwOSw5NS44MSw5OS43MTYsOTcuMDIxLDk5LjAxNicvPg0KPHBhdGggZmlsbD0nIzRCMzJDMycgZD0nTTI3My4zMzYsMTI0LjQ4OEwyMTUuNDY5LDIzLjgxNmMtMi4xMDItMy42NC01Ljk4NS02LjMyNS0xMC4xODgtNi4zMjVIODkuNTQ1IGMtNC4yMDQsMC04LjA4OCwyLjY4NS0xMC4xOSw2LjMyNWwtNTcuODY3LDEwMC40NWMtMi4xMDIsMy42NDEtMi4xMDIsOC4yMzYsMCwxMS44NzdsNTcuODY3LDk5Ljg0NyBjMi4xMDIsMy42NCw1Ljk4Niw1LjUwMSwxMC4xOSw1LjUwMWgxMTUuNzM1YzQuMjAzLDAsOC4wODctMS44MDUsMTAuMTg4LTUuNDQ2bDU3Ljg2Ny0xMDAuMDEgQzI3NS40MzksMTMyLjM5NiwyNzUuNDM5LDEyOC4xMjgsMjczLjMzNiwxMjQuNDg4IE0yMjUuNDE5LDE3Mi44OThjMCwxLjQ4LTAuODkxLDIuODQ5LTIuMTc0LDMuNTlsLTczLjcxLDQyLjUyNyBjLTEuMjgyLDAuNzQtMi44ODgsMC43NC00LjE3LDBsLTczLjc2Ny00Mi41MjdjLTEuMjgyLTAuNzQxLTIuMTc5LTIuMTA5LTIuMTc5LTMuNTlWODcuODQzYzAtMS40ODEsMC44ODQtMi44NDksMi4xNjctMy41OSBsNzMuNzA3LTQyLjUyN2MxLjI4Mi0wLjc0MSwyLjg4Ni0wLjc0MSw0LjE2OCwwbDczLjc3Miw0Mi41MjdjMS4yODMsMC43NDEsMi4xODYsMi4xMDksMi4xODYsMy41OVYxNzIuODk4eicvPg0KPC9zdmc+">
         <style>
             body {
                 font-family:Arial, "Helvetica Neue", Helvetica, sans-serif;
diff --git a/lib/rules/valid-typeof.js b/lib/rules/valid-typeof.js
index 604635812..cb85cd9cb 100644
--- a/lib/rules/valid-typeof.js
+++ b/lib/rules/valid-typeof.js
@@ -19,6 +19,8 @@ module.exports = {
             url: "https://eslint.org/docs/rules/valid-typeof"
         },
 
+        hasSuggestions: true,
+
         schema: [
             {
                 type: "object",
@@ -33,7 +35,8 @@ module.exports = {
         ],
         messages: {
             invalidValue: "Invalid typeof comparison value.",
-            notString: "Typeof comparisons should be to string literals."
+            notString: "Typeof comparisons should be to string literals.",
+            suggestString: 'Use `"{{type}}"` instead of `{{type}}`.'
         }
     },
 
@@ -44,6 +47,21 @@ module.exports = {
 
         const requireStringLiterals = context.options[0] && context.options[0].requireStringLiterals;
 
+        let globalScope;
+
+        /**
+         * Checks whether the given node represents a reference to a global variable that is not declared in the source code.
+         * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
+         * @param {ASTNode} node `Identifier` node to check.
+         * @returns {boolean} `true` if the node is a reference to a global variable.
+         */
+        function isReferenceToGlobalVariable(node) {
+            const variable = globalScope.set.get(node.name);
+
+            return variable && variable.defs.length === 0 &&
+                variable.references.some(ref => ref.identifier === node);
+        }
+
         /**
          * Determines whether a node is a typeof expression.
          * @param {ASTNode} node The node
@@ -59,6 +77,10 @@ module.exports = {
 
         return {
 
+            Program() {
+                globalScope = context.getScope();
+            },
+
             UnaryExpression(node) {
                 if (isTypeofExpression(node)) {
                     const parent = context.getAncestors().pop();
@@ -72,6 +94,20 @@ module.exports = {
                             if (VALID_TYPES.indexOf(value) === -1) {
                                 context.report({ node: sibling, messageId: "invalidValue" });
                             }
+                        } else if (sibling.type === "Identifier" && sibling.name === "undefined" && isReferenceToGlobalVariable(sibling)) {
+                            context.report({
+                                node: sibling,
+                                messageId: requireStringLiterals ? "notString" : "invalidValue",
+                                suggest: [
+                                    {
+                                        messageId: "suggestString",
+                                        data: { type: "undefined" },
+                                        fix(fixer) {
+                                            return fixer.replaceText(sibling, '"undefined"');
+                                        }
+                                    }
+                                ]
+                            });
                         } else if (requireStringLiterals && !isTypeofExpression(sibling)) {
                             context.report({ node: sibling, messageId: "notString" });
                         }


Test patch:
diff --git a/tests/lib/rules/valid-typeof.js b/tests/lib/rules/valid-typeof.js
index cd28088a0..35a52f0a6 100644
--- a/tests/lib/rules/valid-typeof.js
+++ b/tests/lib/rules/valid-typeof.js
@@ -45,6 +45,7 @@ ruleTester.run("valid-typeof", rule, {
         "typeof(foo) == 'string'",
         "typeof(foo) != 'string'",
         "var oddUse = typeof foo + 'thing'",
+        "function f(undefined) { typeof x === undefined }",
         {
             code: "typeof foo === 'number'",
             options: [{ requireStringLiterals: true }]
@@ -136,6 +137,21 @@ ruleTester.run("valid-typeof", rule, {
             options: [{ requireStringLiterals: true }],
             errors: [{ messageId: "invalidValue", type: "Literal" }]
         },
+        {
+            code: "if (typeof bar !== undefined) {}",
+            errors: [
+                {
+                    messageId: "invalidValue",
+                    type: "Identifier",
+                    suggestions: [
+                        {
+                            messageId: "suggestString",
+                            data: { type: "undefined" },
+                            output: 'if (typeof bar !== "undefined") {}'
+                        }
+                    ]
+                }]
+        },
         {
             code: "typeof foo == Object",
             options: [{ requireStringLiterals: true }],
@@ -144,17 +160,50 @@ ruleTester.run("valid-typeof", rule, {
         {
             code: "typeof foo === undefined",
             options: [{ requireStringLiterals: true }],
-            errors: [{ messageId: "notString", type: "Identifier" }]
+            errors: [
+                {
+                    messageId: "notString",
+                    type: "Identifier",
+                    suggestions: [
+                        {
+                            messageId: "suggestString",
+                            data: { type: "undefined" },
+                            output: 'typeof foo === "undefined"'
+                        }
+                    ]
+                }]
         },
         {
             code: "undefined === typeof foo",
             options: [{ requireStringLiterals: true }],
-            errors: [{ messageId: "notString", type: "Identifier" }]
+            errors: [
+                {
+                    messageId: "notString",
+                    type: "Identifier",
+                    suggestions: [
+                        {
+                            messageId: "suggestString",
+                            data: { type: "undefined" },
+                            output: '"undefined" === typeof foo'
+                        }
+                    ]
+                }]
         },
         {
             code: "undefined == typeof foo",
             options: [{ requireStringLiterals: true }],
-            errors: [{ messageId: "notString", type: "Identifier" }]
+            errors: [
+                {
+                    messageId: "notString",
+                    type: "Identifier",
+                    suggestions: [
+                        {
+                            messageId: "suggestString",
+                            data: { type: "undefined" },
+                            output: '"undefined" == typeof foo'
+                        }
+                    ]
+                }]
         },
         {
             code: "typeof foo === `undefined${foo}`",