@plumeria/eslint-plugin 11.0.2 → 11.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/README.md CHANGED
@@ -12,6 +12,7 @@ The `plugin:@plumeria/recommended` config enables the following:
12
12
  - `@plumeria/no-destructure`: **error**
13
13
  - `@plumeria/no-inline-object`: **error**
14
14
  - `@plumeria/no-inner-call`: **error**
15
+ - `@plumeria/no-invalid-selector-nesting`: **error**
15
16
  - `@plumeria/no-unknown-css-properties`: **error**
16
17
  - `@plumeria/no-unused-keys`: **warn**
17
18
  - `@plumeria/sort-properties`: **warn**
@@ -46,6 +47,14 @@ Disallow passing inline object to `styleName` and `css.use()`. Only compiled sty
46
47
 
47
48
  Disallow calling APIs inside functions.
48
49
 
50
+ ### no-invalid-selector-nesting
51
+
52
+ Disallow invalid selector nesting inside `css.create()`. (e.g. Pseudo -> Query, Query -> Query)
53
+
54
+ ### no-mixed-styling-props
55
+
56
+ Disallow mixing `styleName` with `className` or `style`. `styleName` can handle both `className` and `style`.
57
+
49
58
  ### no-unknown-css-properties
50
59
 
51
60
  Disallow unknown CSS properties in camelCase within `css.create`, `css.keyframes`, and `css.viewTransition`.
package/dist/index.js CHANGED
@@ -6,17 +6,21 @@ const no_combinator_1 = require("./rules/no-combinator");
6
6
  const no_destructure_1 = require("./rules/no-destructure");
7
7
  const no_inline_object_1 = require("./rules/no-inline-object");
8
8
  const no_inner_call_1 = require("./rules/no-inner-call");
9
+ const no_invalid_selector_nesting_1 = require("./rules/no-invalid-selector-nesting");
10
+ const no_mixed_styling_props_1 = require("./rules/no-mixed-styling-props");
11
+ const no_unknown_css_properties_1 = require("./rules/no-unknown-css-properties");
9
12
  const no_unused_keys_1 = require("./rules/no-unused-keys");
10
13
  const sort_properties_1 = require("./rules/sort-properties");
11
14
  const format_properties_1 = require("./rules/format-properties");
12
15
  const validate_values_1 = require("./rules/validate-values");
