Skip to content

Commit

Permalink
feat: Add no-duplicate-imports rule (#4)
Browse files Browse the repository at this point in the history
* feat: Add no-duplicate-imports rule

* Update src/rules/no-duplicate-imports.js

Co-authored-by: Milos Djermanovic <[email protected]>

* Update docs/rules/no-duplicate-imports.md

Co-authored-by: Milos Djermanovic <[email protected]>

* Update docs/rules/no-duplicate-imports.md

Co-authored-by: Milos Djermanovic <[email protected]>

* Add rule to recommended and docs

* Add rule to plugin

---------

Co-authored-by: Milos Djermanovic <[email protected]>
  • Loading branch information
nzakas and mdjermanovic authored Nov 8, 2024
1 parent c9424e5 commit 8d4558b
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 3 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ export default [

<!-- Rule Table Start -->

| **Rule Name** | **Description** | **Recommended** |
| :--------------------------------------------------- | :--------------------- | :-------------: |
| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks. | 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 |

<!-- Rule Table End -->

Expand Down
41 changes: 41 additions & 0 deletions docs/rules/no-duplicate-imports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# no-duplicate-imports

Disallow duplicate `@import` rules.

## Background

CSS files can import rules from other CSS files using the `@import` rule and specifying a URL from which to load. In a file with many `@import` rules it can be difficult to see if you've accidentally imported the same URL twice, for example:

```css
@import url(a.css);
@import "b.css";
@import url("c.css");
@import "a.css";
```

There is no reason to import the same URL twice, so this is a mistake.

## Rule Details

This rule warns when it finds an `@import` rule that imports the same URL as a previous `@import` rule. This includes all of the URL forms (`"a.css"`, `url("a.css")`, and `url(a.css)`).

Examples of incorrect code:

```css
@import url(a.css);
@import "b.css";
@import url("c.css");

/* duplicates */
@import "a.css";
@import url(b.css);
@import "c.css";
```

## When Not to Use It

If you aren't concerned with duplicate `@import` rules, you can safely disable this rule.

## Prior Art

- [`no-duplicate-at-import-rules`](https://stylelint.io/user-guide/rules/no-duplicate-at-import-rules)
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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";

//-----------------------------------------------------------------------------
// Plugin
Expand All @@ -25,6 +26,7 @@ const plugin = {
},
rules: {
"no-empty-blocks": noEmptyBlocks,
"no-duplicate-imports": noDuplicateImports,
},
configs: {},
};
Expand All @@ -34,6 +36,7 @@ Object.assign(plugin.configs, {
plugins: { css: plugin },
rules: {
"css/no-empty-blocks": "error",
"css/no-duplicate-imports": "error",
},
},
});
Expand Down
39 changes: 39 additions & 0 deletions src/rules/no-duplicate-imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @fileoverview Rule to prevent duplicate imports in CSS.
* @author Nicholas C. Zakas
*/

export default {
meta: {
type: "problem",

docs: {
description: "Disallow duplicate @import rules.",
recommended: true,
},

messages: {
duplicateImport: "Unexpected duplicate @import rule for {{url}}.",
},
},

create(context) {
const imports = new Set();

return {
"Atrule[name=import]"(node) {
const url = node.prelude.children[0].value;

if (imports.has(url)) {
context.report({
loc: node.loc,
messageId: "duplicateImport",
data: { url },
});
} else {
imports.add(url);
}
},
};
},
};
101 changes: 101 additions & 0 deletions tests/rules/no-duplicate-imports.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* @fileoverview Tests for no-duplicate-imports rule.
* @author Nicholas C. Zakas
*/

//------------------------------------------------------------------------------
// Imports
//------------------------------------------------------------------------------

import rule from "../../src/rules/no-duplicate-imports.js";
import css from "../../src/index.js";
import { RuleTester } from "eslint";

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester({
plugins: {
css,
},
language: "css/css",
});

ruleTester.run("no-duplicate-imports", rule, {
valid: [
"@import url('x.css');",
"@import url('x.css'); @import url('y.css');",
"@import 'x.css'; @import url('y.css'); @import 'z.css';",
],
invalid: [
{
code: "@import url('x.css');\n@import url('x.css');",
errors: [
{
messageId: "duplicateImport",
data: { url: "x.css" },
line: 2,
column: 1,
endLine: 2,
endColumn: 22,
},
],
},
{
code: "@import url('x.css');\n@import 'x.css';",
errors: [
{
messageId: "duplicateImport",
data: { url: "x.css" },
line: 2,
column: 1,
endLine: 2,
endColumn: 17,
},
],
},
{
code: "@import url('x.css');\n@import 'x.css';\n@import 'x.css';",
errors: [
{
messageId: "duplicateImport",
data: { url: "x.css" },
line: 2,
column: 1,
endLine: 2,
endColumn: 17,
},
{
messageId: "duplicateImport",
data: { url: "x.css" },
line: 3,
column: 1,
endLine: 3,
endColumn: 17,
},
],
},
{
code: "@import url('x.css');\n@import 'x.css';\n@import url('y.css');\n@import 'y.css';",
errors: [
{
messageId: "duplicateImport",
data: { url: "x.css" },
line: 2,
column: 1,
endLine: 2,
endColumn: 17,
},
{
messageId: "duplicateImport",
data: { url: "y.css" },
line: 4,
column: 1,
endLine: 4,
endColumn: 17,
},
],
},
],
});

0 comments on commit 8d4558b

Please sign in to comment.