@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 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
@@ -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 };
@@ -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
+ }