13
- const no_unknown_css_properties_1 = require("./rules/no-unknown-css-properties");
14
16
  const rules = {
15
17
  'style-name-requires-import': style_name_requires_import_1.styleNameRequiresImport,
16
18
  'no-combinator': no_combinator_1.noCombinator,
17
19
  'no-destructure': no_destructure_1.noDestructure,
18
20
  'no-inline-object': no_inline_object_1.noInlineObject,
19
21
  'no-inner-call': no_inner_call_1.noInnerCall,
22
+ 'no-invalid-selector-nesting': no_invalid_selector_nesting_1.noInvalidSelectorNesting,
23
+ 'no-mixed-styling-props': no_mixed_styling_props_1.noMixedStylingProps,
20
24
  'no-unknown-css-properties': no_unknown_css_properties_1.noUnknownCssProperties,
21
25
  'no-unused-keys': no_unused_keys_1.noUnusedKeys,
22
26
  'sort-properties': sort_properties_1.sortProperties,
@@ -32,6 +36,8 @@ const configs = {
32
36
  '@plumeria/no-destructure': 'error',
33
37
  '@plumeria/no-inline-object': 'error',
34
38
  '@plumeria/no-inner-call': 'error',
39
+ '@plumeria/no-invalid-selector-nesting': 'error',
40
+ '@plumeria/no-mixed-styling-props': 'error',
35
41
  '@plumeria/no-unknown-css-properties': 'error',
36
42
  '@plumeria/no-unused-keys': 'warn',
37
43
  '@plumeria/sort-properties': 'warn',
@@ -53,6 +59,8 @@ const flatConfigs = {
53
59
  '@plumeria/no-destructure': 'error',
54
60
  '@plumeria/no-inline-object': 'error',
55
61
  '@plumeria/no-inner-call': 'error',
62
+ '@plumeria/no-invalid-selector-nesting': 'error',
63
+ '@plumeria/no-mixed-styling-props': 'error',
56
64
  '@plumeria/no-unknown-css-properties': 'error',
57
65
  '@plumeria/no-unused-keys': 'warn',
58
66
  '@plumeria/sort-properties': 'warn',
@@ -0,0 +1,2 @@
1
+ import { Rule } from 'eslint';
2
+ export declare const noInvalidSelectorNesting: Rule.RuleModule;
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noInvalidSelectorNesting = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ exports.noInvalidSelectorNesting = {
6
+ meta: {
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Disallow invalid selector nesting (e.g. Pseudo -> Query, Query -> Query) based on Plumeria rules.',
10
+ },
11
+ messages: {
12
+ noQueryInsidePseudo: 'Media/Container queries cannot be nested inside pseudo-selectors.',
13
+ noQueryInsideQuery: 'Media/Container queries cannot be nested inside other queries.',
14
+ noPseudoInsidePseudo: 'Pseudo-selectors cannot be nested inside other pseudo-selectors.',
15
+ },
16
+ schema: [],
17
+ },
18
+ create(context) {
19
+ const plumeriaAliases = {};
20
+ const parserServices = context.sourceCode.parserServices;
21
+ const checker = parserServices?.program?.getTypeChecker();
22
+ function getSelectorType(node) {
23
+ if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Literal &&
24
+ typeof node.value === 'string') {
25
+ if (node.value.startsWith('@'))
26
+ return 'QUERY';
27
+ if (node.value.startsWith(':'))
28
+ return 'PSEUDO';
29
+ return 'PROPERTY';
30
+ }
31
+ if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
32
+ !(node.parent?.type === utils_1.TSESTree.AST_NODE_TYPES.Property &&
33
+ node.parent.computed)) {
34
+ return 'PROPERTY';
35
+ }
36
+ if (checker && parserServices?.esTreeNodeToTSNodeMap) {
37
+ try {
38
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
39
+ const type = checker.getTypeAtLocation(tsNode);
40
+ if (type.isStringLiteral()) {
41
+ if (type.value.startsWith('@'))
42
+ return 'QUERY';
43
+ if (type.value.startsWith(':'))
44
+ return 'PSEUDO';
45
+ }
46
+ if (type.isUnion()) {
47
+ const types = type.types;
48
+ if (types.every((t) => t.isStringLiteral() && t.value.startsWith('@')))
49
+ return 'QUERY';
50
+ if (types.every((t) => t.isStringLiteral() && t.value.startsWith(':')))
51
+ return 'PSEUDO';
52
+ }
53
+ }
54
+ catch (e) {
55
+ }
56
+ }
57
+ return 'UNKNOWN';
58
+ }
59
+ function checkNesting(node, parentType) {
60
+ for (const prop of node.properties) {
61
+ if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
62
+ continue;
63
+ const currentType = getSelectorType(prop.key);
64
+ if (parentType === 'PSEUDO' && currentType === 'QUERY') {
65
+ context.report({
66
+ node: prop.key,
67
+ messageId: 'noQueryInsidePseudo',
68
+ });
69
+ }
70
+ else if (parentType === 'QUERY' && currentType === 'QUERY') {
71
+ context.report({ node: prop.key, messageId: 'noQueryInsideQuery' });
72
+ }
73
+ else if (parentType === 'PSEUDO' && currentType === 'PSEUDO') {
74
+ context.report({
75
+ node: prop.key,
76
+ messageId: 'noPseudoInsidePseudo',
77
+ });
78
+ }
79
+ if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
80
+ checkNesting(prop.value, currentType === 'PROPERTY' ? parentType : currentType);
81
+ }
82
+ }
83
+ }
84
+ return {
85
+ ImportDeclaration(node) {
86
+ if (node.source.value === '@plumeria/core') {
87
+ node.specifiers.forEach((specifier) => {
88
+ if (specifier.type ===
89
+ utils_1.TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier ||
90
+ specifier.type === utils_1.TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier) {
91
+ plumeriaAliases[specifier.local.name] = 'NAMESPACE';
92
+ }
93
+ else {
94
+ const importedName = specifier.imported.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier
95
+ ? specifier.imported.name
96
+ : String(specifier.imported.value);
97
+ plumeriaAliases[specifier.local.name] = importedName;
98
+ }
99
+ });
100
+ }
101
+ },
102
+ CallExpression(node) {
103
+ let isCssCreate = false;
104
+ if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression) {
105
+ if (node.callee.object.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
106
+ plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
107
+ const propertyName = node.callee.property.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier
108
+ ? node.callee.property.name
109
+ : null;
110
+ if (propertyName === 'create')
111
+ isCssCreate = true;
112
+ }
113
+ }
114
+ else if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) {
115
+ const alias = plumeriaAliases[node.callee.name];
116
+ if (alias === 'create')
117
+ isCssCreate = true;
118
+ }
119
+ if (isCssCreate &&
120
+ node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
121
+ const styleObj = node.arguments[0];
122
+ styleObj.properties.forEach((prop) => {
123
+ if (prop.type === utils_1.TSESTree.AST_NODE_TYPES.Property &&
124
+ prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
125
+ checkNesting(prop.value, 'CLASS');
126
+ }
127
+ });
128
+ }
129
+ },
130
+ };
131
+ },
132
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const noMixedStylingProps: Rule.RuleModule;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noMixedStylingProps = void 0;
4
+ exports.noMixedStylingProps = {
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Disallow className and style props when styleName is present',
9
+ },
10
+ messages: {
11
+ noMixedStylingProps: '"styleName" handles both "className" and "style". Avoid mixing them.',
12
+ },
13
+ schema: [],
14
+ },
15
+ create(context) {
16
+ return {
17
+ JSXOpeningElement(node) {
18
+ const attributes = node.attributes;
19
+ const hasStyleName = attributes.some((attr) => attr.type === 'JSXAttribute' &&
20
+ attr.name.type === 'JSXIdentifier' &&
21
+ attr.name.name === 'styleName');
22
+ if (hasStyleName) {
23
+ for (const attr of attributes) {
24
+ if (attr.type === 'JSXAttribute' &&
25
+ attr.name.type === 'JSXIdentifier' &&
26
+ (attr.name.name === 'className' || attr.name.name === 'style')) {
27
+ context.report({
28
+ node: attr,
29
+ messageId: 'noMixedStylingProps',
30
+ });
31
+ }
32
+ }
33
+ }
34
+ },
35
+ };
36
+ },
37
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/eslint-plugin",
3
- "version": "11.0.2",
3
+ "version": "11.1.0",
4
4
  "description": "Plumeria ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -36,6 +36,7 @@
36
36
  "devDependencies": {
37
37
  "@types/eslint": "^9.6.1",
38
38
  "@types/estree": "^1.0.8",
39
+ "@typescript-eslint/parser": "^8.59.2",
39
40
  "eslint": "^9.39.0"
40
41
  },
41
42
  "publishConfig": {
@@ -43,6 +44,7 @@
43
44
  "provenance": true
44
45
  },
45
46
  "dependencies": {
47
+ "@typescript-eslint/utils": "^8.59.2",
46
48
  "known-css-properties": "^0.37.0"
47
49
  },
48
50
  "scripts": {