@maz-ui/stylelint-config 5.0.0-beta.0
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/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/index.d.mts +196 -0
- package/dist/index.d.ts +196 -0
- package/dist/index.mjs +243 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 LouisMazel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @maz-ui/stylelint-config
|
|
2
|
+
|
|
3
|
+
Reusable [Stylelint](https://stylelint.io/) configuration for Vue/Nuxt/JS/TS projects, with first-class Tailwind CSS v4 support, RTL-friendly logical properties, and SCSS opt-in.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Stylelint Standard base** — opinionated, maintained by the Stylelint team
|
|
8
|
+
- 🎨 **Tailwind v4** — at-rule whitelist, `import-notation: string` enforced
|
|
9
|
+
- 🌍 **RTL-friendly** — logical properties via `stylelint-use-logical-spec`
|
|
10
|
+
- 🔠 **Property ordering** — Recess-based by default, alphabetical or off
|
|
11
|
+
- 💚 **Vue/HTML/SCSS** — auto-detected from your `package.json`
|
|
12
|
+
- 🛠 **Configurable** — toggle features, override rules, append per-file overrides
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add -D @maz-ui/stylelint-config stylelint
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Basic usage
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
// stylelint.config.mjs
|
|
24
|
+
import { defineConfig } from '@maz-ui/stylelint-config'
|
|
25
|
+
|
|
26
|
+
export default defineConfig()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Vue, Tailwind and SCSS are auto-detected from your `package.json`.
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
defineConfig({
|
|
35
|
+
vue: true, // auto-detected from `vue`/`nuxt` deps
|
|
36
|
+
html: false,
|
|
37
|
+
tailwind: true, // auto-detected from `tailwindcss` dep
|
|
38
|
+
scss: false, // auto-detected from `sass`/`sass-embedded`/`node-sass`
|
|
39
|
+
logical: true, // recommend logical CSS properties (RTL-friendly)
|
|
40
|
+
order: 'recess', // 'recess' | 'alphabetical' | false
|
|
41
|
+
|
|
42
|
+
ignores: ['**/legacy/**'],
|
|
43
|
+
rules: {
|
|
44
|
+
'no-descending-specificity': null,
|
|
45
|
+
},
|
|
46
|
+
overrides: [
|
|
47
|
+
{ files: ['**/*.legacy.css'], rules: { 'color-no-invalid-hex': null } },
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Documentation
|
|
53
|
+
|
|
54
|
+
See the full documentation at [maz-ui.com/ecosystem/stylelint-config](https://maz-ui.com/ecosystem/stylelint-config).
|
|
55
|
+
|
|
56
|
+
## Compatibility
|
|
57
|
+
|
|
58
|
+
- **Stylelint** >=16 <18
|
|
59
|
+
- **Node.js** >=20.19.0
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { Config } from 'stylelint';
|
|
2
|
+
|
|
3
|
+
type StylelintConfig = Config;
|
|
4
|
+
type StylelintRules = Config['rules'];
|
|
5
|
+
type StylelintOverride = NonNullable<Config['overrides']>[number];
|
|
6
|
+
/**
|
|
7
|
+
* Property order strategy.
|
|
8
|
+
*
|
|
9
|
+
* - `'recess'`: Recess-based ordering (`stylelint-config-recess-order`).
|
|
10
|
+
* Pragmatic, widely adopted, groups related properties together.
|
|
11
|
+
* - `'alphabetical'`: Strict A→Z ordering. Easy mental model, slightly worse
|
|
12
|
+
* diffs than recess.
|
|
13
|
+
* - `false`: Disable ordering rules entirely.
|
|
14
|
+
*/
|
|
15
|
+
type StylelintOrderStrategy = 'recess' | 'alphabetical' | false;
|
|
16
|
+
interface MazStylelintOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Enable Vue support — extends `stylelint-config-recommended-vue` and
|
|
19
|
+
* registers `postcss-html` for `<style>` blocks in `.vue` files.
|
|
20
|
+
*
|
|
21
|
+
* Auto-detected from `vue` or `nuxt` in the consuming `package.json`
|
|
22
|
+
* unless explicitly set.
|
|
23
|
+
*/
|
|
24
|
+
vue?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Enable HTML support — extends `stylelint-config-html` and registers
|
|
27
|
+
* `postcss-html` for `<style>` blocks in `.html`, `.svg` and similar files.
|
|
28
|
+
*
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
html?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Enable Tailwind CSS support — whitelists the v4 at-rules
|
|
34
|
+
* (`@apply`, `@reference`, `@variant`, `@theme`, `@layer`, `@utility`,
|
|
35
|
+
* `@source`, `@custom-variant`, `@tailwind`, `@screen`, `@starting-style`)
|
|
36
|
+
* and relaxes patterns that conflict with Tailwind's prefixed classes.
|
|
37
|
+
*
|
|
38
|
+
* Auto-detected from `tailwindcss` in the consuming `package.json` unless
|
|
39
|
+
* explicitly set.
|
|
40
|
+
*/
|
|
41
|
+
tailwind?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Enable SCSS support — extends `stylelint-config-recommended-scss` and
|
|
44
|
+
* registers `postcss-scss` for `.scss` files.
|
|
45
|
+
*
|
|
46
|
+
* Auto-detected from `sass` / `sass-embedded` / `node-sass` /
|
|
47
|
+
* `stylelint-scss` in the consuming `package.json` unless explicitly set.
|
|
48
|
+
*/
|
|
49
|
+
scss?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Encourage logical CSS properties (`inset-block`, `margin-inline-start`,
|
|
52
|
+
* `padding-inline`, …) over their physical equivalents (`top`, `margin-left`,
|
|
53
|
+
* `padding-left`, …). Highly recommended for RTL/LTR-aware design systems.
|
|
54
|
+
*
|
|
55
|
+
* @default true
|
|
56
|
+
*/
|
|
57
|
+
logical?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Property order strategy. See {@link StylelintOrderStrategy}.
|
|
60
|
+
*
|
|
61
|
+
* @default 'recess'
|
|
62
|
+
*/
|
|
63
|
+
order?: StylelintOrderStrategy;
|
|
64
|
+
/**
|
|
65
|
+
* Glob patterns of files Stylelint should ignore, merged with the package's
|
|
66
|
+
* sensible defaults (`node_modules/**`, `dist/**`, `coverage/**`, …).
|
|
67
|
+
*/
|
|
68
|
+
ignores?: string[];
|
|
69
|
+
/**
|
|
70
|
+
* Replace the default ignore list with this one (no merge).
|
|
71
|
+
*/
|
|
72
|
+
ignoresOverride?: string[];
|
|
73
|
+
/**
|
|
74
|
+
* Custom rules merged on top of the resolved configuration. Use this to
|
|
75
|
+
* tweak severity, disable rules or override their options.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* defineConfig({
|
|
80
|
+
* rules: {
|
|
81
|
+
* 'no-descending-specificity': null,
|
|
82
|
+
* 'declaration-block-no-redundant-longhand-properties': [true, {
|
|
83
|
+
* ignoreShorthands: ['grid-template'],
|
|
84
|
+
* }],
|
|
85
|
+
* },
|
|
86
|
+
* })
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
rules?: StylelintRules;
|
|
90
|
+
/**
|
|
91
|
+
* Additional overrides appended to the resolved configuration. Use this to
|
|
92
|
+
* apply rules to specific file globs (legacy code, generated CSS, …).
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* defineConfig({
|
|
97
|
+
* overrides: [
|
|
98
|
+
* { files: ['**\/*.legacy.css'], rules: { 'no-descending-specificity': null } },
|
|
99
|
+
* ],
|
|
100
|
+
* })
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
overrides?: StylelintOverride[];
|
|
104
|
+
/**
|
|
105
|
+
* Additional plugins merged on top of the resolved configuration.
|
|
106
|
+
*/
|
|
107
|
+
plugins?: NonNullable<Config['plugins']>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Baseline rules applied on top of `stylelint-config-standard`.
|
|
112
|
+
*
|
|
113
|
+
* The goal is **opinionated but pragmatic**: keep the standard config's
|
|
114
|
+
* idiomatic-CSS rules, soften the few that fight nested component styling
|
|
115
|
+
* (Vue, BEM, Tailwind), and add a couple of universally-good rules that are
|
|
116
|
+
* not in the standard config.
|
|
117
|
+
*/
|
|
118
|
+
declare const baseRules: StylelintRules;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Glob patterns Stylelint should ignore by default. Mirrors the eslint-config
|
|
122
|
+
* defaults plus a few CSS-specific build outputs.
|
|
123
|
+
*/
|
|
124
|
+
declare const GLOBAL_IGNORES: string[];
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Flag every physical property/value that has a logical equivalent.
|
|
128
|
+
* `liberty/use-logical-spec` is the rule name exposed by the plugin.
|
|
129
|
+
*
|
|
130
|
+
* The third argument `except` is intentionally empty — we want a clean
|
|
131
|
+
* baseline. Teams that need to allow specific physical props (e.g. they
|
|
132
|
+
* deliberately compute `top: 0` for a fixed header that can't be flipped)
|
|
133
|
+
* can override the rule in their own config.
|
|
134
|
+
*/
|
|
135
|
+
declare const logicalRules: StylelintRules;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Rules applied when SCSS support is enabled. Mirrors the at-rule whitelist
|
|
139
|
+
* so projects that use SCSS *and* Tailwind don't get false positives from
|
|
140
|
+
* `scss/at-rule-no-unknown`.
|
|
141
|
+
*/
|
|
142
|
+
declare function scssRules(includeTailwind: boolean): StylelintRules;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Tailwind v4 at-rules. New ones can be added without bumping a major
|
|
146
|
+
* version since the rule treats unknown at-rules as warnings, not errors,
|
|
147
|
+
* but we still prefer to keep this list current.
|
|
148
|
+
*
|
|
149
|
+
* Source: https://tailwindcss.com/docs (v4 directives)
|
|
150
|
+
*/
|
|
151
|
+
declare const TAILWIND_AT_RULES: string[];
|
|
152
|
+
/**
|
|
153
|
+
* Rules to whitelist Tailwind's at-rules and relax patterns that conflict
|
|
154
|
+
* with Tailwind-prefixed classes (`maz:flex`, `dark:bg-…`).
|
|
155
|
+
*
|
|
156
|
+
* When SCSS is also enabled, `at-rule-no-unknown` is disabled by
|
|
157
|
+
* `stylelint-config-recommended-scss` in favor of `scss/at-rule-no-unknown`
|
|
158
|
+
* — keep that rule out of this set and merge it in only when SCSS is off.
|
|
159
|
+
*/
|
|
160
|
+
declare const tailwindRules: StylelintRules;
|
|
161
|
+
/**
|
|
162
|
+
* Tailwind at-rule whitelist for plain CSS contexts (no SCSS).
|
|
163
|
+
*
|
|
164
|
+
* Merged on top of `tailwindRules` only when SCSS support is disabled —
|
|
165
|
+
* with SCSS on, `scss/at-rule-no-unknown` covers both Tailwind and SCSS
|
|
166
|
+
* directives, and `at-rule-no-unknown` itself is disabled by the SCSS
|
|
167
|
+
* recommended config.
|
|
168
|
+
*/
|
|
169
|
+
declare const tailwindAtRuleNoUnknown: StylelintRules;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Build a Stylelint config tailored for Vue/Nuxt/JS/TS/Tailwind projects.
|
|
173
|
+
*
|
|
174
|
+
* Defaults to the Stylelint Standard rules with a few opinions softened
|
|
175
|
+
* (BEM/Tailwind class patterns, deep nesting, `v-bind()`). Vue, Tailwind
|
|
176
|
+
* and SCSS support is auto-detected from the consuming `package.json` —
|
|
177
|
+
* pass the matching boolean explicitly to opt in or out.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* // stylelint.config.mjs
|
|
182
|
+
* import { defineConfig } from '@maz-ui/stylelint-config'
|
|
183
|
+
*
|
|
184
|
+
* export default defineConfig({
|
|
185
|
+
* tailwind: true,
|
|
186
|
+
* logical: true,
|
|
187
|
+
* rules: {
|
|
188
|
+
* 'no-descending-specificity': null,
|
|
189
|
+
* },
|
|
190
|
+
* })
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare function defineConfig(options?: MazStylelintOptions): StylelintConfig;
|
|
194
|
+
|
|
195
|
+
export { GLOBAL_IGNORES, TAILWIND_AT_RULES, baseRules, defineConfig, logicalRules, scssRules, tailwindAtRuleNoUnknown, tailwindRules };
|
|
196
|
+
export type { MazStylelintOptions, StylelintConfig, StylelintOrderStrategy, StylelintOverride, StylelintRules };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { Config } from 'stylelint';
|
|
2
|
+
|
|
3
|
+
type StylelintConfig = Config;
|
|
4
|
+
type StylelintRules = Config['rules'];
|
|
5
|
+
type StylelintOverride = NonNullable<Config['overrides']>[number];
|
|
6
|
+
/**
|
|
7
|
+
* Property order strategy.
|
|
8
|
+
*
|
|
9
|
+
* - `'recess'`: Recess-based ordering (`stylelint-config-recess-order`).
|
|
10
|
+
* Pragmatic, widely adopted, groups related properties together.
|
|
11
|
+
* - `'alphabetical'`: Strict A→Z ordering. Easy mental model, slightly worse
|
|
12
|
+
* diffs than recess.
|
|
13
|
+
* - `false`: Disable ordering rules entirely.
|
|
14
|
+
*/
|
|
15
|
+
type StylelintOrderStrategy = 'recess' | 'alphabetical' | false;
|
|
16
|
+
interface MazStylelintOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Enable Vue support — extends `stylelint-config-recommended-vue` and
|
|
19
|
+
* registers `postcss-html` for `<style>` blocks in `.vue` files.
|
|
20
|
+
*
|
|
21
|
+
* Auto-detected from `vue` or `nuxt` in the consuming `package.json`
|
|
22
|
+
* unless explicitly set.
|
|
23
|
+
*/
|
|
24
|
+
vue?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Enable HTML support — extends `stylelint-config-html` and registers
|
|
27
|
+
* `postcss-html` for `<style>` blocks in `.html`, `.svg` and similar files.
|
|
28
|
+
*
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
html?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Enable Tailwind CSS support — whitelists the v4 at-rules
|
|
34
|
+
* (`@apply`, `@reference`, `@variant`, `@theme`, `@layer`, `@utility`,
|
|
35
|
+
* `@source`, `@custom-variant`, `@tailwind`, `@screen`, `@starting-style`)
|
|
36
|
+
* and relaxes patterns that conflict with Tailwind's prefixed classes.
|
|
37
|
+
*
|
|
38
|
+
* Auto-detected from `tailwindcss` in the consuming `package.json` unless
|
|
39
|
+
* explicitly set.
|
|
40
|
+
*/
|
|
41
|
+
tailwind?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Enable SCSS support — extends `stylelint-config-recommended-scss` and
|
|
44
|
+
* registers `postcss-scss` for `.scss` files.
|
|
45
|
+
*
|
|
46
|
+
* Auto-detected from `sass` / `sass-embedded` / `node-sass` /
|
|
47
|
+
* `stylelint-scss` in the consuming `package.json` unless explicitly set.
|
|
48
|
+
*/
|
|
49
|
+
scss?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Encourage logical CSS properties (`inset-block`, `margin-inline-start`,
|
|
52
|
+
* `padding-inline`, …) over their physical equivalents (`top`, `margin-left`,
|
|
53
|
+
* `padding-left`, …). Highly recommended for RTL/LTR-aware design systems.
|
|
54
|
+
*
|
|
55
|
+
* @default true
|
|
56
|
+
*/
|
|
57
|
+
logical?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Property order strategy. See {@link StylelintOrderStrategy}.
|
|
60
|
+
*
|
|
61
|
+
* @default 'recess'
|
|
62
|
+
*/
|
|
63
|
+
order?: StylelintOrderStrategy;
|
|
64
|
+
/**
|
|
65
|
+
* Glob patterns of files Stylelint should ignore, merged with the package's
|
|
66
|
+
* sensible defaults (`node_modules/**`, `dist/**`, `coverage/**`, …).
|
|
67
|
+
*/
|
|
68
|
+
ignores?: string[];
|
|
69
|
+
/**
|
|
70
|
+
* Replace the default ignore list with this one (no merge).
|
|
71
|
+
*/
|
|
72
|
+
ignoresOverride?: string[];
|
|
73
|
+
/**
|
|
74
|
+
* Custom rules merged on top of the resolved configuration. Use this to
|
|
75
|
+
* tweak severity, disable rules or override their options.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* defineConfig({
|
|
80
|
+
* rules: {
|
|
81
|
+
* 'no-descending-specificity': null,
|
|
82
|
+
* 'declaration-block-no-redundant-longhand-properties': [true, {
|
|
83
|
+
* ignoreShorthands: ['grid-template'],
|
|
84
|
+
* }],
|
|
85
|
+
* },
|
|
86
|
+
* })
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
rules?: StylelintRules;
|
|
90
|
+
/**
|
|
91
|
+
* Additional overrides appended to the resolved configuration. Use this to
|
|
92
|
+
* apply rules to specific file globs (legacy code, generated CSS, …).
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* defineConfig({
|
|
97
|
+
* overrides: [
|
|
98
|
+
* { files: ['**\/*.legacy.css'], rules: { 'no-descending-specificity': null } },
|
|
99
|
+
* ],
|
|
100
|
+
* })
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
overrides?: StylelintOverride[];
|
|
104
|
+
/**
|
|
105
|
+
* Additional plugins merged on top of the resolved configuration.
|
|
106
|
+
*/
|
|
107
|
+
plugins?: NonNullable<Config['plugins']>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Baseline rules applied on top of `stylelint-config-standard`.
|
|
112
|
+
*
|
|
113
|
+
* The goal is **opinionated but pragmatic**: keep the standard config's
|
|
114
|
+
* idiomatic-CSS rules, soften the few that fight nested component styling
|
|
115
|
+
* (Vue, BEM, Tailwind), and add a couple of universally-good rules that are
|
|
116
|
+
* not in the standard config.
|
|
117
|
+
*/
|
|
118
|
+
declare const baseRules: StylelintRules;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Glob patterns Stylelint should ignore by default. Mirrors the eslint-config
|
|
122
|
+
* defaults plus a few CSS-specific build outputs.
|
|
123
|
+
*/
|
|
124
|
+
declare const GLOBAL_IGNORES: string[];
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Flag every physical property/value that has a logical equivalent.
|
|
128
|
+
* `liberty/use-logical-spec` is the rule name exposed by the plugin.
|
|
129
|
+
*
|
|
130
|
+
* The third argument `except` is intentionally empty — we want a clean
|
|
131
|
+
* baseline. Teams that need to allow specific physical props (e.g. they
|
|
132
|
+
* deliberately compute `top: 0` for a fixed header that can't be flipped)
|
|
133
|
+
* can override the rule in their own config.
|
|
134
|
+
*/
|
|
135
|
+
declare const logicalRules: StylelintRules;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Rules applied when SCSS support is enabled. Mirrors the at-rule whitelist
|
|
139
|
+
* so projects that use SCSS *and* Tailwind don't get false positives from
|
|
140
|
+
* `scss/at-rule-no-unknown`.
|
|
141
|
+
*/
|
|
142
|
+
declare function scssRules(includeTailwind: boolean): StylelintRules;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Tailwind v4 at-rules. New ones can be added without bumping a major
|
|
146
|
+
* version since the rule treats unknown at-rules as warnings, not errors,
|
|
147
|
+
* but we still prefer to keep this list current.
|
|
148
|
+
*
|
|
149
|
+
* Source: https://tailwindcss.com/docs (v4 directives)
|
|
150
|
+
*/
|
|
151
|
+
declare const TAILWIND_AT_RULES: string[];
|
|
152
|
+
/**
|
|
153
|
+
* Rules to whitelist Tailwind's at-rules and relax patterns that conflict
|
|
154
|
+
* with Tailwind-prefixed classes (`maz:flex`, `dark:bg-…`).
|
|
155
|
+
*
|
|
156
|
+
* When SCSS is also enabled, `at-rule-no-unknown` is disabled by
|
|
157
|
+
* `stylelint-config-recommended-scss` in favor of `scss/at-rule-no-unknown`
|
|
158
|
+
* — keep that rule out of this set and merge it in only when SCSS is off.
|
|
159
|
+
*/
|
|
160
|
+
declare const tailwindRules: StylelintRules;
|
|
161
|
+
/**
|
|
162
|
+
* Tailwind at-rule whitelist for plain CSS contexts (no SCSS).
|
|
163
|
+
*
|
|
164
|
+
* Merged on top of `tailwindRules` only when SCSS support is disabled —
|
|
165
|
+
* with SCSS on, `scss/at-rule-no-unknown` covers both Tailwind and SCSS
|
|
166
|
+
* directives, and `at-rule-no-unknown` itself is disabled by the SCSS
|
|
167
|
+
* recommended config.
|
|
168
|
+
*/
|
|
169
|
+
declare const tailwindAtRuleNoUnknown: StylelintRules;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Build a Stylelint config tailored for Vue/Nuxt/JS/TS/Tailwind projects.
|
|
173
|
+
*
|
|
174
|
+
* Defaults to the Stylelint Standard rules with a few opinions softened
|
|
175
|
+
* (BEM/Tailwind class patterns, deep nesting, `v-bind()`). Vue, Tailwind
|
|
176
|
+
* and SCSS support is auto-detected from the consuming `package.json` —
|
|
177
|
+
* pass the matching boolean explicitly to opt in or out.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* // stylelint.config.mjs
|
|
182
|
+
* import { defineConfig } from '@maz-ui/stylelint-config'
|
|
183
|
+
*
|
|
184
|
+
* export default defineConfig({
|
|
185
|
+
* tailwind: true,
|
|
186
|
+
* logical: true,
|
|
187
|
+
* rules: {
|
|
188
|
+
* 'no-descending-specificity': null,
|
|
189
|
+
* },
|
|
190
|
+
* })
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare function defineConfig(options?: MazStylelintOptions): StylelintConfig;
|
|
194
|
+
|
|
195
|
+
export { GLOBAL_IGNORES, TAILWIND_AT_RULES, baseRules, defineConfig, logicalRules, scssRules, tailwindAtRuleNoUnknown, tailwindRules };
|
|
196
|
+
export type { MazStylelintOptions, StylelintConfig, StylelintOrderStrategy, StylelintOverride, StylelintRules };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
const baseRules = {
|
|
5
|
+
/**
|
|
6
|
+
* `selector-class-pattern` enforces kebab-case by default. Component
|
|
7
|
+
* libraries that ship BEM (`m-btn__icon`) or Tailwind-prefixed classes
|
|
8
|
+
* (`maz:flex`) trip over it constantly.
|
|
9
|
+
*/
|
|
10
|
+
"selector-class-pattern": null,
|
|
11
|
+
/**
|
|
12
|
+
* `no-descending-specificity` fires false positives in scoped Vue/CSS
|
|
13
|
+
* modules where the nesting depth simply reflects the markup. Off by
|
|
14
|
+
* default; teams that want it can re-enable in their own override.
|
|
15
|
+
*/
|
|
16
|
+
"no-descending-specificity": null,
|
|
17
|
+
/**
|
|
18
|
+
* Allow author-time use of Vue's `v-bind()` and the (now widely supported)
|
|
19
|
+
* native `light-dark()` color function.
|
|
20
|
+
*/
|
|
21
|
+
"function-no-unknown": [true, {
|
|
22
|
+
ignoreFunctions: ["v-bind", "light-dark"]
|
|
23
|
+
}],
|
|
24
|
+
/**
|
|
25
|
+
* Native CSS nesting now allows scoping inside another at-rule without an
|
|
26
|
+
* explicit selector — the `& selector` form is valid in 2026 browsers and
|
|
27
|
+
* Tailwind v4 emits it. Disable the warning.
|
|
28
|
+
*/
|
|
29
|
+
"nesting-selector-no-missing-scoping-root": null,
|
|
30
|
+
/**
|
|
31
|
+
* Allow `:deep()` (Vue scoped-style descent) and `:global()` (CSS Modules)
|
|
32
|
+
* out of the box.
|
|
33
|
+
*/
|
|
34
|
+
"selector-pseudo-class-no-unknown": [true, {
|
|
35
|
+
ignorePseudoClasses: ["deep", "global", "slotted", "where"]
|
|
36
|
+
}],
|
|
37
|
+
/**
|
|
38
|
+
* Custom property names follow kebab-case in our public CSS APIs
|
|
39
|
+
* (`--maz-primary`, `--m-btn-bg`). Standard config already allows this but
|
|
40
|
+
* we make the pattern explicit to surface the convention.
|
|
41
|
+
*/
|
|
42
|
+
"custom-property-pattern": [
|
|
43
|
+
"^[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?:--[a-z0-9-]+)?$",
|
|
44
|
+
{
|
|
45
|
+
message: "Custom properties should be kebab-case (e.g. --my-token, --my-block--variant)."
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
/**
|
|
49
|
+
* Be lenient on shorthand collapsing — `grid-template`/`grid-template-areas`
|
|
50
|
+
* and the `inset` longhands are routinely written long-form for readability.
|
|
51
|
+
*/
|
|
52
|
+
"declaration-block-no-redundant-longhand-properties": [true, {
|
|
53
|
+
ignoreShorthands: ["grid-template", "inset"]
|
|
54
|
+
}]
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const GLOBAL_IGNORES = [
|
|
58
|
+
"**/node_modules/**",
|
|
59
|
+
"**/dist/**",
|
|
60
|
+
"**/build/**",
|
|
61
|
+
"**/output/**",
|
|
62
|
+
"**/.output/**",
|
|
63
|
+
"**/coverage/**",
|
|
64
|
+
"**/temp/**",
|
|
65
|
+
"**/.temp/**",
|
|
66
|
+
"**/tmp/**",
|
|
67
|
+
"**/.tmp/**",
|
|
68
|
+
"**/.history/**",
|
|
69
|
+
"**/.vitepress/cache/**",
|
|
70
|
+
"**/.nuxt/**",
|
|
71
|
+
"**/.next/**",
|
|
72
|
+
"**/.svelte-kit/**",
|
|
73
|
+
"**/.vercel/**",
|
|
74
|
+
"**/.cache/**",
|
|
75
|
+
"**/*.min.css",
|
|
76
|
+
"**/*.min.scss"
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const LOGICAL_PLUGIN = ["stylelint-use-logical-spec"];
|
|
80
|
+
const logicalRules = {
|
|
81
|
+
"liberty/use-logical-spec": ["always"]
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const TAILWIND_AT_RULES = [
|
|
85
|
+
// v4 core
|
|
86
|
+
"theme",
|
|
87
|
+
"utility",
|
|
88
|
+
"source",
|
|
89
|
+
"variant",
|
|
90
|
+
"custom-variant",
|
|
91
|
+
"reference",
|
|
92
|
+
"apply",
|
|
93
|
+
"layer",
|
|
94
|
+
"screen",
|
|
95
|
+
"starting-style",
|
|
96
|
+
// v3 carryover (still emitted by some Tailwind plugins)
|
|
97
|
+
"tailwind",
|
|
98
|
+
"config",
|
|
99
|
+
"plugin",
|
|
100
|
+
"responsive"
|
|
101
|
+
];
|
|
102
|
+
const tailwindRules = {
|
|
103
|
+
"at-rule-no-deprecated": [true, { ignoreAtRules: ["apply"] }],
|
|
104
|
+
/**
|
|
105
|
+
* Tailwind v4 only parses the `prefix(...)` modifier on the bare-string
|
|
106
|
+
* `@import "..."` form; wrapping the URL in `url(...)` swallows the modifier
|
|
107
|
+
* and breaks the build. Force the bare-string syntax.
|
|
108
|
+
*/
|
|
109
|
+
"import-notation": "string"
|
|
110
|
+
};
|
|
111
|
+
const tailwindAtRuleNoUnknown = {
|
|
112
|
+
"at-rule-no-unknown": [true, { ignoreAtRules: TAILWIND_AT_RULES }]
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const SCSS_AT_RULES = [
|
|
116
|
+
"use",
|
|
117
|
+
"forward",
|
|
118
|
+
"mixin",
|
|
119
|
+
"include",
|
|
120
|
+
"function",
|
|
121
|
+
"return",
|
|
122
|
+
"if",
|
|
123
|
+
"else",
|
|
124
|
+
"each",
|
|
125
|
+
"for",
|
|
126
|
+
"while",
|
|
127
|
+
"extend",
|
|
128
|
+
"at-root",
|
|
129
|
+
"debug",
|
|
130
|
+
"warn",
|
|
131
|
+
"error",
|
|
132
|
+
"content"
|
|
133
|
+
];
|
|
134
|
+
function scssRules(includeTailwind) {
|
|
135
|
+
return {
|
|
136
|
+
"scss/at-rule-no-unknown": [true, {
|
|
137
|
+
ignoreAtRules: [
|
|
138
|
+
...SCSS_AT_RULES,
|
|
139
|
+
...includeTailwind ? TAILWIND_AT_RULES : []
|
|
140
|
+
]
|
|
141
|
+
}],
|
|
142
|
+
/**
|
|
143
|
+
* SCSS `@use` is forbidden by default in some configs because it must be
|
|
144
|
+
* at the very top of the file. We trust authors here — the SCSS compiler
|
|
145
|
+
* itself enforces the placement.
|
|
146
|
+
*/
|
|
147
|
+
"scss/at-use-no-unnamespaced": null
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function readConsumerPackageJson() {
|
|
152
|
+
try {
|
|
153
|
+
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8"));
|
|
154
|
+
} catch {
|
|
155
|
+
return void 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function hasDependency(pkg, ...names) {
|
|
159
|
+
if (!pkg)
|
|
160
|
+
return false;
|
|
161
|
+
const buckets = [pkg.dependencies, pkg.devDependencies, pkg.peerDependencies];
|
|
162
|
+
return names.some((name) => buckets.some((bucket) => bucket && name in bucket));
|
|
163
|
+
}
|
|
164
|
+
function resolveOptions(options) {
|
|
165
|
+
const pkg = readConsumerPackageJson();
|
|
166
|
+
return {
|
|
167
|
+
vue: options.vue ?? hasDependency(pkg, "vue", "nuxt"),
|
|
168
|
+
html: options.html ?? false,
|
|
169
|
+
tailwind: options.tailwind ?? hasDependency(pkg, "tailwindcss", "@tailwindcss/vite"),
|
|
170
|
+
scss: options.scss ?? hasDependency(pkg, "sass", "sass-embedded", "node-sass"),
|
|
171
|
+
logical: options.logical ?? true,
|
|
172
|
+
order: options.order ?? "recess"
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function applyScss(resolved, ctx) {
|
|
176
|
+
if (!resolved.scss)
|
|
177
|
+
return;
|
|
178
|
+
ctx.extends.push("stylelint-config-recommended-scss");
|
|
179
|
+
Object.assign(ctx.rules, scssRules(resolved.tailwind));
|
|
180
|
+
ctx.rules["at-rule-no-unknown"] = null;
|
|
181
|
+
ctx.overrides.push({
|
|
182
|
+
files: ["**/*.scss"],
|
|
183
|
+
customSyntax: "postcss-scss"
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function applyVueAndHtml(resolved, ctx) {
|
|
187
|
+
if (resolved.vue)
|
|
188
|
+
ctx.extends.push("stylelint-config-recommended-vue");
|
|
189
|
+
if (resolved.html)
|
|
190
|
+
ctx.extends.push("stylelint-config-html");
|
|
191
|
+
if (!resolved.vue && !resolved.html)
|
|
192
|
+
return;
|
|
193
|
+
const vueGlobs = resolved.vue ? ["**/*.vue"] : [];
|
|
194
|
+
const htmlGlobs = resolved.html ? ["**/*.html"] : [];
|
|
195
|
+
ctx.overrides.push({
|
|
196
|
+
files: [...vueGlobs, ...htmlGlobs],
|
|
197
|
+
customSyntax: "postcss-html"
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function applyLogical(resolved, ctx) {
|
|
201
|
+
if (!resolved.logical)
|
|
202
|
+
return;
|
|
203
|
+
ctx.plugins.push(...LOGICAL_PLUGIN);
|
|
204
|
+
Object.assign(ctx.rules, logicalRules);
|
|
205
|
+
}
|
|
206
|
+
function applyUserOverrides(options, ctx) {
|
|
207
|
+
if (Array.isArray(options.plugins) && options.plugins.length > 0)
|
|
208
|
+
ctx.plugins.push(...options.plugins);
|
|
209
|
+
if (options.rules)
|
|
210
|
+
Object.assign(ctx.rules, options.rules);
|
|
211
|
+
if (options.overrides?.length)
|
|
212
|
+
ctx.overrides.push(...options.overrides);
|
|
213
|
+
}
|
|
214
|
+
function defineConfig(options = {}) {
|
|
215
|
+
const resolved = resolveOptions(options);
|
|
216
|
+
const ctx = {
|
|
217
|
+
extends: ["stylelint-config-standard"],
|
|
218
|
+
plugins: [],
|
|
219
|
+
rules: { ...baseRules },
|
|
220
|
+
overrides: []
|
|
221
|
+
};
|
|
222
|
+
if (resolved.order === "recess")
|
|
223
|
+
ctx.extends.push("stylelint-config-recess-order");
|
|
224
|
+
applyScss(resolved, ctx);
|
|
225
|
+
applyVueAndHtml(resolved, ctx);
|
|
226
|
+
if (resolved.tailwind) {
|
|
227
|
+
Object.assign(ctx.rules, tailwindRules);
|
|
228
|
+
if (!resolved.scss)
|
|
229
|
+
Object.assign(ctx.rules, tailwindAtRuleNoUnknown);
|
|
230
|
+
}
|
|
231
|
+
applyLogical(resolved, ctx);
|
|
232
|
+
applyUserOverrides(options, ctx);
|
|
233
|
+
const ignoreFiles = options.ignoresOverride ?? [...GLOBAL_IGNORES, ...options.ignores ?? []];
|
|
234
|
+
return {
|
|
235
|
+
extends: ctx.extends,
|
|
236
|
+
plugins: ctx.plugins.length > 0 ? ctx.plugins : void 0,
|
|
237
|
+
rules: ctx.rules,
|
|
238
|
+
overrides: ctx.overrides.length > 0 ? ctx.overrides : void 0,
|
|
239
|
+
ignoreFiles
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export { GLOBAL_IGNORES, TAILWIND_AT_RULES, baseRules, defineConfig, logicalRules, scssRules, tailwindAtRuleNoUnknown, tailwindRules };
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@maz-ui/stylelint-config",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "5.0.0-beta.0",
|
|
5
|
+
"description": "Stylelint configuration for CSS, SCSS, Vue and Tailwind CSS projects",
|
|
6
|
+
"author": "Louis Mazel <me@loicmazuel.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/LouisMazel/maz-ui.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": "https://github.com/LouisMazel/maz-ui/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"stylelint",
|
|
15
|
+
"stylelint-config",
|
|
16
|
+
"css",
|
|
17
|
+
"scss",
|
|
18
|
+
"vue",
|
|
19
|
+
"tailwindcss",
|
|
20
|
+
"code-quality",
|
|
21
|
+
"maz-ui"
|
|
22
|
+
],
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.mjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.mjs",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"files": [
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"README.md",
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20.19.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"stylelint": ">=16.0.0 <18.0.0"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"postcss-html": "^1.8.1",
|
|
48
|
+
"postcss-scss": "^4.0.9",
|
|
49
|
+
"stylelint-config-html": "^1.1.0",
|
|
50
|
+
"stylelint-config-recess-order": "^7.7.0",
|
|
51
|
+
"stylelint-config-recommended-scss": "^17.0.1",
|
|
52
|
+
"stylelint-config-recommended-vue": "^1.6.1",
|
|
53
|
+
"stylelint-config-standard": "^40.0.0",
|
|
54
|
+
"stylelint-scss": "^7.0.0",
|
|
55
|
+
"stylelint-use-logical-spec": "^5.0.1"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@vitest/coverage-v8": "^4.1.5",
|
|
59
|
+
"stylelint": "^17.11.0",
|
|
60
|
+
"unbuild": "^3.6.1",
|
|
61
|
+
"vitest": "^4.1.5",
|
|
62
|
+
"@maz-ui/eslint-config": "5.0.0-beta.0"
|
|
63
|
+
},
|
|
64
|
+
"lint-staged": {
|
|
65
|
+
"*.{js,jsx,ts,tsx,mjs,mts,cjs,md,yml,json}": [
|
|
66
|
+
"eslint --fix"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"codegen": "nx build",
|
|
71
|
+
"build": "unbuild",
|
|
72
|
+
"dev": "unbuild --stub",
|
|
73
|
+
"typecheck": "tsc --noEmit --skipLibCheck && tsc --noEmit --skipLibCheck --project tsconfig.test.json",
|
|
74
|
+
"lint": "cross-env NODE_ENV=production eslint .",
|
|
75
|
+
"lint:fix": "pnpm lint --fix",
|
|
76
|
+
"test:unit": "vitest run",
|
|
77
|
+
"test:unit:coverage": "vitest run --coverage",
|
|
78
|
+
"test:unit:watch": "vitest watch"
|
|
79
|
+
}
|
|
80
|
+
}
|