From f29d6573b3a2d083a8f55893affa308d57fbc2f1 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Tue, 19 Nov 2024 12:10:13 -0500 Subject: [PATCH 01/10] feat!: no-unknown-at-rule -> no-invalid-at-rule --- docs/rules/no-invalid-at-rules.md | 80 +++++++++++ docs/rules/no-unknown-at-rules.md | 51 ------- src/index.js | 2 +- src/rules/no-invalid-at-rules.js | 126 ++++++++++++++++++ ...es.test.js => no-invalid-at-rules.test.js} | 56 +++++++- 5 files changed, 259 insertions(+), 56 deletions(-) create mode 100644 docs/rules/no-invalid-at-rules.md delete mode 100644 docs/rules/no-unknown-at-rules.md create mode 100644 src/rules/no-invalid-at-rules.js rename tests/rules/{no-unknown-at-rules.test.js => no-invalid-at-rules.test.js} (64%) diff --git a/docs/rules/no-invalid-at-rules.md b/docs/rules/no-invalid-at-rules.md new file mode 100644 index 0000000..b438883 --- /dev/null +++ b/docs/rules/no-invalid-at-rules.md @@ -0,0 +1,80 @@ +# no-invalid-at-rules + +Disallow invalid at-rules. + +## Background + +CSS contains a number of at-rules, each beginning with a `@`, that perform various operations. Some common at-rules include: + +- `@import` +- `@media` +- `@font-face` +- `@keyframes` +- `@supports` +- `@namespace` +- `@page` +- `@charset` + +It's important to use a known at-rule because unknown at-rules cause the browser to ignore the entire block, including any rules contained within. For example: + +```css +/* typo */ +@charse "UTF-8"; +``` + +Here, the `@charset` at-rule is incorrectly spelled as `@charse`, which means that it will be ignored. + +Each at-rule also has a defined prelude (which may be empty) and potentially one or more descriptors. For example: + +```css +@property --main-bg-color { + syntax: ""; + inherits: false; + initial-value: #000000; +} +``` + +Here, `--main-bg-color` is the prelude for `@property` while `syntax`, `inherits`, and `initial-value` are descriptors. The `@property` at-rule requires a specific format for its prelude and only specific descriptors to be present. If any of these are incorrect, the browser ignores the at-rule. + +## Rule Details + +This rule warns when it finds a CSS at-rule that is unknown or invalid according to the CSS specification. As such, the rule warns for the following problems: + +- An unknown at-rule +- An invalid prelude for a known at-rule +- An unknown descriptor for a known at-rule +- An invalid descriptor value for a known at-rule + +The at-rule data is provided via the [CSSTree](https://github.com/csstree/csstree) project. + +Examples of incorrect code: + +```css +@charse "UTF-8"; + +@importx url(foo.css); + +@foobar { + .my-style { + color: red; + } +} + +@property main-bg-color { + syntax: ""; + inherits: false; + initial-value: #000000; +} + +@property --main-bg-color { + syntax: red; +} +``` + +## When Not to Use It + +If you are purposely using at-rules that aren't part of the CSS specification, then you can safely disable this rule. + +## Prior Art + +- [`at-rule-no-unknown`](https://stylelint.io/user-guide/rules/at-rule-no-unknown) diff --git a/docs/rules/no-unknown-at-rules.md b/docs/rules/no-unknown-at-rules.md deleted file mode 100644 index 197a0fb..0000000 --- a/docs/rules/no-unknown-at-rules.md +++ /dev/null @@ -1,51 +0,0 @@ -# no-unknown-at-rules - -Disallow unknown at-rules. - -## Background - -CSS contains a number of at-rules, each beginning with a `@`, that perform various operations. Some common at-rules include: - -- `@import` -- `@media` -- `@font-face` -- `@keyframes` -- `@supports` -- `@namespace` -- `@page` -- `@charset` - -It's important to use a known at-rule because unknown at-rules cause the browser to ignore the entire block, including any rules contained within. For example: - -```css -/* typo */ -@charse "UTF-8"; -``` - -Here, the `@charset` at-rule is incorrectly spelled as `@charse`, which means that it will be ignored. - -## Rule Details - -This rule warns when it finds a CSS at-rule that isn't part of the CSS specification. The at-rule data is provided via the [CSSTree](https://github.com/csstree/csstree) project. - -Examples of incorrect code: - -```css -@charse "UTF-8"; - -@importx url(foo.css); - -@foobar { - .my-style { - color: red; - } -} -``` - -## When Not to Use It - -If you are purposely using at-rules that aren't part of the CSS specification, then you can safely disable this rule. - -## Prior Art - -- [`at-rule-no-unknown`](https://stylelint.io/user-guide/rules/at-rule-no-unknown) diff --git a/src/index.js b/src/index.js index 9048356..a130917 100644 --- a/src/index.js +++ b/src/index.js @@ -11,8 +11,8 @@ import { CSSLanguage } from "./languages/css-language.js"; import { CSSSourceCode } from "./languages/css-source-code.js"; import noEmptyBlocks from "./rules/no-empty-blocks.js"; import noDuplicateImports from "./rules/no-duplicate-imports.js"; -import noUnknownAtRules from "./rules/no-unknown-at-rules.js"; import noInvalidProperties from "./rules/no-invalid-properties.js"; +import noUnknownAtRules from "./rules/no-invalid-at-rules.js"; //----------------------------------------------------------------------------- // Plugin diff --git a/src/rules/no-invalid-at-rules.js b/src/rules/no-invalid-at-rules.js new file mode 100644 index 0000000..da0836f --- /dev/null +++ b/src/rules/no-invalid-at-rules.js @@ -0,0 +1,126 @@ +/** + * @fileoverview Rule to prevent the use of unknown at-rules in CSS. + * @author Nicholas C. Zakas + */ + +//----------------------------------------------------------------------------- +// Imports +//----------------------------------------------------------------------------- + +import { lexer } from "css-tree"; + +//----------------------------------------------------------------------------- +// Rule Definition +//----------------------------------------------------------------------------- + +export default { + meta: { + type: "problem", + + docs: { + description: "Disallow invalid at-rules.", + recommended: true, + }, + + messages: { + unknownAtRule: "Unknown at-rule '@{{name}}' found.", + invalidPrelude: + "Invalid prelude '{{prelude}}' found for at-rule '@{{name}}'. Expected '{{expected}}'.", + unknownDescriptor: + "Unknown descriptor '{{descriptor}}' found for at-rule '@{{name}}'.", + invalidDescriptor: + "Invalid value '{{value}}' for descriptor '{{descriptor}}' found for at-rule '@{{name}}'. Expected {{expected}}.", + }, + }, + + create(context) { + const { sourceCode } = context; + + return { + Atrule(node) { + // checks both name and prelude + const { error } = lexer.matchAtrulePrelude( + node.name, + node.prelude, + ); + + if (error) { + if (error.reference) { + const loc = node.loc; + + context.report({ + loc: { + start: loc.start, + end: { + line: loc.start.line, + + // add 1 to account for the @ symbol + column: + loc.start.column + node.name.length + 1, + }, + }, + messageId: "unknownAtRule", + data: { + name: node.name, + }, + }); + } else { + context.report({ + loc: error.loc, + messageId: "invalidPrelude", + data: { + name: node.name, + prelude: error.css, + expected: error.syntax, + }, + }); + } + } + }, + + "AtRule > Block > Declaration"(node) { + // get at rule node + const atRule = sourceCode.getParent(sourceCode.getParent(node)); + + const { error } = lexer.matchAtruleDescriptor( + atRule.name, + node.property, + node.value, + ); + + if (error) { + if (error.reference) { + const loc = node.loc; + + context.report({ + loc: { + start: loc.start, + end: { + line: loc.start.line, + column: + loc.start.column + node.property.length, + }, + }, + messageId: "unknownDescriptor", + data: { + name: atRule.name, + descriptor: error.reference, + }, + }); + } else { + context.report({ + loc: error.loc, + messageId: "invalidDescriptor", + data: { + name: atRule.name, + descriptor: node.property, + value: error.css, + expected: error.syntax, + }, + }); + } + } + }, + }; + }, +}; diff --git a/tests/rules/no-unknown-at-rules.test.js b/tests/rules/no-invalid-at-rules.test.js similarity index 64% rename from tests/rules/no-unknown-at-rules.test.js rename to tests/rules/no-invalid-at-rules.test.js index 1d2ebf4..972f728 100644 --- a/tests/rules/no-unknown-at-rules.test.js +++ b/tests/rules/no-invalid-at-rules.test.js @@ -1,5 +1,5 @@ /** - * @fileoverview Tests for no-unknown-at-rules rule. + * @fileoverview Tests for no-invalid-at-rules rule. * @author Nicholas C. Zakas */ @@ -7,7 +7,7 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/no-unknown-at-rules.js"; +import rule from "../../src/rules/no-invalid-at-rules.js"; import css from "../../src/index.js"; import { RuleTester } from "eslint"; @@ -22,7 +22,7 @@ const ruleTester = new RuleTester({ language: "css/css", }); -ruleTester.run("no-unknown-at-rules", rule, { +ruleTester.run("no-invalid-at-rules", rule, { valid: [ "@import url('styles.css');", "@font-face { font-family: 'MyFont'; src: url('myfont.woff2') format('woff2'); }", @@ -62,7 +62,7 @@ ruleTester.run("no-unknown-at-rules", rule, { code: "@foobaz { }", errors: [ { - message: "Unknown at-rule 'foobaz' found.", + message: "Unknown at-rule '@foobaz' found.", line: 1, column: 1, endLine: 1, @@ -91,5 +91,53 @@ ruleTester.run("no-unknown-at-rules", rule, { }, ], }, + { + code: "@property foo { }", + errors: [ + { + messageId: "invalidPrelude", + data: { + name: "property", + prelude: "foo", + expected: "", + }, + line: 1, + column: 11, + endLine: 1, + endColumn: 14, + }, + ], + }, + { + code: "@property --foo { baz: red; }", + errors: [ + { + messageId: "unknownDescriptor", + data: { name: "property", descriptor: "baz" }, + line: 1, + column: 19, + endLine: 1, + endColumn: 22, + }, + ], + }, + { + code: "@property --foo { syntax: red; }", + errors: [ + { + messageId: "invalidDescriptor", + line: 1, + data: { + name: "property", + descriptor: "syntax", + value: "red", + expected: "", + }, + column: 27, + endLine: 1, + endColumn: 30, + }, + ], + }, ], }); From 6053fe271d53aa342301cf20af814b0d3957c8b4 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 20 Nov 2024 10:57:35 -0500 Subject: [PATCH 02/10] Update README --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5827871..80bca80 100644 --- a/README.md +++ b/README.md @@ -56,12 +56,12 @@ export default [ -| **Rule Name** | **Description** | **Recommended** | -| :--------------------------------------------------------------- | :------------------------------- | :-------------: | -| [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules | yes | -| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks | yes | -| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties | yes | -| [`no-unknown-at-rules`](./docs/rules/no-unknown-at-rules.md) | Disallow unknown at-rules | yes | +| **Rule Name** | **Description** | **Recommended** | +| :--------------------------------------------------------------- | :-------------------------------- | :-------------: | +| [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules. | yes | +| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks. | yes | +| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties. | yes | +| [`no-invalid-at-rules`](./docs/rules/no-invalid-at-rules.md) | Disallow invalid at-rules. | yes | From 84236a3d0e950ae13c93dedb47794597b6c0845c Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 20 Nov 2024 11:48:38 -0500 Subject: [PATCH 03/10] Catch more errors in at-rules --- src/index.js | 6 +- src/rules/no-invalid-at-rules.js | 112 +++++++++++++++--------- src/rules/no-invalid-properties.js | 20 +---- src/util.js | 33 +++++++ tests/rules/no-invalid-at-rules.test.js | 26 ++++++ 5 files changed, 136 insertions(+), 61 deletions(-) create mode 100644 src/util.js diff --git a/src/index.js b/src/index.js index a130917..fe8bd2c 100644 --- a/src/index.js +++ b/src/index.js @@ -12,7 +12,7 @@ import { CSSSourceCode } from "./languages/css-source-code.js"; import noEmptyBlocks from "./rules/no-empty-blocks.js"; import noDuplicateImports from "./rules/no-duplicate-imports.js"; import noInvalidProperties from "./rules/no-invalid-properties.js"; -import noUnknownAtRules from "./rules/no-invalid-at-rules.js"; +import noInvalidAtRules from "./rules/no-invalid-at-rules.js"; //----------------------------------------------------------------------------- // Plugin @@ -29,7 +29,7 @@ const plugin = { rules: { "no-empty-blocks": noEmptyBlocks, "no-duplicate-imports": noDuplicateImports, - "no-unknown-at-rules": noUnknownAtRules, + "no-invalid-at-rules": noInvalidAtRules, "no-invalid-properties": noInvalidProperties, }, configs: {}, @@ -41,7 +41,7 @@ Object.assign(plugin.configs, { rules: { "css/no-empty-blocks": "error", "css/no-duplicate-imports": "error", - "css/no-unknown-at-rules": "error", + "css/no-invalid-at-rules": "error", "css/no-invalid-properties": "error", }, }, diff --git a/src/rules/no-invalid-at-rules.js b/src/rules/no-invalid-at-rules.js index da0836f..17f099c 100644 --- a/src/rules/no-invalid-at-rules.js +++ b/src/rules/no-invalid-at-rules.js @@ -8,6 +8,35 @@ //----------------------------------------------------------------------------- import { lexer } from "css-tree"; +import { isSyntaxReferenceError, isSyntaxMatchError } from "../util.js"; + +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +/** + * Extracts metadata from an error object. + * @param {SyntaxError} error The error object to extract metadata from. + * @returns {Object} The metadata extracted from the error. + */ +function extractMetaDataFromError(error) { + const message = error.message; + const atRuleName = /`@(.*)`/u.exec(message)[1]; + let messageId = "unknownAtRule"; + + if (message.endsWith("prelude")) { + messageId = message.includes("should not") + ? "invalidExtraPrelude" + : "missingPrelude"; + } + + return { + messageId, + data: { + name: atRuleName, + }, + }; +} //----------------------------------------------------------------------------- // Rule Definition @@ -30,6 +59,9 @@ export default { "Unknown descriptor '{{descriptor}}' found for at-rule '@{{name}}'.", invalidDescriptor: "Invalid value '{{value}}' for descriptor '{{descriptor}}' found for at-rule '@{{name}}'. Expected {{expected}}.", + invalidExtraPrelude: + "At-rule '@{{name}}' should not contain a prelude.", + missingPrelude: "At-rule '@{{name}}' should contain a prelude.", }, }, @@ -45,26 +77,7 @@ export default { ); if (error) { - if (error.reference) { - const loc = node.loc; - - context.report({ - loc: { - start: loc.start, - end: { - line: loc.start.line, - - // add 1 to account for the @ symbol - column: - loc.start.column + node.name.length + 1, - }, - }, - messageId: "unknownAtRule", - data: { - name: node.name, - }, - }); - } else { + if (isSyntaxMatchError(error)) { context.report({ loc: error.loc, messageId: "invalidPrelude", @@ -74,7 +87,23 @@ export default { expected: error.syntax, }, }); + return; } + + const loc = node.loc; + + context.report({ + loc: { + start: loc.start, + end: { + line: loc.start.line, + + // add 1 to account for the @ symbol + column: loc.start.column + node.name.length + 1, + }, + }, + ...extractMetaDataFromError(error), + }); } }, @@ -89,25 +118,7 @@ export default { ); if (error) { - if (error.reference) { - const loc = node.loc; - - context.report({ - loc: { - start: loc.start, - end: { - line: loc.start.line, - column: - loc.start.column + node.property.length, - }, - }, - messageId: "unknownDescriptor", - data: { - name: atRule.name, - descriptor: error.reference, - }, - }); - } else { + if (isSyntaxMatchError(error)) { context.report({ loc: error.loc, messageId: "invalidDescriptor", @@ -118,7 +129,30 @@ export default { expected: error.syntax, }, }); + return; } + + const loc = node.loc; + const metaData = isSyntaxReferenceError(error) + ? { + messageId: "unknownDescriptor", + data: { + name: atRule.name, + descriptor: error.reference, + }, + } + : extractMetaDataFromError(error); + + context.report({ + loc: { + start: loc.start, + end: { + line: loc.start.line, + column: loc.start.column + node.property.length, + }, + }, + ...metaData, + }); } }, }; diff --git a/src/rules/no-invalid-properties.js b/src/rules/no-invalid-properties.js index b281c34..2566948 100644 --- a/src/rules/no-invalid-properties.js +++ b/src/rules/no-invalid-properties.js @@ -8,25 +8,7 @@ //----------------------------------------------------------------------------- import { lexer } from "css-tree"; - -//----------------------------------------------------------------------------- -// Type Definitions -//----------------------------------------------------------------------------- - -/** @typedef {import("css-tree").SyntaxMatchError} SyntaxMatchError */ - -//----------------------------------------------------------------------------- -// Helpers -//----------------------------------------------------------------------------- - -/** - * Determines if an error is a syntax match error. - * @param {Object} error The error object from the CSS parser. - * @returns {error is SyntaxMatchError} True if the error is a syntax match error, false if not. - */ -function isSyntaxMatchError(error) { - return typeof error.css === "string"; -} +import { isSyntaxMatchError } from "../util.js"; //----------------------------------------------------------------------------- // Rule Definition diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..12e7e12 --- /dev/null +++ b/src/util.js @@ -0,0 +1,33 @@ +/** + * @fileoverview Utility functions for ESLint CSS plugin. + * @author Nicholas C. Zakas + */ + +//----------------------------------------------------------------------------- +// Type Definitions +//----------------------------------------------------------------------------- + +/** @typedef {import("css-tree").SyntaxReferenceError} SyntaxReferenceError */ +/** @typedef {import("css-tree").SyntaxMatchError} SyntaxMatchError */ + +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +/** + * Determines if an error is a reference error. + * @param {Object} error The error object to check. + * @returns {error is SyntaxReferenceError} True if the error is a reference error, false if not. + */ +export function isSyntaxReferenceError(error) { + return typeof error.reference === "string"; +} + +/** + * Determines if an error is a syntax match error. + * @param {Object} error The error object to check. + * @returns {error is SyntaxMatchError} True if the error is a syntax match error, false if not. + */ +export function isSyntaxMatchError(error) { + return typeof error.syntax === "string"; +} diff --git a/tests/rules/no-invalid-at-rules.test.js b/tests/rules/no-invalid-at-rules.test.js index 972f728..8e903c0 100644 --- a/tests/rules/no-invalid-at-rules.test.js +++ b/tests/rules/no-invalid-at-rules.test.js @@ -139,5 +139,31 @@ ruleTester.run("no-invalid-at-rules", rule, { }, ], }, + { + code: "@supports { a {} }", + errors: [ + { + messageId: "missingPrelude", + data: { name: "supports" }, + line: 1, + column: 1, + endLine: 1, + endColumn: 10, + }, + ], + }, + { + code: "@font-face foo { }", + errors: [ + { + messageId: "invalidExtraPrelude", + data: { name: "font-face" }, + line: 1, + column: 1, + endLine: 1, + endColumn: 11, + }, + ], + }, ], }); From bdfed661159f87c33ee0fa422fd98d402e43926e Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 20 Nov 2024 14:17:34 -0500 Subject: [PATCH 04/10] Adjust error reporting --- README.md | 2 +- src/rules/no-invalid-at-rules.js | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 80bca80..c931e62 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,8 @@ export default [ | :--------------------------------------------------------------- | :-------------------------------- | :-------------: | | [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules. | yes | | [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks. | yes | -| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties. | yes | | [`no-invalid-at-rules`](./docs/rules/no-invalid-at-rules.md) | Disallow invalid at-rules. | yes | +| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties. | yes | diff --git a/src/rules/no-invalid-at-rules.js b/src/rules/no-invalid-at-rules.js index 17f099c..c42b8a0 100644 --- a/src/rules/no-invalid-at-rules.js +++ b/src/rules/no-invalid-at-rules.js @@ -8,7 +8,7 @@ //----------------------------------------------------------------------------- import { lexer } from "css-tree"; -import { isSyntaxReferenceError, isSyntaxMatchError } from "../util.js"; +import { isSyntaxMatchError } from "../util.js"; //----------------------------------------------------------------------------- // Helpers @@ -28,6 +28,8 @@ function extractMetaDataFromError(error) { messageId = message.includes("should not") ? "invalidExtraPrelude" : "missingPrelude"; + } else if (message.includes("descriptor")) { + messageId = "invalidDescriptor"; } return { @@ -133,15 +135,6 @@ export default { } const loc = node.loc; - const metaData = isSyntaxReferenceError(error) - ? { - messageId: "unknownDescriptor", - data: { - name: atRule.name, - descriptor: error.reference, - }, - } - : extractMetaDataFromError(error); context.report({ loc: { @@ -151,7 +144,11 @@ export default { column: loc.start.column + node.property.length, }, }, - ...metaData, + messageId: "unknownDescriptor", + data: { + name: atRule.name, + descriptor: node.property, + }, }); } }, From 723bfd352ce2ac08e5cda3892120de965bb729c9 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 20 Nov 2024 14:18:06 -0500 Subject: [PATCH 05/10] Remove unnecessary if statement --- src/rules/no-invalid-at-rules.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rules/no-invalid-at-rules.js b/src/rules/no-invalid-at-rules.js index c42b8a0..13baca4 100644 --- a/src/rules/no-invalid-at-rules.js +++ b/src/rules/no-invalid-at-rules.js @@ -28,8 +28,6 @@ function extractMetaDataFromError(error) { messageId = message.includes("should not") ? "invalidExtraPrelude" : "missingPrelude"; - } else if (message.includes("descriptor")) { - messageId = "invalidDescriptor"; } return { From f938e5dafab2fb8c846823fbe017282571d8da59 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Fri, 22 Nov 2024 10:45:01 -0500 Subject: [PATCH 06/10] Update README --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c931e62..c0a174b 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,12 @@ export default [ - -| **Rule Name** | **Description** | **Recommended** | -| :--------------------------------------------------------------- | :-------------------------------- | :-------------: | -| [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules. | yes | -| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks. | yes | -| [`no-invalid-at-rules`](./docs/rules/no-invalid-at-rules.md) | Disallow invalid at-rules. | yes | -| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties. | yes | - +| **Rule Name** | **Description** | **Recommended** | +| :- | :- | :-: | +| [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules. | yes | +| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks. | yes | +| [`no-invalid-at-rules`](./docs/rules/no-invalid-at-rules.md) | Disallow invalid at-rules. | yes | +| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties. | yes | **Note:** This plugin does not provide formatting rules. We recommend using a source code formatter such as [Prettier](https://prettier.io) for that purpose. From 213f3f996b1b44027d4d62259032a2fed9d571ec Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Fri, 22 Nov 2024 11:12:02 -0500 Subject: [PATCH 07/10] Fix type error --- src/util.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/util.js b/src/util.js index 12e7e12..968c625 100644 --- a/src/util.js +++ b/src/util.js @@ -7,8 +7,11 @@ // Type Definitions //----------------------------------------------------------------------------- -/** @typedef {import("css-tree").SyntaxReferenceError} SyntaxReferenceError */ -/** @typedef {import("css-tree").SyntaxMatchError} SyntaxMatchError */ +/* + * Note: Using `import()` in the JSDoc comments below because including them as + * typedef comments here caused Rollup to remove them. I couldn't figure out why + * this was happening so just working around for now. + */ //----------------------------------------------------------------------------- // Helpers @@ -17,7 +20,7 @@ /** * Determines if an error is a reference error. * @param {Object} error The error object to check. - * @returns {error is SyntaxReferenceError} True if the error is a reference error, false if not. + * @returns {error is import("css-tree").SyntaxReferenceError} True if the error is a reference error, false if not. */ export function isSyntaxReferenceError(error) { return typeof error.reference === "string"; @@ -26,7 +29,7 @@ export function isSyntaxReferenceError(error) { /** * Determines if an error is a syntax match error. * @param {Object} error The error object to check. - * @returns {error is SyntaxMatchError} True if the error is a syntax match error, false if not. + * @returns {error is import("css-tree").SyntaxMatchError} True if the error is a syntax match error, false if not. */ export function isSyntaxMatchError(error) { return typeof error.syntax === "string"; From 5dceff5187d350818e9bcd6de8640ffd51ce7bb3 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Fri, 22 Nov 2024 11:14:53 -0500 Subject: [PATCH 08/10] Fix validation issues --- src/rules/no-invalid-at-rules.js | 2 +- src/rules/no-unknown-at-rules.js | 61 -------------------------------- 2 files changed, 1 insertion(+), 62 deletions(-) delete mode 100644 src/rules/no-unknown-at-rules.js diff --git a/src/rules/no-invalid-at-rules.js b/src/rules/no-invalid-at-rules.js index 13baca4..e291270 100644 --- a/src/rules/no-invalid-at-rules.js +++ b/src/rules/no-invalid-at-rules.js @@ -47,7 +47,7 @@ export default { type: "problem", docs: { - description: "Disallow invalid at-rules.", + description: "Disallow invalid at-rules", recommended: true, }, diff --git a/src/rules/no-unknown-at-rules.js b/src/rules/no-unknown-at-rules.js deleted file mode 100644 index 931e2f8..0000000 --- a/src/rules/no-unknown-at-rules.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @fileoverview Rule to prevent the use of unknown at-rules in CSS. - * @author Nicholas C. Zakas - */ - -//----------------------------------------------------------------------------- -// Imports -//----------------------------------------------------------------------------- - -import data from "css-tree/definition-syntax-data"; - -//----------------------------------------------------------------------------- -// Helpers -//----------------------------------------------------------------------------- - -const knownAtRules = new Set(Object.keys(data.atrules)); - -//----------------------------------------------------------------------------- -// Rule Definition -//----------------------------------------------------------------------------- - -export default { - meta: { - type: "problem", - - docs: { - description: "Disallow unknown at-rules", - recommended: true, - }, - - messages: { - unknownAtRule: "Unknown at-rule '{{name}}' found.", - }, - }, - - create(context) { - return { - Atrule(node) { - if (!knownAtRules.has(node.name)) { - const loc = node.loc; - - context.report({ - loc: { - start: loc.start, - end: { - line: loc.start.line, - - // add 1 to account for the @ symbol - column: loc.start.column + node.name.length + 1, - }, - }, - messageId: "unknownAtRule", - data: { - name: node.name, - }, - }); - } - }, - }; - }, -}; From 6a8cceabab1f51df34fef3c26dac9e373c6f8c1f Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Fri, 22 Nov 2024 11:49:17 -0500 Subject: [PATCH 09/10] Fix README --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c0a174b..9d9a1d9 100644 --- a/README.md +++ b/README.md @@ -55,12 +55,14 @@ export default [ -| **Rule Name** | **Description** | **Recommended** | -| :- | :- | :-: | -| [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules. | yes | -| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks. | yes | -| [`no-invalid-at-rules`](./docs/rules/no-invalid-at-rules.md) | Disallow invalid at-rules. | yes | -| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties. | yes | + +| **Rule Name** | **Description** | **Recommended** | +| :--------------------------------------------------------------- | :------------------------------- | :-------------: | +| [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules | yes | +| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks | yes | +| [`no-invalid-at-rules`](./docs/rules/no-invalid-at-rules.md) | Disallow invalid at-rules | yes | +| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties | yes | + **Note:** This plugin does not provide formatting rules. We recommend using a source code formatter such as [Prettier](https://prettier.io) for that purpose. From dfcddb4fd41c1b34c5797e4f9e6cc72f8beb2d81 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Mon, 25 Nov 2024 11:22:02 -0500 Subject: [PATCH 10/10] Remove unused function --- src/util.js | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/util.js b/src/util.js index 968c625..67880a6 100644 --- a/src/util.js +++ b/src/util.js @@ -7,29 +7,16 @@ // Type Definitions //----------------------------------------------------------------------------- -/* - * Note: Using `import()` in the JSDoc comments below because including them as - * typedef comments here caused Rollup to remove them. I couldn't figure out why - * this was happening so just working around for now. - */ +/** @typedef {import("css-tree").SyntaxMatchError} SyntaxMatchError */ //----------------------------------------------------------------------------- // Helpers //----------------------------------------------------------------------------- -/** - * Determines if an error is a reference error. - * @param {Object} error The error object to check. - * @returns {error is import("css-tree").SyntaxReferenceError} True if the error is a reference error, false if not. - */ -export function isSyntaxReferenceError(error) { - return typeof error.reference === "string"; -} - /** * Determines if an error is a syntax match error. * @param {Object} error The error object to check. - * @returns {error is import("css-tree").SyntaxMatchError} True if the error is a syntax match error, false if not. + * @returns {error is SyntaxMatchError} True if the error is a syntax match error, false if not. */ export function isSyntaxMatchError(error) { return typeof error.syntax === "string";