@plumeria/eslint-plugin 11.1.3 → 11.2.1

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,7 +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
+ - `@plumeria/no-invalid-selector`: **error**
16
16
  - `@plumeria/no-mixed-styling-props`: **error**
17
17
  - `@plumeria/no-unknown-css-properties`: **error**
18
18
  - `@plumeria/no-unused-keys`: **warn**
@@ -48,9 +48,10 @@ Disallow passing inline object to `styleName` and `css.use()`. Only compiled sty
48
48
 
49
49
  Disallow calling APIs inside functions.
50
50
 
51
- ### no-invalid-selector-nesting
51
+ ### no-invalid-selector
52
52
 
53
- Disallow invalid selector nesting inside `css.create()`. (e.g. Pseudo -> Query, Query -> Query)
53
+ Disallow invalid selector inside `css.create()` and `css.keyframes()` and `css.viewTransition()`.
54
+ `create()` example: (Pseudo -> Query, Query -> Query)
54
55
 
55
56
  ### no-mixed-styling-props
56
57
 
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ 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");
9
+ const no_invalid_selector_1 = require("./rules/no-invalid-selector");
10
10
  const no_mixed_styling_props_1 = require("./rules/no-mixed-styling-props");
11
11
  const no_unknown_css_properties_1 = require("./rules/no-unknown-css-properties");
12
12
  const no_unused_keys_1 = require("./rules/no-unused-keys");
