@d-zero/stylelint-rules 5.0.0-alpha.73 → 5.0.0-alpha.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d-zero/stylelint-rules",
3
- "version": "5.0.0-alpha.73",
3
+ "version": "5.0.0-alpha.75",
4
4
  "description": "Rules of Stylelint for D-ZERO",
5
5
  "repository": "https://github.com/d-zero-dev/linters.git",
6
6
  "author": "D-ZERO Co., Ltd.",
@@ -22,14 +22,14 @@
22
22
  "build": "tsc"
23
23
  },
24
24
  "dependencies": {
25
- "@d-zero/csstree-scss-syntax": "5.0.0-alpha.73",
25
+ "@d-zero/csstree-scss-syntax": "5.0.0-alpha.75",
26
26
  "css-tree": "3.1.0",
27
- "postcss-selector-parser": "7.1.0",
27
+ "postcss-selector-parser": "7.1.1",
28
28
  "postcss-value-parser": "4.2.0",
29
- "stylelint": "16.23.1"
29
+ "stylelint": "16.26.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "postcss": "8.5.6"
33
33
  },
34
- "gitHead": "a4e8a1e38c0ea98ebc77bdadf0c4890a072c7cd0"
34
+ "gitHead": "736d3b0a9e0b37ef8987c9bb478925f464fc917c"
35
35
  }
