@spiracss/stylelint-plugin 0.1.11 → 0.1.12
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
CHANGED
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ const messages = stylelint_1.default.utils.ruleMessages(exports.ruleName, {
|
|
|
25
25
|
disallowedModifier: () => 'Modifier classes are disabled by selectorPolicy. Use data-variant/data-state or enable class mode.',
|
|
26
26
|
invalidDataAttribute: (attr) => `Attribute "${attr}" is not allowed by selectorPolicy.`,
|
|
27
27
|
invalidDataValue: (attr, value) => `Attribute "${attr}" value "${value}" does not match selectorPolicy valueNaming.`,
|
|
28
|
+
rootSelectorMissingBlock: (block, selector) => `Root selector "${selector}" must include the root Block ".${block}".`,
|
|
28
29
|
fileNameMismatch: (block, expected, actual) => `Root Block ".${block}" must be defined in "${expected}.scss" (found "${actual}.scss").`
|
|
29
30
|
});
|
|
30
31
|
const defaultValueNaming = {
|
|
@@ -341,6 +342,24 @@ const collectRootBlockNames = (selector, options, patterns) => {
|
|
|
341
342
|
}).processSync(selector);
|
|
342
343
|
return [...names];
|
|
343
344
|
};
|
|
345
|
+
const analyzeRootSelector = (sel, rootBlockName, options, patterns) => {
|
|
346
|
+
let hasSpiraClass = false;
|
|
347
|
+
let hasRootBlock = false;
|
|
348
|
+
let hasOtherBlock = false;
|
|
349
|
+
sel.walk((node) => {
|
|
350
|
+
if (node.type !== 'class')
|
|
351
|
+
return;
|
|
352
|
+
const name = node.value;
|
|
353
|
+
const kind = classify(name, options, patterns);
|
|
354
|
+
if (kind !== 'external')
|
|
355
|
+
hasSpiraClass = true;
|
|
356
|
+
if (name === rootBlockName)
|
|
357
|
+
hasRootBlock = true;
|
|
358
|
+
if (kind === 'block' && name !== rootBlockName)
|
|
359
|
+
hasOtherBlock = true;
|
|
360
|
+
});
|
|
361
|
+
return { hasSpiraClass, hasRootBlock, hasOtherBlock };
|
|
362
|
+
};
|
|
344
363
|
const buildValuePattern = (naming) => {
|
|
345
364
|
const maxWords = naming.maxWords;
|
|
346
365
|
switch (naming.case) {
|
|
@@ -771,6 +790,7 @@ const spiracssClassStructure = stylelint_1.default.createPlugin(exports.ruleName
|
|
|
771
790
|
const interactionRules = markInteractionRules(root, commentPatterns);
|
|
772
791
|
let firstRule = null;
|
|
773
792
|
let rootBlockName = null;
|
|
793
|
+
const topLevelRules = [];
|
|
774
794
|
const filePath = ((_a = result === null || result === void 0 ? void 0 : result.opts) === null || _a === void 0 ? void 0 : _a.from) || '';
|
|
775
795
|
const normalizedPath = filePath ? path_1.default.normalize(filePath) : '';
|
|
776
796
|
const pathSegments = normalizedPath ? normalizedPath.split(path_1.default.sep).filter(Boolean) : [];
|
|
@@ -799,6 +819,7 @@ const spiracssClassStructure = stylelint_1.default.createPlugin(exports.ruleName
|
|
|
799
819
|
});
|
|
800
820
|
};
|
|
801
821
|
if (!parentRule) {
|
|
822
|
+
topLevelRules.push(rule);
|
|
802
823
|
const rootBlocks = collectRootBlockNames(rule.selector, options, patterns);
|
|
803
824
|
if (rootBlocks.length > 0) {
|
|
804
825
|
const rootName = rootBlockName || rootBlocks[0];
|
|
@@ -826,6 +847,28 @@ const spiracssClassStructure = stylelint_1.default.createPlugin(exports.ruleName
|
|
|
826
847
|
}));
|
|
827
848
|
}).processSync(rule.selector);
|
|
828
849
|
});
|
|
850
|
+
if (options.enforceSingleRootBlock && rootBlockName) {
|
|
851
|
+
const resolvedRootBlockName = rootBlockName;
|
|
852
|
+
topLevelRules.forEach((rule) => {
|
|
853
|
+
if (typeof rule.selector !== 'string')
|
|
854
|
+
return;
|
|
855
|
+
if (rule.selector.includes(':global'))
|
|
856
|
+
return;
|
|
857
|
+
(0, postcss_selector_parser_1.default)((selectors) => {
|
|
858
|
+
selectors.each((sel) => {
|
|
859
|
+
const { hasSpiraClass, hasRootBlock, hasOtherBlock } = analyzeRootSelector(sel, resolvedRootBlockName, options, patterns);
|
|
860
|
+
if (!hasSpiraClass || hasRootBlock || hasOtherBlock)
|
|
861
|
+
return;
|
|
862
|
+
stylelint_1.default.utils.report({
|
|
863
|
+
ruleName: exports.ruleName,
|
|
864
|
+
result,
|
|
865
|
+
node: rule,
|
|
866
|
+
message: messages.rootSelectorMissingBlock(resolvedRootBlockName, sel.toString().trim())
|
|
867
|
+
});
|
|
868
|
+
});
|
|
869
|
+
}).processSync(rule.selector);
|
|
870
|
+
});
|
|
871
|
+
}
|
|
829
872
|
if (options.enforceRootFileName && rootBlockName && fileBase) {
|
|
830
873
|
const isComponentsLayer = options.componentsDirs.some((dir) => {
|
|
831
874
|
const normalizedDir = path_1.default.normalize(dir);
|
|
@@ -19,6 +19,7 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
|
19
19
|
disallowedModifier: () => 'Modifier classes are disabled by selectorPolicy. Use data-variant/data-state or enable class mode.',
|
|
20
20
|
invalidDataAttribute: (attr) => `Attribute "${attr}" is not allowed by selectorPolicy.`,
|
|
21
21
|
invalidDataValue: (attr, value) => `Attribute "${attr}" value "${value}" does not match selectorPolicy valueNaming.`,
|
|
22
|
+
rootSelectorMissingBlock: (block, selector) => `Root selector "${selector}" must include the root Block ".${block}".`,
|
|
22
23
|
fileNameMismatch: (block, expected, actual) => `Root Block ".${block}" must be defined in "${expected}.scss" (found "${actual}.scss").`
|
|
23
24
|
});
|
|
24
25
|
const defaultValueNaming = {
|
|
@@ -335,6 +336,24 @@ const collectRootBlockNames = (selector, options, patterns) => {
|
|
|
335
336
|
}).processSync(selector);
|
|
336
337
|
return [...names];
|
|
337
338
|
};
|
|
339
|
+
const analyzeRootSelector = (sel, rootBlockName, options, patterns) => {
|
|
340
|
+
let hasSpiraClass = false;
|
|
341
|
+
let hasRootBlock = false;
|
|
342
|
+
let hasOtherBlock = false;
|
|
343
|
+
sel.walk((node) => {
|
|
344
|
+
if (node.type !== 'class')
|
|
345
|
+
return;
|
|
346
|
+
const name = node.value;
|
|
347
|
+
const kind = classify(name, options, patterns);
|
|
348
|
+
if (kind !== 'external')
|
|
349
|
+
hasSpiraClass = true;
|
|
350
|
+
if (name === rootBlockName)
|
|
351
|
+
hasRootBlock = true;
|
|
352
|
+
if (kind === 'block' && name !== rootBlockName)
|
|
353
|
+
hasOtherBlock = true;
|
|
354
|
+
});
|
|
355
|
+
return { hasSpiraClass, hasRootBlock, hasOtherBlock };
|
|
356
|
+
};
|
|
338
357
|
const buildValuePattern = (naming) => {
|
|
339
358
|
const maxWords = naming.maxWords;
|
|
340
359
|
switch (naming.case) {
|
|
@@ -765,6 +784,7 @@ const spiracssClassStructure = stylelint.createPlugin(ruleName, (primaryOption,
|
|
|
765
784
|
const interactionRules = markInteractionRules(root, commentPatterns);
|
|
766
785
|
let firstRule = null;
|
|
767
786
|
let rootBlockName = null;
|
|
787
|
+
const topLevelRules = [];
|
|
768
788
|
const filePath = ((_a = result === null || result === void 0 ? void 0 : result.opts) === null || _a === void 0 ? void 0 : _a.from) || '';
|
|
769
789
|
const normalizedPath = filePath ? path.normalize(filePath) : '';
|
|
770
790
|
const pathSegments = normalizedPath ? normalizedPath.split(path.sep).filter(Boolean) : [];
|
|
@@ -793,6 +813,7 @@ const spiracssClassStructure = stylelint.createPlugin(ruleName, (primaryOption,
|
|
|
793
813
|
});
|
|
794
814
|
};
|
|
795
815
|
if (!parentRule) {
|
|
816
|
+
topLevelRules.push(rule);
|
|
796
817
|
const rootBlocks = collectRootBlockNames(rule.selector, options, patterns);
|
|
797
818
|
if (rootBlocks.length > 0) {
|
|
798
819
|
const rootName = rootBlockName || rootBlocks[0];
|
|
@@ -820,6 +841,28 @@ const spiracssClassStructure = stylelint.createPlugin(ruleName, (primaryOption,
|
|
|
820
841
|
}));
|
|
821
842
|
}).processSync(rule.selector);
|
|
822
843
|
});
|
|
844
|
+
if (options.enforceSingleRootBlock && rootBlockName) {
|
|
845
|
+
const resolvedRootBlockName = rootBlockName;
|
|
846
|
+
topLevelRules.forEach((rule) => {
|
|
847
|
+
if (typeof rule.selector !== 'string')
|
|
848
|
+
return;
|
|
849
|
+
if (rule.selector.includes(':global'))
|
|
850
|
+
return;
|
|
851
|
+
selectorParser((selectors) => {
|
|
852
|
+
selectors.each((sel) => {
|
|
853
|
+
const { hasSpiraClass, hasRootBlock, hasOtherBlock } = analyzeRootSelector(sel, resolvedRootBlockName, options, patterns);
|
|
854
|
+
if (!hasSpiraClass || hasRootBlock || hasOtherBlock)
|
|
855
|
+
return;
|
|
856
|
+
stylelint.utils.report({
|
|
857
|
+
ruleName,
|
|
858
|
+
result,
|
|
859
|
+
node: rule,
|
|
860
|
+
message: messages.rootSelectorMissingBlock(resolvedRootBlockName, sel.toString().trim())
|
|
861
|
+
});
|
|
862
|
+
});
|
|
863
|
+
}).processSync(rule.selector);
|
|
864
|
+
});
|
|
865
|
+
}
|
|
823
866
|
if (options.enforceRootFileName && rootBlockName && fileBase) {
|
|
824
867
|
const isComponentsLayer = options.componentsDirs.some((dir) => {
|
|
825
868
|
const normalizedDir = path.normalize(dir);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spiracss/stylelint-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "SpiraCSS development utilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spiracss",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public",
|
|
16
|
-
"tag": "
|
|
16
|
+
"tag": "latest"
|
|
17
17
|
},
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"engines": {
|