@projectwallace/stylelint-plugin 0.1.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) 2023 Project Wallace
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,58 @@
1
+ # @projectwallace/stylelint-plugin
2
+
3
+ A stylelint plugin that checks the complexity of your CSS.
4
+
5
+ > Only use this plugin with standard CSS. Non-standard CSS (Sass, Less, etc.) is not supported.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npm install --save-dev @projectwallace/stylelint-plugin
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Add the plugin and configure rules in your stylelint config:
16
+
17
+ ```json
18
+ {
19
+ "plugins": ["@projectwallace/stylelint-plugin"],
20
+ "rules": {
21
+ "projectwallace/max-lines-of-code": 200,
22
+ "projectwallace/max-selector-complexity": 5,
23
+ "projectwallace/no-anonymous-layers": true,
24
+ "projectwallace/no-property-browserhacks": true,
25
+ "projectwallace/no-static-container-query": true,
26
+ "projectwallace/no-static-media-query": true,
27
+ "projectwallace/no-undeclared-container-names": true,
28
+ "projectwallace/no-unknown-custom-property": true,
29
+ "projectwallace/no-unreachable-media-conditions": true,
30
+ "projectwallace/no-unused-container-names": true,
31
+ "projectwallace/no-unused-custom-properties": true,
32
+ "projectwallace/no-unused-layers": true,
33
+ "projectwallace/no-useless-custom-property-assignment": true
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Rules
39
+
40
+ | Rule | Description |
41
+ | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
42
+ | [max-lines-of-code](src/rules/max-lines-of-code/README.md) | Prevent a stylesheet from exceeding a predefined number of lines of code |
43
+ | [no-anonymous-layers](src/rules/no-anonymous-layers/README.md) | Disallow anonymous (unnamed) `@layer` blocks |
44
+ | [max-selector-complexity](src/rules/max-selector-complexity/README.md) | Prevent selector complexity from going over a predefined maximum |
45
+ | [no-property-browserhacks](src/rules/no-property-browserhacks/README.md) | Prevent the use of known browserhacks for properties |
46
+ | [no-undeclared-container-names](src/rules/no-undeclared-container-names/README.md) | Disallow container names in `@container` that were never declared |
47
+ | [no-unknown-custom-property](src/rules/no-unknown-custom-property/README.md) | Disallow the use of undeclared custom properties in a `var()` |
48
+ | [no-useless-custom-property-assignment](src/rules/no-useless-custom-property-assignment/README.md) | Disallow custom property assignments that reference themselves via `var()` |
49
+ | [no-unused-container-names](src/rules/no-unused-container-names/README.md) | Disallow container names that are declared but never queried |
50
+ | [no-unused-custom-properties](src/rules/no-unused-custom-properties/README.md) | Disallow custom properties that are never used in a `var()` |
51
+ | [no-static-container-query](src/rules/no-static-container-query/README.md) | Disallow static (exact-match) numeric container feature conditions |
52
+ | [no-static-media-query](src/rules/no-static-media-query/README.md) | Disallow static (exact-match) numeric media feature conditions |
53
+ | [no-unreachable-media-conditions](src/rules/no-unreachable-media-conditions/README.md) | Disallow media queries with contradictory conditions that can never match |
54
+ | [no-unused-layers](src/rules/no-unused-layers/README.md) | Disallow `@layer` names that are declared but never defined |
55
+
56
+ ## License
57
+
58
+ MIT
@@ -0,0 +1,6 @@
1
+ import stylelint from "stylelint";
2
+
3
+ //#region src/index.d.ts
4
+ declare const plugins: stylelint.Plugin[];
5
+ //#endregion
6
+ export { plugins as default };
package/dist/index.mjs ADDED
@@ -0,0 +1,424 @@
1
+ import stylelint from "stylelint";
2
+ import { FUNCTION, IDENTIFIER, parse_declaration, parse_selector, walk } from "@projectwallace/css-parser";
3
+ import { analyze, selectorComplexity } from "@projectwallace/css-analyzer";
4
+ //#region src/rules/max-selector-complexity/index.ts
5
+ const { createPlugin: createPlugin$9, utils: utils$9 } = stylelint;
6
+ const rule_name$9 = "project-wallace/max-selector-complexity";
7
+ const messages$9 = utils$9.ruleMessages(rule_name$9, { rejected: (selector, actual, expected) => `Selector complexity of "${selector}" is ${actual} which is greater than the allowed ${expected}` });
8
+ const meta$9 = { url: "https://github.com/projectwallace/stylelint-plugins" };
9
+ const ruleFunction$9 = (primaryOption) => {
10
+ return (root, result) => {
11
+ if (!utils$9.validateOptions(result, rule_name$9, {
12
+ actual: primaryOption,
13
+ possible: [Number]
14
+ }) || !Number.isInteger(primaryOption) || primaryOption <= 1) return;
15
+ root.walkRules((rule) => {
16
+ const selector = rule.selector;
17
+ const parsed = parse_selector(selector);
18
+ for (const sel of parsed.children) {
19
+ const complexity = selectorComplexity(sel);
20
+ const stringified = sel.text.replace(/\n/g, "");
21
+ if (complexity > primaryOption) utils$9.report({
22
+ message: messages$9.rejected(stringified, complexity, primaryOption),
23
+ node: rule,
24
+ result,
25
+ ruleName: rule_name$9
26
+ });
27
+ }
28
+ });
29
+ };
30
+ };
31
+ ruleFunction$9.ruleName = rule_name$9;
32
+ ruleFunction$9.messages = messages$9;
33
+ ruleFunction$9.meta = meta$9;
34
+ var max_selector_complexity_default = createPlugin$9(rule_name$9, ruleFunction$9);
35
+ //#endregion
36
+ //#region src/utils/custom-properties.ts
37
+ function collect_declared_properties(root) {
38
+ const properties = /* @__PURE__ */ new Map();
39
+ root.walkAtRules("property", function(atRule) {
40
+ const property_name = atRule.params.trim();
41
+ if (property_name.startsWith("--")) properties.set(property_name, atRule);
42
+ });
43
+ root.walkDecls(function(declaration) {
44
+ if (declaration.prop.startsWith("--")) properties.set(declaration.prop, declaration);
45
+ });
46
+ return properties;
47
+ }
48
+ function collect_var_usages(root) {
49
+ const usages = [];
50
+ root.walkDecls(function(declaration) {
51
+ walk(parse_declaration(root.source.input.css.substring(declaration.source.start.offset, declaration.source.end.offset)), (node) => {
52
+ if (node.type === FUNCTION && node.name === "var") {
53
+ let found_name = null;
54
+ let has_fallback = false;
55
+ for (const child of node.children) if (found_name === null && child.type === IDENTIFIER && child.text.startsWith("--")) found_name = child.text;
56
+ else if (found_name !== null) {
57
+ has_fallback = true;
58
+ break;
59
+ }
60
+ if (found_name !== null) usages.push({
61
+ name: found_name,
62
+ has_fallback,
63
+ node: declaration
64
+ });
65
+ }
66
+ });
67
+ });
68
+ return usages;
69
+ }
70
+ //#endregion
71
+ //#region src/rules/no-unused-custom-properties/index.ts
72
+ const { createPlugin: createPlugin$8, utils: utils$8 } = stylelint;
73
+ const rule_name$8 = "project-wallace/no-unused-custom-properties";
74
+ const messages$8 = utils$8.ruleMessages(rule_name$8, { rejected: (property) => `"${property}" was declared but never used in a var()` });
75
+ const meta$8 = { url: "https://github.com/projectwallace/stylelint-plugins" };
76
+ const ruleFunction$8 = (primaryOptions, secondaryOptions) => {
77
+ return (root, result) => {
78
+ if (!utils$8.validateOptions(result, rule_name$8, {
79
+ actual: primaryOptions,
80
+ possible: [true]
81
+ })) return;
82
+ const declared_properties = collect_declared_properties(root);
83
+ const used_names = new Set(collect_var_usages(root).map((u) => u.name));
84
+ for (const [prop, node] of declared_properties) {
85
+ if (used_names.has(prop)) continue;
86
+ if (secondaryOptions?.ignoreProperties) {
87
+ if (secondaryOptions.ignoreProperties.some((pattern) => typeof pattern === "string" && pattern === prop || pattern instanceof RegExp && pattern.test(prop))) continue;
88
+ }
89
+ utils$8.report({
90
+ result,
91
+ ruleName: rule_name$8,
92
+ message: messages$8.rejected(prop),
93
+ node,
94
+ word: prop
95
+ });
96
+ }
97
+ };
98
+ };
99
+ ruleFunction$8.ruleName = rule_name$8;
100
+ ruleFunction$8.messages = messages$8;
101
+ ruleFunction$8.meta = meta$8;
102
+ var no_unused_custom_properties_default = createPlugin$8(rule_name$8, ruleFunction$8);
103
+ //#endregion
104
+ //#region src/rules/no-unknown-custom-property/index.ts
105
+ const { createPlugin: createPlugin$7, utils: utils$7 } = stylelint;
106
+ const rule_name$7 = "project-wallace/no-unknown-custom-property";
107
+ const messages$7 = utils$7.ruleMessages(rule_name$7, { rejected: (property) => `"${property}" is used in a var() but was never declared` });
108
+ const meta$7 = { url: "https://github.com/projectwallace/stylelint-plugins" };
109
+ const ruleFunction$7 = (primaryOptions, secondaryOptions) => {
110
+ return (root, result) => {
111
+ if (!utils$7.validateOptions(result, rule_name$7, {
112
+ actual: primaryOptions,
113
+ possible: [true]
114
+ })) return;
115
+ const declared_properties = collect_declared_properties(root);
116
+ const usages = collect_var_usages(root);
117
+ for (const usage of usages) {
118
+ if (declared_properties.has(usage.name)) continue;
119
+ if (secondaryOptions?.allowFallback && usage.has_fallback) continue;
120
+ if (secondaryOptions?.allowList) {
121
+ if (secondaryOptions.allowList.some((pattern) => typeof pattern === "string" && pattern === usage.name || pattern instanceof RegExp && pattern.test(usage.name))) continue;
122
+ }
123
+ utils$7.report({
124
+ result,
125
+ ruleName: rule_name$7,
126
+ message: messages$7.rejected(usage.name),
127
+ node: usage.node,
128
+ word: usage.name
129
+ });
130
+ }
131
+ };
132
+ };
133
+ ruleFunction$7.ruleName = rule_name$7;
134
+ ruleFunction$7.messages = messages$7;
135
+ ruleFunction$7.meta = meta$7;
136
+ var no_unknown_custom_property_default = createPlugin$7(rule_name$7, ruleFunction$7);
137
+ //#endregion
138
+ //#region src/rules/no-property-browserhacks/index.ts
139
+ const { createPlugin: createPlugin$6, utils: utils$6 } = stylelint;
140
+ const rule_name$6 = "project-wallace/no-property-browserhacks";
141
+ const messages$6 = utils$6.ruleMessages(rule_name$6, { rejected: (property) => `Property "${property}" is a browserhack and is not allowed` });
142
+ const meta$6 = { url: "https://github.com/projectwallace/stylelint-plugins" };
143
+ const ruleFunction$6 = (primaryOption) => {
144
+ return (root, result) => {
145
+ if (!utils$6.validateOptions(result, rule_name$6, {
146
+ actual: primaryOption,
147
+ possible: [true]
148
+ })) return;
149
+ root.walkDecls((declaration) => {
150
+ const parsed = parse_declaration(root.source.input.css.substring(declaration.source.start.offset, declaration.source.end.offset));
151
+ if (parsed.is_browserhack) utils$6.report({
152
+ message: messages$6.rejected(parsed.property),
153
+ node: declaration,
154
+ result,
155
+ ruleName: rule_name$6
156
+ });
157
+ });
158
+ };
159
+ };
160
+ ruleFunction$6.ruleName = rule_name$6;
161
+ ruleFunction$6.messages = messages$6;
162
+ ruleFunction$6.meta = meta$6;
163
+ var no_property_browserhacks_default = createPlugin$6(rule_name$6, ruleFunction$6);
164
+ //#endregion
165
+ //#region src/rules/max-lines-of-code/index.ts
166
+ const { createPlugin: createPlugin$5, utils: utils$5 } = stylelint;
167
+ const rule_name$5 = "project-wallace/max-lines-of-code";
168
+ const messages$5 = utils$5.ruleMessages(rule_name$5, { rejected: (actual, expected) => `Counted ${actual} Lines of Code which is greater than the allowed ${expected}` });
169
+ const meta$5 = { url: "https://github.com/projectwallace/stylelint-plugins" };
170
+ const ruleFunction$5 = (primaryOption) => {
171
+ return (root, result) => {
172
+ if (!utils$5.validateOptions(result, rule_name$5, {
173
+ actual: primaryOption,
174
+ possible: [Number]
175
+ }) || !Number.isInteger(primaryOption) || primaryOption <= 0) return;
176
+ const actual = analyze(root.source.input.css).stylesheet.sourceLinesOfCode;
177
+ if (actual > primaryOption) utils$5.report({
178
+ message: messages$5.rejected(actual, primaryOption),
179
+ node: root,
180
+ result,
181
+ ruleName: rule_name$5
182
+ });
183
+ };
184
+ };
185
+ ruleFunction$5.ruleName = rule_name$5;
186
+ ruleFunction$5.messages = messages$5;
187
+ ruleFunction$5.meta = meta$5;
188
+ var max_lines_of_code_default = createPlugin$5(rule_name$5, ruleFunction$5);
189
+ //#endregion
190
+ //#region src/rules/no-unused-layers/index.ts
191
+ const { createPlugin: createPlugin$4, utils: utils$4 } = stylelint;
192
+ const rule_name$4 = "project-wallace/no-unused-layers";
193
+ const messages$4 = utils$4.ruleMessages(rule_name$4, { rejected: (layer) => `Layer "${layer}" was declared but never defined` });
194
+ const meta$4 = { url: "https://github.com/projectwallace/stylelint-plugin" };
195
+ const ruleFunction$4 = (primaryOptions, secondaryOptions) => {
196
+ return (root, result) => {
197
+ if (!utils$4.validateOptions(result, rule_name$4, {
198
+ actual: primaryOptions,
199
+ possible: [true]
200
+ })) return;
201
+ const declared_layers = /* @__PURE__ */ new Map();
202
+ const defined_layers = /* @__PURE__ */ new Set();
203
+ root.walkAtRules("layer", (atRule) => {
204
+ if (atRule.nodes !== void 0) {
205
+ const name = atRule.params.trim();
206
+ if (name) defined_layers.add(name);
207
+ } else {
208
+ const names = atRule.params.split(",").map((n) => n.trim()).filter(Boolean);
209
+ for (const name of names) if (!declared_layers.has(name)) declared_layers.set(name, atRule);
210
+ }
211
+ });
212
+ for (const [layer, node] of declared_layers) {
213
+ if (defined_layers.has(layer)) continue;
214
+ if (secondaryOptions?.allowlist) {
215
+ if (secondaryOptions.allowlist.some((pattern) => typeof pattern === "string" && pattern === layer || pattern instanceof RegExp && pattern.test(layer))) continue;
216
+ }
217
+ utils$4.report({
218
+ result,
219
+ ruleName: rule_name$4,
220
+ message: messages$4.rejected(layer),
221
+ node,
222
+ word: layer
223
+ });
224
+ }
225
+ };
226
+ };
227
+ ruleFunction$4.ruleName = rule_name$4;
228
+ ruleFunction$4.messages = messages$4;
229
+ ruleFunction$4.meta = meta$4;
230
+ var no_unused_layers_default = createPlugin$4(rule_name$4, ruleFunction$4);
231
+ //#endregion
232
+ //#region src/utils/container-names.ts
233
+ function collect_declared_container_names(root) {
234
+ const names = /* @__PURE__ */ new Map();
235
+ root.walkDecls(/^container(-name)?$/i, (decl) => {
236
+ let value = decl.value.trim();
237
+ if (decl.prop.toLowerCase() === "container") {
238
+ const slash_index = value.indexOf("/");
239
+ if (slash_index !== -1) value = value.substring(0, slash_index).trim();
240
+ }
241
+ if (value.toLowerCase() === "none") return;
242
+ const name_list = value.split(/\s+/).filter(Boolean);
243
+ for (const name of name_list) if (!names.has(name)) names.set(name, decl);
244
+ });
245
+ return names;
246
+ }
247
+ function collect_container_name_usages(root) {
248
+ const usages = [];
249
+ root.walkAtRules("container", (atRule) => {
250
+ const params = atRule.params.trim();
251
+ if (/^\(|^not[\s(]|^style\s*\(|^and[\s(]|^or[\s(]/.test(params)) return;
252
+ const match = params.match(/^([\w-]+)/);
253
+ if (match && match[1].toLowerCase() !== "none") usages.push({
254
+ name: match[1],
255
+ node: atRule
256
+ });
257
+ });
258
+ return usages;
259
+ }
260
+ //#endregion
261
+ //#region src/rules/no-unused-container-names/index.ts
262
+ const { createPlugin: createPlugin$3, utils: utils$3 } = stylelint;
263
+ const rule_name$3 = "project-wallace/no-unused-container-names";
264
+ const messages$3 = utils$3.ruleMessages(rule_name$3, { rejected: (name) => `Container name "${name}" was declared but never used in a @container query` });
265
+ const meta$3 = { url: "https://github.com/projectwallace/stylelint-plugin" };
266
+ const ruleFunction$3 = (primaryOptions, secondaryOptions) => {
267
+ return (root, result) => {
268
+ if (!utils$3.validateOptions(result, rule_name$3, {
269
+ actual: primaryOptions,
270
+ possible: [true]
271
+ })) return;
272
+ const declared_names = collect_declared_container_names(root);
273
+ const used_names = new Set(collect_container_name_usages(root).map((u) => u.name));
274
+ for (const [name, node] of declared_names) {
275
+ if (used_names.has(name)) continue;
276
+ if (secondaryOptions?.allowList) {
277
+ if (secondaryOptions.allowList.some((pattern) => typeof pattern === "string" && pattern === name || pattern instanceof RegExp && pattern.test(name))) continue;
278
+ }
279
+ utils$3.report({
280
+ result,
281
+ ruleName: rule_name$3,
282
+ message: messages$3.rejected(name),
283
+ node,
284
+ word: name
285
+ });
286
+ }
287
+ };
288
+ };
289
+ ruleFunction$3.ruleName = rule_name$3;
290
+ ruleFunction$3.messages = messages$3;
291
+ ruleFunction$3.meta = meta$3;
292
+ var no_unused_container_names_default = createPlugin$3(rule_name$3, ruleFunction$3);
293
+ //#endregion
294
+ //#region src/rules/no-undeclared-container-names/index.ts
295
+ const { createPlugin: createPlugin$2, utils: utils$2 } = stylelint;
296
+ const rule_name$2 = "project-wallace/no-undeclared-container-names";
297
+ const messages$2 = utils$2.ruleMessages(rule_name$2, { rejected: (name) => `Container name "${name}" is used in a @container query but was never declared` });
298
+ const meta$2 = { url: "https://github.com/projectwallace/stylelint-plugin" };
299
+ const ruleFunction$2 = (primaryOptions, secondaryOptions) => {
300
+ return (root, result) => {
301
+ if (!utils$2.validateOptions(result, rule_name$2, {
302
+ actual: primaryOptions,
303
+ possible: [true]
304
+ })) return;
305
+ const declared_names = collect_declared_container_names(root);
306
+ const usages = collect_container_name_usages(root);
307
+ for (const usage of usages) {
308
+ if (declared_names.has(usage.name)) continue;
309
+ if (secondaryOptions?.allowList) {
310
+ if (secondaryOptions.allowList.some((pattern) => typeof pattern === "string" && pattern === usage.name || pattern instanceof RegExp && pattern.test(usage.name))) continue;
311
+ }
312
+ utils$2.report({
313
+ result,
314
+ ruleName: rule_name$2,
315
+ message: messages$2.rejected(usage.name),
316
+ node: usage.node,
317
+ word: usage.name
318
+ });
319
+ }
320
+ };
321
+ };
322
+ ruleFunction$2.ruleName = rule_name$2;
323
+ ruleFunction$2.messages = messages$2;
324
+ ruleFunction$2.meta = meta$2;
325
+ var no_undeclared_container_names_default = createPlugin$2(rule_name$2, ruleFunction$2);
326
+ //#endregion
327
+ //#region src/rules/no-anonymous-layers/index.ts
328
+ const { createPlugin: createPlugin$1, utils: utils$1 } = stylelint;
329
+ const rule_name$1 = "project-wallace/no-anonymous-layers";
330
+ const messages$1 = utils$1.ruleMessages(rule_name$1, { rejected: () => `Anonymous @layer is not allowed` });
331
+ const meta$1 = { url: "https://github.com/projectwallace/stylelint-plugin" };
332
+ const ruleFunction$1 = (primaryOptions) => {
333
+ return (root, result) => {
334
+ if (!utils$1.validateOptions(result, rule_name$1, {
335
+ actual: primaryOptions,
336
+ possible: [true]
337
+ })) return;
338
+ root.walkAtRules("layer", (atRule) => {
339
+ if (atRule.nodes !== void 0 && atRule.params.trim() === "") utils$1.report({
340
+ result,
341
+ ruleName: rule_name$1,
342
+ message: messages$1.rejected(),
343
+ node: atRule,
344
+ word: "@layer"
345
+ });
346
+ });
347
+ root.walkAtRules("import", (atRule) => {
348
+ const params = atRule.params;
349
+ if (!/\blayer\b/.test(params)) return;
350
+ if (/\blayer\([^)]+\)/.test(params)) return;
351
+ utils$1.report({
352
+ result,
353
+ ruleName: rule_name$1,
354
+ message: messages$1.rejected(),
355
+ node: atRule,
356
+ word: "layer"
357
+ });
358
+ });
359
+ };
360
+ };
361
+ ruleFunction$1.ruleName = rule_name$1;
362
+ ruleFunction$1.messages = messages$1;
363
+ ruleFunction$1.meta = meta$1;
364
+ var no_anonymous_layers_default = createPlugin$1(rule_name$1, ruleFunction$1);
365
+ //#endregion
366
+ //#region src/rules/no-useless-custom-property-assignment/index.ts
367
+ const { createPlugin, utils } = stylelint;
368
+ const rule_name = "project-wallace/no-useless-custom-property-assignment";
369
+ const messages = utils.ruleMessages(rule_name, { rejected: (property) => `"${property}" is assigned to itself via var(), which has no effect` });
370
+ const meta = { url: "https://github.com/projectwallace/stylelint-plugins" };
371
+ const ruleFunction = (primaryOptions, secondaryOptions) => {
372
+ return (root, result) => {
373
+ if (!utils.validateOptions(result, rule_name, {
374
+ actual: primaryOptions,
375
+ possible: [true]
376
+ })) return;
377
+ root.walkDecls(function(declaration) {
378
+ const prop = declaration.prop;
379
+ if (!prop.startsWith("--")) return;
380
+ if (secondaryOptions?.allowList) {
381
+ if (secondaryOptions.allowList.some((pattern) => typeof pattern === "string" && pattern === prop || pattern instanceof RegExp && pattern.test(prop))) return;
382
+ }
383
+ const parsed = parse_declaration(root.source.input.css.substring(declaration.source.start.offset, declaration.source.end.offset));
384
+ let reported = false;
385
+ walk(parsed, (node) => {
386
+ if (reported) return;
387
+ if (node.type !== FUNCTION || node.name !== "var") return;
388
+ for (const child of node.children) {
389
+ if (child.type === IDENTIFIER && child.text === prop) {
390
+ utils.report({
391
+ result,
392
+ ruleName: rule_name,
393
+ message: messages.rejected(prop),
394
+ node: declaration,
395
+ word: prop
396
+ });
397
+ reported = true;
398
+ return;
399
+ }
400
+ if (child.type === IDENTIFIER && child.text.startsWith("--")) break;
401
+ }
402
+ });
403
+ });
404
+ };
405
+ };
406
+ ruleFunction.ruleName = rule_name;
407
+ ruleFunction.messages = messages;
408
+ ruleFunction.meta = meta;
409
+ //#endregion
410
+ //#region src/index.ts
411
+ const plugins = [
412
+ max_selector_complexity_default,
413
+ max_lines_of_code_default,
414
+ no_unused_custom_properties_default,
415
+ no_unknown_custom_property_default,
416
+ no_property_browserhacks_default,
417
+ no_unused_layers_default,
418
+ no_unused_container_names_default,
419
+ no_undeclared_container_names_default,
420
+ no_anonymous_layers_default,
421
+ createPlugin(rule_name, ruleFunction)
422
+ ];
423
+ //#endregion
424
+ export { plugins as default };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@projectwallace/stylelint-plugin",
3
+ "version": "0.1.0",
4
+ "description": "A stylelint plugin that checks the complexity of your CSS.",
5
+ "keywords": [
6
+ "complexity",
7
+ "css",
8
+ "lint",
9
+ "stylelint",
10
+ "stylelint-plugin",
11
+ "wallace"
12
+ ],
13
+ "homepage": "https://github.com/projectwallace/stylelint-plugin",
14
+ "bugs": "https://github.com/projectwallace/stylelint-plugin/issues",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/projectwallace/stylelint-plugin.git"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "type": "module",
24
+ "main": "./dist/index.mjs",
25
+ "exports": {
26
+ ".": "./dist/index.mjs",
27
+ "./configs/recommended": "./dist/configs/recommended.mjs"
28
+ },
29
+ "scripts": {
30
+ "test": "vitest --coverage",
31
+ "check": "tsc --noEmit",
32
+ "lint": "oxlint -D perf; oxfmt --check",
33
+ "build": "tsdown"
34
+ },
35
+ "dependencies": {
36
+ "@projectwallace/css-analyzer": "^9.4.0",
37
+ "@projectwallace/css-parser": "^0.13.9"
38
+ },
39
+ "devDependencies": {
40
+ "@codecov/rollup-plugin": "^1.9.1",
41
+ "@types/node": "^25.5.0",
42
+ "@vitest/coverage-v8": "^4.0.0",
43
+ "oxfmt": "^0.41.0",
44
+ "oxlint": "^1.56.0",
45
+ "publint": "^0.3.18",
46
+ "stylelint": "^17.0.0",
47
+ "tsdown": "^0.21.4",
48
+ "typescript": "^5.9.3",
49
+ "vitest": "^4.0.0"
50
+ },
51
+ "peerDependencies": {
52
+ "stylelint": "^17.0.0"
53
+ }
54
+ }