@@ -19,7 +19,7 @@ const rules = {
19
19
  'no-destructure': no_destructure_1.noDestructure,
20
20
  'no-inline-object': no_inline_object_1.noInlineObject,
21
21
  'no-inner-call': no_inner_call_1.noInnerCall,
22
- 'no-invalid-selector-nesting': no_invalid_selector_nesting_1.noInvalidSelectorNesting,
22
+ 'no-invalid-selector': no_invalid_selector_1.noInvalidSelector,
23
23
  'no-mixed-styling-props': no_mixed_styling_props_1.noMixedStylingProps,
24
24
  'no-unknown-css-properties': no_unknown_css_properties_1.noUnknownCssProperties,
25
25
  'no-unused-keys': no_unused_keys_1.noUnusedKeys,
@@ -36,7 +36,7 @@ const configs = {
36
36
  '@plumeria/no-destructure': 'error',
37
37
  '@plumeria/no-inline-object': 'error',
38
38
  '@plumeria/no-inner-call': 'error',
39
- '@plumeria/no-invalid-selector-nesting': 'error',
39
+ '@plumeria/no-invalid-selector': 'error',
40
40
  '@plumeria/no-mixed-styling-props': 'error',
41
41
  '@plumeria/no-unknown-css-properties': 'error',
42
42
  '@plumeria/no-unused-keys': 'warn',
@@ -59,7 +59,7 @@ const flatConfigs = {
59
59
  '@plumeria/no-destructure': 'error',
60
60
  '@plumeria/no-inline-object': 'error',
61
61
  '@plumeria/no-inner-call': 'error',
62
- '@plumeria/no-invalid-selector-nesting': 'error',
62
+ '@plumeria/no-invalid-selector': 'error',
63
63
  '@plumeria/no-mixed-styling-props': 'error',
64
64
  '@plumeria/no-unknown-css-properties': 'error',
65
65
  '@plumeria/no-unused-keys': 'warn',
@@ -0,0 +1,2 @@
1
+ import { Rule } from 'eslint';
2
+ export declare const noInvalidSelector: Rule.RuleModule;
@@ -1,17 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noInvalidSelectorNesting = void 0;
3
+ exports.noInvalidSelector = void 0;
4
4
  const utils_1 = require("@typescript-eslint/utils");
5
- exports.noInvalidSelectorNesting = {
5
+ exports.noInvalidSelector = {
6
6
  meta: {
7
7
  type: 'problem',
8
8
  docs: {
9
9
  description: 'Disallow invalid selector nesting (e.g. Pseudo -> Query, Query -> Query) based on Plumeria rules.',
10
10
  },
11
11
  messages: {
12
+ invalidKeySelector: 'Invalid key selector.',
12
13
  noQueryInsidePseudo: 'Media/Container queries cannot be nested inside pseudo-selectors.',
13
14
  noQueryInsideQuery: 'Media/Container queries cannot be nested inside other queries.',
14
15
  noPseudoInsidePseudo: 'Pseudo-selectors cannot be nested inside other pseudo-selectors.',
16
+ invalidKeyframesKey: 'Keyframes keys must be "from", "to", or a percentage value (e.g. "0%", "50%", "100%").',
17
+ invalidViewTransitionKey: 'ViewTransition keys must be one of: "group", "imagePair", "new", "old".',
15
18
  },
16
19
  schema: [],
17
20
  },
@@ -43,15 +46,8 @@ exports.noInvalidSelectorNesting = {
43
46
  if (type.value.startsWith(':'))
44
47
  return 'PSEUDO';
45
48
  }
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
49
  }
54
- catch (e) {
50
+ catch (error) {
55
51
  }
56
52
  }
57
53
  return 'UNKNOWN';
@@ -61,6 +57,12 @@ exports.noInvalidSelectorNesting = {
61
57
  if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
62
58
  continue;
63
59
  const currentType = getSelectorType(prop.key);
60
+ if (currentType === 'UNKNOWN') {
61
+ context.report({
62
+ node: prop.key,
63
+ messageId: 'invalidKeySelector',
64
+ });
65
+ }
64
66
  if (parentType === 'PSEUDO' && currentType === 'QUERY') {
65
67
  context.report({
66
68
  node: prop.key,
@@ -81,6 +83,77 @@ exports.noInvalidSelectorNesting = {
81
83
  }
82
84
  }
83
85
  }
86
+ function checkOnlyProperties(node) {
87
+ for (const prop of node.properties) {
88
+ if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
89
+ continue;
90
+ const currentType = getSelectorType(prop.key);
91
+ if (currentType !== 'PROPERTY') {
92
+ context.report({
93
+ node: prop.key,
94
+ messageId: 'invalidKeySelector',
95
+ });
96
+ }
97
+ if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
98
+ checkOnlyProperties(prop.value);
99
+ }
100
+ }
101
+ }
102
+ function getKeyString(node) {
103
+ if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Literal &&
104
+ typeof node.value === 'string') {
105
+ return node.value;
106
+ }
107
+ if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) {
108
+ return node.name;
109
+ }
110
+ return null;
111
+ }
112
+ function checkKeyframesKeys(node) {
113
+ for (const prop of node.properties) {
114
+ if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
115
+ continue;
116
+ const key = getKeyString(prop.key);
117
+ if (key == null) {
118
+ context.report({
119
+ node: prop.key,
120
+ messageId: 'invalidKeyframesKey',
121
+ });
122
+ }
123
+ if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
124
+ checkOnlyProperties(prop.value);
125
+ }
126
+ if (key !== null &&
127
+ key !== 'from' &&
128
+ key !== 'to' &&
129
+ !/^\d+(\.\d+)?%$/.test(key)) {
130
+ context.report({ node: prop.key, messageId: 'invalidKeyframesKey' });
131
+ }
132
+ }
133
+ }
134
+ function checkViewTransitionKeys(node) {
135
+ const allowed = new Set(['group', 'imagePair', 'new', 'old']);
136
+ for (const prop of node.properties) {
137
+ if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
138
+ continue;
139
+ const key = getKeyString(prop.key);
140
+ if (key == null) {
141
+ context.report({
142
+ node: prop.key,
143
+ messageId: 'invalidViewTransitionKey',
144
+ });
145
+ }
146
+ if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
147
+ checkOnlyProperties(prop.value);
148
+ }
149
+ if (key !== null && !allowed.has(key)) {
150
+ context.report({
151
+ node: prop.key,
152
+ messageId: 'invalidViewTransitionKey',
153
+ });
154
+ }
155
+ }
156
+ }
84
157
  return {
85
158
  ImportDeclaration(node) {
86
159
  if (node.source.value === '@plumeria/core') {
@@ -101,6 +174,8 @@ exports.noInvalidSelectorNesting = {
101
174
  },
102
175
  CallExpression(node) {
103
176
  let isCssCreate = false;
177
+ let isCssKeyframes = false;
178
+ let isCssViewTransition = false;
104
179
  if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression) {
105
180
  if (node.callee.object.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
106
181
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
@@ -109,23 +184,48 @@ exports.noInvalidSelectorNesting = {
109
184
  : null;
110
185
  if (propertyName === 'create')
111
186
  isCssCreate = true;
187
+ if (propertyName === 'keyframes')
188
+ isCssKeyframes = true;
189
+ if (propertyName === 'viewTransition')
190
+ isCssViewTransition = true;
112
191
  }
113
192
  }
114
193
  else if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) {
115
194
  const alias = plumeriaAliases[node.callee.name];
116
195
  if (alias === 'create')
117
196
  isCssCreate = true;
197
+ if (alias === 'keyframes')
198
+ isCssKeyframes = true;
199
+ if (alias === 'viewTransition')
200
+ isCssViewTransition = true;
118
201
  }
119
202
  if (isCssCreate &&
120
203
  node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
121
204
  const styleObj = node.arguments[0];
122
205
  styleObj.properties.forEach((prop) => {
206
+ if (prop.type === utils_1.TSESTree.AST_NODE_TYPES.Property) {
207
+ const currentType = getSelectorType(prop.key);
208
+ if (currentType === 'UNKNOWN') {
209
+ context.report({
210
+ node: prop.key,
211
+ messageId: 'invalidKeySelector',
212
+ });
213
+ }
214
+ }
123
215
  if (prop.type === utils_1.TSESTree.AST_NODE_TYPES.Property &&
124
216
  prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
125
217
  checkNesting(prop.value, 'CLASS');
126
218
  }
127
219
  });
128
220
  }
221
+ if (isCssKeyframes &&
222
+ node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
223
+ checkKeyframesKeys(node.arguments[0]);
224
+ }
225
+ if (isCssViewTransition &&
226
+ node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
227
+ checkViewTransitionKeys(node.arguments[0]);
228
+ }
129
229
  },
130
230
  };
131
231
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/eslint-plugin",
3
- "version": "11.1.3",
3
+ "version": "11.2.1",
4
4
  "description": "Plumeria ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -1,2 +0,0 @@
1
- import { Rule } from 'eslint';
2
- export declare const noInvalidSelectorNesting: Rule.RuleModule;