@culur/config-stylelint 0.0.0-dev-20240527064403
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/CHANGELOG.md +21 -0
- package/README.md +122 -0
- package/dist/index.cjs +190 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +188 -0
- package/dist/index.js.map +1 -0
- package/package.json +100 -0
- package/src/__tests__/dedent.ts +5 -0
- package/src/__tests__/index.ts +5 -0
- package/src/__tests__/mock.ts +36 -0
- package/src/__tests__/syntax.ts +11 -0
- package/src/__tests__/test-lint-and-fix.ts +55 -0
- package/src/__tests__/test-rule-or-config-value.ts +68 -0
- package/src/configs/css.test.ts +10 -0
- package/src/configs/css.ts +16 -0
- package/src/configs/scss.test.ts +9 -0
- package/src/configs/scss.ts +11 -0
- package/src/configs/shared.test.ts +9 -0
- package/src/configs/shared.ts +5 -0
- package/src/configs/vue.test.ts +18 -0
- package/src/configs/vue.ts +10 -0
- package/src/factory.test.ts +15 -0
- package/src/factory.ts +23 -0
- package/src/index.test.ts +9 -0
- package/src/index.ts +3 -0
- package/src/rules/at-rule-no-unknown.test.ts +43 -0
- package/src/rules/at-rule-no-unknown.ts +33 -0
- package/src/rules/declaration-block-no-redundant-longhand-properties.test.ts +38 -0
- package/src/rules/declaration-block-no-redundant-longhand-properties.ts +11 -0
- package/src/rules/dollar-variable-pattern.test.ts +27 -0
- package/src/rules/dollar-variable-pattern.ts +18 -0
- package/src/rules/function-no-unknown.test.ts +39 -0
- package/src/rules/function-no-unknown.ts +35 -0
- package/src/rules/order.test.ts +46 -0
- package/src/rules/order.ts +108 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/utils.test.ts +46 -0
- package/src/utils/utils.ts +44 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { hasSass, hasTailwind, hasVue } from '@culur/utils-packages';
|
|
2
|
+
import { defineRule, defineRules } from '~/utils';
|
|
3
|
+
|
|
4
|
+
export const functionNoUnknownRule = () => {
|
|
5
|
+
const ignoreFunctions = [
|
|
6
|
+
// https://tailwindcss.com/docs/functions-and-directives#functions
|
|
7
|
+
...(hasTailwind() //
|
|
8
|
+
? ['theme', 'screen']
|
|
9
|
+
: []),
|
|
10
|
+
|
|
11
|
+
// https://sass-lang.com/documentation/modules/list
|
|
12
|
+
// https://sass-lang.com/documentation/modules/math
|
|
13
|
+
// https://sass-lang.com/documentation/modules/color
|
|
14
|
+
...(hasSass() //
|
|
15
|
+
? [/^(list|math|color)\..+/]
|
|
16
|
+
: []),
|
|
17
|
+
|
|
18
|
+
// https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
|
19
|
+
...(hasVue() //
|
|
20
|
+
? ['v-bind']
|
|
21
|
+
: []),
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
return defineRule<boolean, { ignoreFunctions?: (string | RegExp)[] }>([
|
|
25
|
+
true,
|
|
26
|
+
ignoreFunctions.length > 0
|
|
27
|
+
? { ignoreFunctions }
|
|
28
|
+
: { ignoreFunctions: undefined },
|
|
29
|
+
]);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const functionNoUnknown = () =>
|
|
33
|
+
defineRules({
|
|
34
|
+
'function-no-unknown': functionNoUnknownRule(),
|
|
35
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { assert, expect } from 'vitest';
|
|
2
|
+
import { scss, testLintAndFix, testRuleValue } from '~/__tests__';
|
|
3
|
+
|
|
4
|
+
testRuleValue(
|
|
5
|
+
[
|
|
6
|
+
{ hasTailwind: false, hasSass: false, length: 8 },
|
|
7
|
+
{ hasTailwind: true, hasSass: false, length: 15 },
|
|
8
|
+
{ hasTailwind: false, hasSass: true, length: 16 },
|
|
9
|
+
{ hasTailwind: true, hasSass: true, length: 23 },
|
|
10
|
+
],
|
|
11
|
+
async () => {
|
|
12
|
+
const module = await import('./order');
|
|
13
|
+
return module.orderRule();
|
|
14
|
+
},
|
|
15
|
+
(rule, options) => {
|
|
16
|
+
assert(Array.isArray(rule));
|
|
17
|
+
|
|
18
|
+
assert(Array.isArray(rule[0]));
|
|
19
|
+
expect(rule[0]).toHaveLength(options.length);
|
|
20
|
+
|
|
21
|
+
assert(typeof rule[1] === 'object');
|
|
22
|
+
expect(rule[1]).toStrictEqual({ unspecified: 'bottom' });
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
testLintAndFix(async () => {
|
|
27
|
+
const module = await import('./order');
|
|
28
|
+
return {
|
|
29
|
+
plugins: ['stylelint-order'],
|
|
30
|
+
rules: module.order(),
|
|
31
|
+
};
|
|
32
|
+
}, [
|
|
33
|
+
{
|
|
34
|
+
hasTailwind: false,
|
|
35
|
+
hasSass: false,
|
|
36
|
+
code: scss`
|
|
37
|
+
a {
|
|
38
|
+
$variable: 1px;
|
|
39
|
+
--property: 3px;
|
|
40
|
+
@variable: 2px;
|
|
41
|
+
width: 4px;
|
|
42
|
+
}
|
|
43
|
+
`,
|
|
44
|
+
isError: false,
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { hasSass, hasTailwind } from '@culur/utils-packages';
|
|
2
|
+
import { defineRule, defineRules } from '~/utils';
|
|
3
|
+
|
|
4
|
+
// https://github.com/hudochenkov/stylelint-order/blob/master/rules/order/README.md#options
|
|
5
|
+
|
|
6
|
+
type Keyword =
|
|
7
|
+
| 'custom-properties' /**/ // Custom properties (e.g. --property: 10px;)
|
|
8
|
+
| 'dollar-variables' /***/ // Dollar variables (e.g. $variable)
|
|
9
|
+
| 'at-variables' /*******/ // At-variables (e.g. @variable available in Less syntax)
|
|
10
|
+
| 'declarations' /*******/ // CSS declarations (e.g. display: block)
|
|
11
|
+
| 'rules' /**************/ // Nested rules (e.g. a { span {} })
|
|
12
|
+
| 'at-rules' /***********/ // Nested at-rules (e.g. div { @media () {} })
|
|
13
|
+
| 'less-mixins'; /*******/ // Mixins in Less syntax (e.g. .mixin();)
|
|
14
|
+
|
|
15
|
+
interface AtRule {
|
|
16
|
+
type: 'at-rule';
|
|
17
|
+
name?: string;
|
|
18
|
+
parameter?: string | RegExp;
|
|
19
|
+
hasBlock?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface Rule {
|
|
23
|
+
type: 'rule';
|
|
24
|
+
selector?: string | RegExp;
|
|
25
|
+
name?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type PrimaryOption = Array<Keyword | AtRule | Rule>;
|
|
29
|
+
interface SecondaryOptions {
|
|
30
|
+
unspecified?: 'top' | 'bottom' | 'ignore';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* https://sass-lang.com/documentation/at-rules
|
|
35
|
+
*/
|
|
36
|
+
const atRuleScssImport: PrimaryOption = [
|
|
37
|
+
{ type: 'at-rule', name: 'use' },
|
|
38
|
+
{ type: 'at-rule', name: 'forward' },
|
|
39
|
+
{ type: 'at-rule', name: 'import' },
|
|
40
|
+
];
|
|
41
|
+
const atRuleScss: PrimaryOption = [
|
|
42
|
+
{ type: 'at-rule', name: 'function' },
|
|
43
|
+
{ type: 'at-rule', name: 'mixin' },
|
|
44
|
+
{ type: 'at-rule', name: 'extend' },
|
|
45
|
+
{ type: 'at-rule', name: 'include' },
|
|
46
|
+
{ type: 'at-rule', name: 'at-root' },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const variables: PrimaryOption = [
|
|
50
|
+
'dollar-variables', // $variable
|
|
51
|
+
'at-variables', // @variable
|
|
52
|
+
'custom-properties', // --property: 10px
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* https://tailwindcss.com/docs/functions-and-directives
|
|
57
|
+
*/
|
|
58
|
+
const atRuleTailwind: PrimaryOption = [
|
|
59
|
+
{ type: 'at-rule', name: 'tailwind' },
|
|
60
|
+
{ type: 'at-rule', name: 'layer' },
|
|
61
|
+
{ type: 'at-rule', name: 'apply' },
|
|
62
|
+
{ type: 'at-rule', name: 'config' },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* https://v2.tailwindcss.com/docs/functions-and-directives
|
|
67
|
+
*
|
|
68
|
+
* backwards compatible for v2
|
|
69
|
+
*/
|
|
70
|
+
const atRuleTailwindV2: PrimaryOption = [
|
|
71
|
+
{ type: 'at-rule', name: 'variants' },
|
|
72
|
+
{ type: 'at-rule', name: 'responsive' },
|
|
73
|
+
{ type: 'at-rule', name: 'screen' },
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Nested rules (e.g. a { span {} })
|
|
78
|
+
*/
|
|
79
|
+
const rules: PrimaryOption = [
|
|
80
|
+
{
|
|
81
|
+
type: 'rule',
|
|
82
|
+
selector: /^&::[\w-]+/,
|
|
83
|
+
},
|
|
84
|
+
'rules',
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
export const orderRule = () =>
|
|
88
|
+
defineRule<PrimaryOption, SecondaryOptions>([
|
|
89
|
+
[
|
|
90
|
+
...(hasSass() ? atRuleScssImport : []),
|
|
91
|
+
...(hasTailwind() ? atRuleTailwind : []),
|
|
92
|
+
...variables,
|
|
93
|
+
...(hasSass() ? atRuleScss : []),
|
|
94
|
+
...(hasTailwind() ? atRuleTailwindV2 : []),
|
|
95
|
+
'at-rules',
|
|
96
|
+
'less-mixins',
|
|
97
|
+
'declarations',
|
|
98
|
+
...rules,
|
|
99
|
+
],
|
|
100
|
+
{
|
|
101
|
+
unspecified: 'bottom',
|
|
102
|
+
},
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
export const order = () =>
|
|
106
|
+
defineRules({
|
|
107
|
+
'order/order': orderRule(),
|
|
108
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './utils';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { assert, describe, expect, expectTypeOf, it } from 'vitest';
|
|
2
|
+
import { defineRule, defineRules, mergeConfigs } from './utils';
|
|
3
|
+
|
|
4
|
+
describe('rules', () => {
|
|
5
|
+
it('defineRule & rules', () => {
|
|
6
|
+
type PrimaryOption = true;
|
|
7
|
+
interface SecondaryOptions {
|
|
8
|
+
ignore: ['comments'];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const blockNoEmptyRule = defineRule<PrimaryOption, SecondaryOptions>([
|
|
12
|
+
true,
|
|
13
|
+
{ ignore: ['comments'] },
|
|
14
|
+
]);
|
|
15
|
+
const blockNoEmpty = defineRules({ 'block-no-empty': blockNoEmptyRule });
|
|
16
|
+
|
|
17
|
+
assert(Array.isArray(blockNoEmptyRule));
|
|
18
|
+
expectTypeOf(blockNoEmpty).toBeObject();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('merge configs', () => {
|
|
23
|
+
it('should merge plugins', () => {
|
|
24
|
+
const mergedConfigs = mergeConfigs(
|
|
25
|
+
{ plugins: ['plugin-a'] }, //
|
|
26
|
+
{ plugins: ['plugin-b'] },
|
|
27
|
+
{ plugins: [] },
|
|
28
|
+
{},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
assert(Array.isArray(mergedConfigs.plugins));
|
|
32
|
+
expect(mergedConfigs.plugins).toHaveLength(2);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should merge overrides', () => {
|
|
36
|
+
const mergedConfigs = mergeConfigs(
|
|
37
|
+
{ overrides: [{ files: 'a.css' }] },
|
|
38
|
+
{ overrides: [{ files: 'b.css' }] },
|
|
39
|
+
{ overrides: [] },
|
|
40
|
+
{},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
assert(Array.isArray(mergedConfigs.overrides));
|
|
44
|
+
expect(mergedConfigs.overrides).toHaveLength(2);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Config, ConfigRuleSettings } from 'stylelint';
|
|
2
|
+
|
|
3
|
+
export type ConfigRulePrimaryOption =
|
|
4
|
+
| boolean
|
|
5
|
+
| number
|
|
6
|
+
| string
|
|
7
|
+
| Array<boolean | number | string | object>;
|
|
8
|
+
export type ConfigRuleSecondaryOptions = object;
|
|
9
|
+
|
|
10
|
+
export const defineRule = <
|
|
11
|
+
PrimaryOption extends ConfigRulePrimaryOption,
|
|
12
|
+
SecondaryOptions extends ConfigRuleSecondaryOptions,
|
|
13
|
+
>(
|
|
14
|
+
rule: ConfigRuleSettings<PrimaryOption, SecondaryOptions>,
|
|
15
|
+
) => rule;
|
|
16
|
+
|
|
17
|
+
export const defineRules = <TRules extends Config['rules']>(rule: TRules) =>
|
|
18
|
+
rule;
|
|
19
|
+
|
|
20
|
+
// clone function mergeConfigs from stylelint repository
|
|
21
|
+
export const mergeConfigs = (
|
|
22
|
+
...configsOrNull: (Config | undefined | null | false)[]
|
|
23
|
+
) => {
|
|
24
|
+
const configs = configsOrNull.filter((c): c is Config => !!c);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
plugins: [...new Set(configs.flatMap(c => c.plugins ?? []))],
|
|
28
|
+
overrides: [...new Set(configs.flatMap(c => c.overrides ?? []))],
|
|
29
|
+
extends: configs
|
|
30
|
+
.flatMap(c => c.extends ?? [])
|
|
31
|
+
.filter((item, index, arr) => arr.lastIndexOf(item) === index),
|
|
32
|
+
rules: configs.reduce(
|
|
33
|
+
(acc, config) => ({ ...acc, ...config.rules }),
|
|
34
|
+
{} as Config['rules'],
|
|
35
|
+
),
|
|
36
|
+
...configs.reduce(
|
|
37
|
+
(
|
|
38
|
+
acc,
|
|
39
|
+
{ plugins: _p, overrides: _o, extends: _e, rules: _r, ...config },
|
|
40
|
+
) => ({ ...acc, ...config }),
|
|
41
|
+
{} as Config,
|
|
42
|
+
),
|
|
43
|
+
};
|
|
44
|
+
};
|