@@ -1,220 +0,0 @@
1
- import stylelint from 'stylelint';
2
- import { describe, test, expect } from 'vitest';
3
- import rule from './index.js';
4
- const { lint } = stylelint;
5
- const config = (settings = true) => ({
6
- plugins: [rule],
7
- rules: {
8
- // @ts-ignore
9
- [rule.ruleName]: settings,
10
- },
11
- });
12
- describe('Exact Match', () => {
13
- test('matched', async () => {
14
- const {
15
- // @ts-ignore
16
- results: [{ warnings, parseErrors }], } = await lint({
17
- codeFilename: 'test.css',
18
- code: '.test { color: currentColor; }',
19
- config: config({}),
20
- });
21
- expect(parseErrors).toHaveLength(0);
22
- expect(warnings).toHaveLength(0);
23
- });
24
- test('unmatched', async () => {
25
- const {
26
- // @ts-ignore
27
- results: [{ warnings, parseErrors }], } = await lint({
28
- codeFilename: 'test.css',
29
- code: '.text, *, div.test { color: currentColor; .test { color: inherit; } }',
30
- config: config({}),
31
- });
32
- expect(parseErrors).toHaveLength(0);
33
- expect(warnings).toStrictEqual([
34
- {
35
- column: 1,
36
- endColumn: 70,
37
- endLine: 1,
38
- line: 1,
39
- rule: '@d-zero/component',
40
- severity: 'error',
41
- text: 'クラス名がファイル名と一致しないか、コンポーネント命名規則(test__)で始まっていません',
42
- url: undefined,
43
- fix: undefined,
44
- },
45
- ]);
46
- });
47
- });
48
- describe('Component Naming Convention for CSS', () => {
49
- test('exact match in CSS', async () => {
50
- const {
51
- // @ts-ignore
52
- results: [{ warnings, parseErrors }], } = await lint({
53
- codeFilename: 'button.css',
54
- code: '.button { color: currentColor; }',
55
- config: config({}),
56
- });
57
- expect(parseErrors).toHaveLength(0);
58
- expect(warnings).toHaveLength(0);
59
- });
60
- test('component element match in CSS', async () => {
61
- const {
62
- // @ts-ignore
63
- results: [{ warnings, parseErrors }], } = await lint({
64
- codeFilename: 'button.css',
65
- code: '.button__text { color: currentColor; }',
66
- config: config({}),
67
- });
68
- expect(parseErrors).toHaveLength(0);
69
- expect(warnings).toHaveLength(0);
70
- });
71
- test('multiple component elements in CSS (auto-allow)', async () => {
72
- const {
73
- // @ts-ignore
74
- results: [{ warnings, parseErrors }], } = await lint({
75
- codeFilename: 'button.css',
76
- code: '.button__text, .button__icon { color: currentColor; }',
77
- config: config({}),
78
- });
79
- expect(parseErrors).toHaveLength(0);
80
- expect(warnings).toHaveLength(0);
81
- });
82
- test('mixed component and component elements in CSS', async () => {
83
- const {
84
- // @ts-ignore
85
- results: [{ warnings, parseErrors }], } = await lint({
86
- codeFilename: 'button.css',
87
- code: '.button, .button__text, .button__icon { color: currentColor; }',
88
- config: config({}),
89
- });
90
- expect(parseErrors).toHaveLength(0);
91
- expect(warnings).toHaveLength(0);
92
- });
93
- test('invalid component naming convention in CSS', async () => {
94
- const {
95
- // @ts-ignore
96
- results: [{ warnings, parseErrors }], } = await lint({
97
- codeFilename: 'button.css',
98
- code: '.card__text { color: currentColor; }',
99
- config: config({}),
100
- });
101
- expect(parseErrors).toHaveLength(0);
102
- expect(warnings).toStrictEqual([
103
- {
104
- column: 1,
105
- endColumn: 37,
106
- endLine: 1,
107
- line: 1,
108
- rule: '@d-zero/component',
109
- severity: 'error',
110
- text: 'クラス名がファイル名と一致しないか、コンポーネント命名規則(button__)で始まっていません',
111
- url: undefined,
112
- fix: undefined,
113
- },
114
- ]);
115
- });
116
- test('CSS with wrong component name', async () => {
117
- const {
118
- // @ts-ignore
119
- results: [{ warnings, parseErrors }], } = await lint({
120
- codeFilename: 'button.css',
121
- code: '.card { color: currentColor; }',
122
- config: config({}),
123
- });
124
- expect(parseErrors).toHaveLength(0);
125
- expect(warnings).toStrictEqual([
126
- {
127
- column: 1,
128
- endColumn: 31,
129
- endLine: 1,
130
- line: 1,
131
- rule: '@d-zero/component',
132
- severity: 'error',
133
- text: 'クラス名がファイル名と一致しないか、コンポーネント命名規則(button__)で始まっていません',
134
- url: undefined,
135
- fix: undefined,
136
- },
137
- ]);
138
- });
139
- });
140
- describe('Partial Name', () => {
141
- test('match', async () => {
142
- const {
143
- // @ts-ignore
144
- results: [{ warnings, parseErrors }], } = await lint({
145
- codeFilename: '_c-component.scss',
146
- code: '.c-component { color: currentColor; }',
147
- config: config({}),
148
- });
149
- expect(parseErrors).toHaveLength(0);
150
- expect(warnings).toHaveLength(0);
151
- });
152
- });
153
- describe('Options', () => {
154
- test('allowMultipleSelectors: false in SCSS', async () => {
155
- const {
156
- // @ts-ignore
157
- results: [{ warnings, parseErrors }], } = await lint({
158
- codeFilename: '_c-component.scss',
159
- code: '.c-component, .x-specific-class-name { color: currentColor; }',
160
- config: config({}),
161
- });
162
- expect(parseErrors).toHaveLength(0);
163
- expect(warnings).toHaveLength(1);
164
- });
165
- test('allowMultipleSelectors: true in SCSS', async () => {
166
- const {
167
- // @ts-ignore
168
- results: [{ warnings, parseErrors }], } = await lint({
169
- codeFilename: '_c-component.scss',
170
- code: '.c-component, .x-specific-class-name { color: currentColor; }',
171
- config: config({
172
- allowMultipleSelectors: true,
173
- }),
174
- });
175
- expect(parseErrors).toHaveLength(0);
176
- expect(warnings).toHaveLength(0);
177
- });
178
- test('CSS files automatically allow multiple selectors', async () => {
179
- const {
180
- // @ts-ignore
181
- results: [{ warnings, parseErrors }], } = await lint({
182
- codeFilename: 'button.css',
183
- code: '.button, .button__text, .button__icon { color: currentColor; }',
184
- config: config({
185
- allowMultipleSelectors: false, // この設定は CSS では無視される
186
- }),
187
- });
188
- expect(parseErrors).toHaveLength(0);
189
- expect(warnings).toHaveLength(0);
190
- });
191
- test('CSS files allow multiple rules', async () => {
192
- const {
193
- // @ts-ignore
194
- results: [{ warnings, parseErrors }], } = await lint({
195
- codeFilename: 'button.css',
196
- code: `.button { color: currentColor; }
197
- .button__text { font-size: 14px; }
198
- .button__icon { width: 16px; }`,
199
- config: config({}),
200
- });
201
- expect(parseErrors).toHaveLength(0);
202
- expect(warnings).toHaveLength(0);
203
- });
204
- test('CSS files reject multiple components', async () => {
205
- const {
206
- // @ts-ignore
207
- results: [{ warnings, parseErrors }], } = await lint({
208
- codeFilename: 'c-component.css',
209
- code: `.c-component { --prop: value; }
210
- .c-component__element { --prop: value; }
211
- .c-component2 { --prop: value; }
212
- .c-specific { --prop: value; }`,
213
- config: config({}),
214
- });
215
- expect(parseErrors).toHaveLength(0);
216
- expect(warnings).toHaveLength(2);
217
- expect(warnings[0].text).toBe('クラス名がファイル名と一致しないか、コンポーネント命名規則(c-component__)で始まっていません');
218
- expect(warnings[1].text).toBe('クラス名がファイル名と一致しないか、コンポーネント命名規則(c-component__)で始まっていません');
219
- });
220
- });
@@ -1,66 +0,0 @@
1
- import stylelint from 'stylelint';
2
- import { describe, test, expect } from 'vitest';
3
- import rule from './index.js';
4
- const { lint } = stylelint;
5
- const config = (settings = true) => ({
6
- plugins: [rule],
7
- rules: {
8
- // @ts-ignore
9
- [rule.ruleName]: settings,
10
- },
11
- });
12
- describe('Parse Error', () => {
13
- test('SCSS Syntax', async () => {
14
- const {
15
- // @ts-ignore
16
- results: [{ warnings, parseErrors }], } = await lint({
17
- code: '* { background: url("a" + $b + "c") }',
18
- config: config({
19
- length: ['px'],
20
- }),
21
- });
22
- expect(parseErrors).toHaveLength(0);
23
- expect(warnings).toHaveLength(0);
24
- });
25
- });
26
- describe('length-pattern', () => {
27
- test('length in flex', async () => {
28
- const {
29
- // @ts-ignore
30
- results: [{ warnings, parseErrors }], } = await lint({
31
- code: '* { flex: 1 1 10px }',
32
- config: config({
33
- length: ['/[0-9]{2,}px/'],
34
- }),
35
- });
36
- expect(parseErrors).toHaveLength(0);
37
- expect(warnings).toStrictEqual([
38
- {
39
- rule: '@d-zero/declaration-value-type-disallowed-list',
40
- severity: 'error',
41
- line: 1,
42
- endLine: 1,
43
- column: 15,
44
- endColumn: 19,
45
- text: 'Unexpected value "10px" for type "length" (@d-zero/declaration-value-type-disallowed-list)',
46
- url: undefined,
47
- fix: undefined,
48
- },
49
- ]);
50
- });
51
- test('ignoreProperties options', async () => {
52
- const {
53
- // @ts-ignore
54
- results: [{ warnings, parseErrors }], } = await lint({
55
- code: '* { flex: 1 1 10px }',
56
- config: config({
57
- length: {
58
- ignoreProperties: ['flex'],
59
- patterns: ['/[0-9]{2,}px/'],
60
- },
61
- }),
62
- });
63
- expect(parseErrors).toHaveLength(0);
64
- expect(warnings).toHaveLength(0);
65
- });
66
- });
@@ -1,137 +0,0 @@
1
- import stylelint from 'stylelint';
2
- import { describe, test, expect } from 'vitest';
3
- import plugin from './index.js';
4
- const ruleName = '@d-zero/prefer-individual-transform-properties';
5
- /**
6
- * Helper function to run stylelint with the plugin
7
- * @param code
8
- * @param options
9
- */
10
- async function lint(code, options) {
11
- const result = await stylelint.lint({
12
- code,
13
- config: {
14
- plugins: [plugin],
15
- rules: {
16
- [ruleName]: options || true,
17
- },
18
- },
19
- });
20
- return result.results[0]?.warnings || [];
21
- }
22
- describe('prefer-individual-transform-properties', () => {
23
- test('should flag simple translate function', async () => {
24
- const warnings = await lint(`
25
- .element {
26
- transform: translate(10px, 20px);
27
- }
28
- `);
29
- expect(warnings).toHaveLength(1);
30
- expect(warnings[0]?.text).toContain('translate: 10px, 20px');
31
- });
32
- test('should flag simple rotate function', async () => {
33
- const warnings = await lint(`
34
- .element {
35
- transform: rotate(45deg);
36
- }
37
- `);
38
- expect(warnings).toHaveLength(1);
39
- expect(warnings[0]?.text).toContain('rotate: 45deg');
40
- });
41
- test('should flag simple scale function', async () => {
42
- const warnings = await lint(`
43
- .element {
44
- transform: scale(1.5);
45
- }
46
- `);
47
- expect(warnings).toHaveLength(1);
48
- expect(warnings[0]?.text).toContain('scale: 1.5');
49
- });
50
- test('should flag translateX function', async () => {
51
- const warnings = await lint(`
52
- .element {
53
- transform: translateX(10px);
54
- }
55
- `);
56
- expect(warnings).toHaveLength(1);
57
- expect(warnings[0]?.text).toContain('translate: 10px');
58
- });
59
- test('should flag rotateY function', async () => {
60
- const warnings = await lint(`
61
- .element {
62
- transform: rotateY(90deg);
63
- }
64
- `);
65
- expect(warnings).toHaveLength(1);
66
- expect(warnings[0]?.text).toContain('rotate: 90deg');
67
- });
68
- test('should flag scaleX function', async () => {
69
- const warnings = await lint(`
70
- .element {
71
- transform: scaleX(2);
72
- }
73
- `);
74
- expect(warnings).toHaveLength(1);
75
- expect(warnings[0]?.text).toContain('scale: 2');
76
- });
77
- test('should not flag complex transforms with multiple different functions', async () => {
78
- const warnings = await lint(`
79
- .element {
80
- transform: translate(10px, 20px) rotate(45deg) scale(1.5);
81
- }
82
- `);
83
- // This contains multiple transform types, so it cannot be easily replaced
84
- expect(warnings).toHaveLength(0);
85
- });
86
- test('should not flag transforms with matrix functions', async () => {
87
- const warnings = await lint(`
88
- .element {
89
- transform: matrix(1, 0, 0, 1, 10, 20);
90
- }
91
- `);
92
- expect(warnings).toHaveLength(0);
93
- });
94
- test('should not flag transforms with skew functions', async () => {
95
- const warnings = await lint(`
96
- .element {
97
- transform: skew(20deg, 10deg);
98
- }
99
- `);
100
- expect(warnings).toHaveLength(0);
101
- });
102
- test('should not flag transforms mixing replaceable and non-replaceable functions', async () => {
103
- const warnings = await lint(`
104
- .element {
105
- transform: translate(10px, 20px) skew(20deg);
106
- }
107
- `);
108
- expect(warnings).toHaveLength(0);
109
- });
110
- test('should not flag non-transform properties', async () => {
111
- const warnings = await lint(`
112
- .element {
113
- transition: transform 0.3s ease;
114
- will-change: transform;
115
- }
116
- `);
117
- expect(warnings).toHaveLength(0);
118
- });
119
- test('should handle CSS variables in transform values', async () => {
120
- const warnings = await lint(`
121
- .element {
122
- transform: translate(var(--x), var(--y));
123
- }
124
- `);
125
- expect(warnings).toHaveLength(1);
126
- expect(warnings[0]?.text).toContain('translate: var(--x), var(--y)');
127
- });
128
- test('should handle calc() in transform values', async () => {
129
- const warnings = await lint(`
130
- .element {
131
- transform: translate(calc(100% - 20px), 0);
132
- }
133
- `);
134
- expect(warnings).toHaveLength(1);
135
- expect(warnings[0]?.text).toContain('translate: calc(100% - 20px), 0');
136
- });
137
- });
@@ -1,162 +0,0 @@
1
- import stylelint from 'stylelint';
2
- import { describe, test, expect } from 'vitest';
3
- import rule from './index.js';
4
- const { lint } = stylelint;
5
- const config = (settings = true) => ({
6
- plugins: [rule],
7
- rules: {
8
- // @ts-ignore
9
- [rule.ruleName]: settings,
10
- },
11
- });
12
- describe('shorthand-property-use-logical', () => {
13
- describe('padding', () => {
14
- test('single value - should not warn', async () => {
15
- const {
16
- // @ts-ignore
17
- results: [{ warnings, parseErrors }], } = await lint({
18
- code: '* { padding: 2rem }',
19
- config: config(),
20
- });
21
- expect(parseErrors).toHaveLength(0);
22
- expect(warnings).toHaveLength(0);
23
- });
24
- test('two values - should warn', async () => {
25
- const {
26
- // @ts-ignore
27
- results: [{ warnings, parseErrors }], } = await lint({
28
- code: '* { padding: 2rem 1rem }',
29
- config: config(),
30
- });
31
- expect(parseErrors).toHaveLength(0);
32
- expect(warnings).toStrictEqual([
33
- {
34
- rule: '@d-zero/shorthand-property-use-logical',
35
- severity: 'error',
36
- line: 1,
37
- endLine: 1,
38
- column: 5,
39
- endColumn: 23,
40
- text: 'Unexpected shorthand property "padding" with multiple values. Consider using logical properties: padding-block, padding-inline (@d-zero/shorthand-property-use-logical)',
41
- url: undefined,
42
- fix: undefined,
43
- },
44
- ]);
45
- });
46
- test('three values - should warn', async () => {
47
- const {
48
- // @ts-ignore
49
- results: [{ warnings, parseErrors }], } = await lint({
50
- code: '* { padding: 2rem 0 0 }',
51
- config: config(),
52
- });
53
- expect(parseErrors).toHaveLength(0);
54
- expect(warnings).toHaveLength(1);
55
- expect(warnings[0].text).toContain('Unexpected shorthand property "padding"');
56
- });
57
- test('four values - should warn', async () => {
58
- const {
59
- // @ts-ignore
60
- results: [{ warnings, parseErrors }], } = await lint({
61
- code: '* { padding: 1rem 2rem 3rem 4rem }',
62
- config: config(),
63
- });
64
- expect(parseErrors).toHaveLength(0);
65
- expect(warnings).toHaveLength(1);
66
- expect(warnings[0].text).toContain('Unexpected shorthand property "padding"');
67
- });
68
- });
69
- describe('margin', () => {
70
- test('single value - should not warn', async () => {
71
- const {
72
- // @ts-ignore
73
- results: [{ warnings, parseErrors }], } = await lint({
74
- code: '* { margin: auto }',
75
- config: config(),
76
- });
77
- expect(parseErrors).toHaveLength(0);
78
- expect(warnings).toHaveLength(0);
79
- });
80
- test('two values - should warn', async () => {
81
- const {
82
- // @ts-ignore
83
- results: [{ warnings, parseErrors }], } = await lint({
84
- code: '* { margin: 1rem 2rem }',
85
- config: config(),
86
- });
87
- expect(parseErrors).toHaveLength(0);
88
- expect(warnings).toHaveLength(1);
89
- expect(warnings[0].text).toContain('margin-block, margin-inline');
90
- });
91
- });
92
- describe('border-width', () => {
93
- test('single value - should not warn', async () => {
94
- const {
95
- // @ts-ignore
96
- results: [{ warnings, parseErrors }], } = await lint({
97
- code: '* { border-width: 1px }',
98
- config: config(),
99
- });
100
- expect(parseErrors).toHaveLength(0);
101
- expect(warnings).toHaveLength(0);
102
- });
103
- test('multiple values - should warn', async () => {
104
- const {
105
- // @ts-ignore
106
- results: [{ warnings, parseErrors }], } = await lint({
107
- code: '* { border-width: 1px 2px }',
108
- config: config(),
109
- });
110
- expect(parseErrors).toHaveLength(0);
111
- expect(warnings).toHaveLength(1);
112
- expect(warnings[0].text).toContain('border-block-width, border-inline-width');
113
- });
114
- });
115
- describe('limited properties configuration', () => {
116
- test('only check specified properties', async () => {
117
- const {
118
- // @ts-ignore
119
- results: [{ warnings, parseErrors }], } = await lint({
120
- code: '* { padding: 1rem 2rem; margin: 1rem 2rem; }',
121
- config: config({ properties: ['padding'] }),
122
- });
123
- expect(parseErrors).toHaveLength(0);
124
- expect(warnings).toHaveLength(1);
125
- expect(warnings[0].text).toContain('padding');
126
- });
127
- });
128
- describe('unsupported properties', () => {
129
- test('should not check properties not in the list', async () => {
130
- const {
131
- // @ts-ignore
132
- results: [{ warnings, parseErrors }], } = await lint({
133
- code: '* { background: url(a.png) no-repeat }',
134
- config: config(),
135
- });
136
- expect(parseErrors).toHaveLength(0);
137
- expect(warnings).toHaveLength(0);
138
- });
139
- });
140
- describe('complex values', () => {
141
- test('calc() function with multiple values', async () => {
142
- const {
143
- // @ts-ignore
144
- results: [{ warnings, parseErrors }], } = await lint({
145
- code: '* { padding: calc(1rem + 2px) 1rem }',
146
- config: config(),
147
- });
148
- expect(parseErrors).toHaveLength(0);
149
- expect(warnings).toHaveLength(1);
150
- });
151
- test('var() function with multiple values', async () => {
152
- const {
153
- // @ts-ignore
154
- results: [{ warnings, parseErrors }], } = await lint({
155
- code: '* { margin: var(--spacing) 2rem }',
156
- config: config(),
157
- });
158
- expect(parseErrors).toHaveLength(0);
159
- expect(warnings).toHaveLength(1);
160
- });
161
- });
162
- });
@@ -1,44 +0,0 @@
1
- import { parse } from 'postcss';
2
- import { describe, test, expect } from 'vitest';
3
- import { getValueType } from './get-value-type.js';
4
- /**
5
- *
6
- * @param css
7
- */
8
- function p(css) {
9
- const root = parse(css);
10
- const rule = root.first;
11
- const decl = rule.first;
12
- const nodeWithType = getValueType(decl);
13
- return nodeWithType?.map((node) => node.valueType) ?? null;
14
- }
15
- describe('getValueType', () => {
16
- test('flex', () => {
17
- expect(p('a { flex: 1 1 calc(var(--foo) * 10vw) }')).toEqual([
18
- 'number',
19
- 'number',
20
- 'length',
21
- ]);
22
- });
23
- test('max-width', () => {
24
- expect(p('a { max-width: 10% }')).toEqual(['length-percentage']);
25
- });
26
- test('height vw', () => {
27
- expect(p('a { height: 100vw }')).toEqual(['length']);
28
- });
29
- test('background', () => {
30
- expect(p('a { background: url(../../img/edit.png) no-repeat scroll 0 0 }')).toEqual([
31
- 'bg-image',
32
- 'repeat-style',
33
- 'attachment',
34
- 'length-percentage',
35
- 'length-percentage',
36
- ]);
37
- });
38
- test('SASS Variable', () => {
39
- expect(p('a { flex: 1 2 $basis }')).toEqual(['number', 'number', '$SASS_VARIABLE']);
40
- });
41
- test('SASS Variable Definition', () => {
42
- expect(p('a { $var: value }')).toEqual(null);
43
- });
44
- });