Skip to content

Commit 8d4558b

Browse files
nzakasmdjermanovic
andauthored
feat: Add no-duplicate-imports rule (#4)
* feat: Add no-duplicate-imports rule * Update src/rules/no-duplicate-imports.js Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com> * Update docs/rules/no-duplicate-imports.md Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com> * Update docs/rules/no-duplicate-imports.md Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com> * Add rule to recommended and docs * Add rule to plugin --------- Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
1 parent c9424e5 commit 8d4558b

5 files changed

Lines changed: 188 additions & 3 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ export default [
5656

5757
<!-- Rule Table Start -->
5858

59-
| **Rule Name** | **Description** | **Recommended** |
60-
| :--------------------------------------------------- | :--------------------- | :-------------: |
61-
| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks. | yes |
59+
| **Rule Name** | **Description** | **Recommended** |
60+
| :------------------------------------------------------------- | :-------------------------------- | :-------------: |
61+
| [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules. | yes |
62+
| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks. | yes |
6263

6364
<!-- Rule Table End -->
6465

docs/rules/no-duplicate-imports.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# no-duplicate-imports
2+
3+
Disallow duplicate `@import` rules.
4+
5+
## Background
6+
7+
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:
8+
9+
```css
10+
@import url(a.css);
11+
@import "b.css";
12+
@import url("c.css");
13+
@import "a.css";
14+
```
15+
16+
There is no reason to import the same URL twice, so this is a mistake.
17+
18+
## Rule Details
19+
20+
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)`).
21+
22+
Examples of incorrect code:
23+
24+
```css
25+
@import url(a.css);
26+
@import "b.css";
27+
@import url("c.css");
28+
29+
/* duplicates */
30+
@import "a.css";
31+
@import url(b.css);
32+
@import "c.css";
33+
```
34+
35+
## When Not to Use It
36+
37+
If you aren't concerned with duplicate `@import` rules, you can safely disable this rule.
38+
39+
## Prior Art
40+
41+
- [`no-duplicate-at-import-rules`](https://stylelint.io/user-guide/rules/no-duplicate-at-import-rules)

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import { CSSLanguage } from "./languages/css-language.js";
1111
import { CSSSourceCode } from "./languages/css-source-code.js";
1212
import noEmptyBlocks from "./rules/no-empty-blocks.js";
13+
import noDuplicateImports from "./rules/no-duplicate-imports.js";
1314

1415
//-----------------------------------------------------------------------------
1516
// Plugin
@@ -25,6 +26,7 @@ const plugin = {
2526
},
2627
rules: {
2728
"no-empty-blocks": noEmptyBlocks,
29+
"no-duplicate-imports": noDuplicateImports,
2830
},
2931
configs: {},
3032
};
@@ -34,6 +36,7 @@ Object.assign(plugin.configs, {
3436
plugins: { css: plugin },
3537
rules: {
3638
"css/no-empty-blocks": "error",
39+
"css/no-duplicate-imports": "error",
3740
},
3841
},
3942
});

src/rules/no-duplicate-imports.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @fileoverview Rule to prevent duplicate imports in CSS.
3+
* @author Nicholas C. Zakas
4+
*/
5+
6+
export default {
7+
meta: {
8+
type: "problem",
9+
10+
docs: {
11+
description: "Disallow duplicate @import rules.",
12+
recommended: true,
13+
},
14+
15+
messages: {
16+
duplicateImport: "Unexpected duplicate @import rule for {{url}}.",
17+
},
18+
},
19+
20+
create(context) {
21+
const imports = new Set();
22+
23+
return {
24+
"Atrule[name=import]"(node) {
25+
const url = node.prelude.children[0].value;
26+
27+
if (imports.has(url)) {
28+
context.report({
29+
loc: node.loc,
30+
messageId: "duplicateImport",
31+
data: { url },
32+
});
33+
} else {
34+
imports.add(url);
35+
}
36+
},
37+
};
38+
},
39+
};
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* @fileoverview Tests for no-duplicate-imports rule.
3+
* @author Nicholas C. Zakas
4+
*/
5+
6+
//------------------------------------------------------------------------------
7+
// Imports
8+
//------------------------------------------------------------------------------
9+
10+
import rule from "../../src/rules/no-duplicate-imports.js";
11+
import css from "../../src/index.js";
12+
import { RuleTester } from "eslint";
13+
14+
//------------------------------------------------------------------------------
15+
// Tests
16+
//------------------------------------------------------------------------------
17+
18+
const ruleTester = new RuleTester({
19+
plugins: {
20+
css,
21+
},
22+
language: "css/css",
23+
});
24+
25+
ruleTester.run("no-duplicate-imports", rule, {
26+
valid: [
27+
"@import url('x.css');",
28+
"@import url('x.css'); @import url('y.css');",
29+
"@import 'x.css'; @import url('y.css'); @import 'z.css';",
30+
],
31+
invalid: [
32+
{
33+
code: "@import url('x.css');\n@import url('x.css');",
34+
errors: [
35+
{
36+
messageId: "duplicateImport",
37+
data: { url: "x.css" },
38+
line: 2,
39+
column: 1,
40+
endLine: 2,
41+
endColumn: 22,
42+
},
43+
],
44+
},
45+
{
46+
code: "@import url('x.css');\n@import 'x.css';",
47+
errors: [
48+
{
49+
messageId: "duplicateImport",
50+
data: { url: "x.css" },
51+
line: 2,
52+
column: 1,
53+
endLine: 2,
54+
endColumn: 17,
55+
},
56+
],
57+
},
58+
{
59+
code: "@import url('x.css');\n@import 'x.css';\n@import 'x.css';",
60+
errors: [
61+
{
62+
messageId: "duplicateImport",
63+
data: { url: "x.css" },
64+
line: 2,
65+
column: 1,
66+
endLine: 2,
67+
endColumn: 17,
68+
},
69+
{
70+
messageId: "duplicateImport",
71+
data: { url: "x.css" },
72+
line: 3,
73+
column: 1,
74+
endLine: 3,
75+
endColumn: 17,
76+
},
77+
],
78+
},
79+
{
80+
code: "@import url('x.css');\n@import 'x.css';\n@import url('y.css');\n@import 'y.css';",
81+
errors: [
82+
{
83+
messageId: "duplicateImport",
84+
data: { url: "x.css" },
85+
line: 2,
86+
column: 1,
87+
endLine: 2,
88+
endColumn: 17,
89+
},
90+
{
91+
messageId: "duplicateImport",
92+
data: { url: "y.css" },
93+
line: 4,
94+
column: 1,
95+
endLine: 4,
96+
endColumn: 17,
97+
},
98+
],
99+
},
100+
],
101+
});

0 commit comments

Comments
 (0)