@d-zero/stylelint-rules 5.0.0-alpha.36 → 5.0.0-alpha.37

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/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
+ import component from './rules/component/index.js';
1
2
  import declarationValueTypeDisallowedList from './rules/declaration-value-type-disallowed-list/index.js';
2
- export default [declarationValueTypeDisallowedList];
3
+ export default [component, declarationValueTypeDisallowedList];
@@ -0,0 +1,71 @@
1
+ import path from 'node:path';
2
+ import selectorParser from 'postcss-selector-parser';
3
+ import stylelint from 'stylelint';
4
+ import { createRule } from '../../utils/create-rule.js';
5
+ export default createRule({
6
+ name: 'component',
7
+ rule: (ruleName) => (primary) => {
8
+ const allowMultipleSelectors = primary.allowMultipleSelectors ?? false;
9
+ return (root, result) => {
10
+ const fileName = root.source?.input.file;
11
+ if (!fileName) {
12
+ return;
13
+ }
14
+ const ext = path.extname(fileName);
15
+ const originalBasename = path.basename(fileName, ext);
16
+ const basename = ['.scss', '.sass'].includes(ext)
17
+ ? originalBasename.replace(/^_/, '')
18
+ : originalBasename;
19
+ const rules = root.nodes.filter((node) => node.type === 'rule');
20
+ const [firstRule, ...overleftRules] = rules;
21
+ for (const rule of overleftRules) {
22
+ stylelint.utils.report({
23
+ result,
24
+ ruleName,
25
+ message: '1つのファイルに定義できるコンポーネントクラスは1つだけです',
26
+ node: rule,
27
+ });
28
+ }
29
+ if (!firstRule) {
30
+ stylelint.utils.report({
31
+ result,
32
+ ruleName,
33
+ message: 'コンポーネントが定義されていません',
34
+ node: root,
35
+ });
36
+ return;
37
+ }
38
+ const selectors = [];
39
+ selectorParser((parsedRoot) => {
40
+ for (const node of parsedRoot.nodes) {
41
+ selectors.push(node);
42
+ }
43
+ }).processSync(firstRule.selector);
44
+ const [firstSelector, ...multipleSelectors] = selectors;
45
+ if (!firstSelector) {
46
+ throw new Error('Do not have a selector');
47
+ }
48
+ for (const node of firstSelector.nodes) {
49
+ if (node.type === 'class') {
50
+ const className = node.value;
51
+ if (className !== basename) {
52
+ stylelint.utils.report({
53
+ result,
54
+ ruleName,
55
+ message: 'クラス名がファイル名と一致しません',
56
+ node: firstRule,
57
+ });
58
+ }
59
+ }
60
+ }
61
+ if (!allowMultipleSelectors && multipleSelectors.length > 0) {
62
+ stylelint.utils.report({
63
+ result,
64
+ ruleName,
65
+ message: 'セレクタの定義は1つだけです',
66
+ node: firstRule,
67
+ });
68
+ }
69
+ };
70
+ },
71
+ });
@@ -0,0 +1,93 @@
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: 'クラス名がファイル名と一致しません',
42
+ },
43
+ {
44
+ column: 1,
45
+ endColumn: 70,
46
+ endLine: 1,
47
+ line: 1,
48
+ rule: '@d-zero/component',
49
+ severity: 'error',
50
+ text: 'セレクタの定義は1つだけです',
51
+ },
52
+ ]);
53
+ });
54
+ });
55
+ describe('Partial Name', () => {
56
+ test('match', async () => {
57
+ const {
58
+ // @ts-ignore
59
+ results: [{ warnings, parseErrors }], } = await lint({
60
+ codeFilename: '_c-component.scss',
61
+ code: '.c-component { color: currentColor; }',
62
+ config: config({}),
63
+ });
64
+ expect(parseErrors).toHaveLength(0);
65
+ expect(warnings).toHaveLength(0);
66
+ });
67
+ });
68
+ describe('Options', () => {
69
+ test('allowMultipleSelectors: false', async () => {
70
+ const {
71
+ // @ts-ignore
72
+ results: [{ warnings, parseErrors }], } = await lint({
73
+ codeFilename: '_c-component.scss',
74
+ code: '.c-component, .x-specific-class-name { color: currentColor; }',
75
+ config: config({}),
76
+ });
77
+ expect(parseErrors).toHaveLength(0);
78
+ expect(warnings).toHaveLength(1);
79
+ });
80
+ test('allowMultipleSelectors: true', async () => {
81
+ const {
82
+ // @ts-ignore
83
+ results: [{ warnings, parseErrors }], } = await lint({
84
+ codeFilename: '_c-component.scss',
85
+ code: '.c-component, .x-specific-class-name { color: currentColor; }',
86
+ config: config({
87
+ allowMultipleSelectors: true,
88
+ }),
89
+ });
90
+ expect(parseErrors).toHaveLength(0);
91
+ expect(warnings).toHaveLength(0);
92
+ });
93
+ });
@@ -8,7 +8,7 @@ export function createRule(setting) {
8
8
  url: `${REPOSITORY_URL}/${name}`,
9
9
  };
10
10
  const messages = stylelint.utils.ruleMessages(ruleName, {
11
- rejected: setting.rejected,
11
+ rejected: setting.rejected ?? (() => ''),
12
12
  });
13
13
  const ruleBase = setting.rule(ruleName, messages);
14
14
  // @ts-ignore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d-zero/stylelint-rules",
3
- "version": "5.0.0-alpha.36",
3
+ "version": "5.0.0-alpha.37",
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.",
@@ -23,13 +23,14 @@
23
23
  "build": "tsc"
24
24
  },
25
25
  "dependencies": {
26
- "@d-zero/csstree-scss-syntax": "5.0.0-alpha.36",
26
+ "@d-zero/csstree-scss-syntax": "5.0.0-alpha.37",
27
27
  "css-tree": "2.3.1",
28
+ "postcss-selector-parser": "6.1.0",
28
29
  "postcss-value-parser": "4.2.0",
29
30
  "stylelint": "16.6.1"
30
31
  },
31
32
  "devDependencies": {
32
33
  "postcss": "8.4.38"
33
34
  },
34
- "gitHead": "4f91c703f88f9bc8a0458e8c369d6a37e980af7d"
35
+ "gitHead": "9f7e27baf2fce73a12662af92396e4d01cf821f3"
35
36
  }