@plumeria/eslint-plugin 19.3.0 → 19.4.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
@@ -20,8 +20,8 @@ The `plugin:@plumeria/recommended` config enables the following:
20
20
  - `@plumeria/sort-properties`: **warn**
21
21
  - `@plumeria/format-properties`: **warn**
22
22
  - `@plumeria/validate-values`: **warn**
23
- - `@plumeria/validate-pseudos`: **error**
24
- - `@plumeria/validate-at-rules`: **error**
23
+ - `@plumeria/validate-pseudos`: **warn**
24
+ - `@plumeria/validate-at-rules`: **warn**
25
25
 
26
26
  ```js
27
27
  import plumeria from '@plumeria/eslint-plugin';
package/dist/index.js CHANGED
@@ -58,8 +58,8 @@ const configs = {
58
58
  '@plumeria/sort-properties': 'warn',
59
59
  '@plumeria/format-properties': 'warn',
60
60
  '@plumeria/validate-values': 'warn',
61
- '@plumeria/validate-pseudos': 'error',
62
- '@plumeria/validate-at-rules': 'error',
61
+ '@plumeria/validate-pseudos': 'warn',
62
+ '@plumeria/validate-at-rules': 'warn',
63
63
  },
64
64
  },
65
65
  };
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noInvalidSelector = void 0;
4
- const utils_1 = require("@typescript-eslint/utils");
5
4
  const styleObject_1 = require("../util/styleObject");
5
+ const staticValue_1 = require("../util/staticValue");
6
6
  exports.noInvalidSelector = {
7
7
  meta: {
8
8
  type: 'problem',
@@ -21,43 +21,34 @@ exports.noInvalidSelector = {
21
21
  },
22
22
  create(context) {
23
23
  const plumeriaAliases = {};
24
- const parserServices = context.sourceCode.parserServices;
25
- const checker = parserServices?.program?.getTypeChecker();
24
+ const staticValue = (0, staticValue_1.staticValueResolver)(context);
26
25
  function getSelectorType(node) {
27
- if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Literal &&
28
- typeof node.value === 'string') {
26
+ if (node.type === 'Literal' && typeof node.value === 'string') {
29
27
  if (node.value.startsWith('@'))
30
28
  return 'QUERY';
31
29
  if (node.value.startsWith(':'))
32
30
  return 'PSEUDO';
33
31
  return 'PROPERTY';
34
32
  }
35
- if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
36
- !(node.parent?.type === utils_1.TSESTree.AST_NODE_TYPES.Property &&
37
- node.parent.computed)) {
33
+ if (node.type === 'Identifier' &&
34
+ !(node.parent?.type === 'Property' && node.parent.computed)) {
38
35
  return 'PROPERTY';
39
36
  }
40
- if (checker && parserServices?.esTreeNodeToTSNodeMap) {
41
- try {
42
- const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
43
- const type = checker.getTypeAtLocation(tsNode);
44
- if (type.isStringLiteral()) {
45
- if (type.value.startsWith('@'))
46
- return 'QUERY';
47
- if (type.value.startsWith(':'))
48
- return 'PSEUDO';
49
- return 'PROPERTY';
50
- }
51
- }
52
- catch (error) {
53
- }
54
- return 'UNKNOWN';
37
+ const value = staticValue(node);
38
+ if (typeof value === 'string') {
39
+ if (value.startsWith('@'))
40
+ return 'QUERY';
41
+ if (value.startsWith(':'))
42
+ return 'PSEUDO';
43
+ return 'PROPERTY';
55
44
  }
45
+ if (value !== undefined)
46
+ return 'UNKNOWN';
56
47
  return 'SKIP';
57
48
  }
58
49
  function checkNesting(node, parentType) {
59
50
  for (const prop of node.properties) {
60
- if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
51
+ if (prop.type !== 'Property')
61
52
  continue;
62
53
  const currentType = getSelectorType(prop.key);
63
54
  if (currentType === 'SKIP')
@@ -83,14 +74,14 @@ exports.noInvalidSelector = {
83
74
  messageId: 'noPseudoInsidePseudo',
84
75
  });
85
76
  }
86
- if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
77
+ if (prop.value.type === 'ObjectExpression') {
87
78
  checkNesting(prop.value, currentType === 'PROPERTY' ? parentType : currentType);
88
79
  }
89
80
  }
90
81
  }
91
82
  function checkOnlyProperties(node) {
92
83
  for (const prop of node.properties) {
93
- if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
84
+ if (prop.type !== 'Property')
94
85
  continue;
95
86
  const currentType = getSelectorType(prop.key);
96
87
  if (currentType !== 'PROPERTY') {
@@ -99,24 +90,23 @@ exports.noInvalidSelector = {
99
90
  messageId: 'invalidKeySelector',
100
91
  });
101
92
  }
102
- if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
93
+ if (prop.value.type === 'ObjectExpression') {
103
94
  checkOnlyProperties(prop.value);
104
95
  }
105
96
  }
106
97
  }
107
98
  function getKeyString(node) {
108
- if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Literal &&
109
- typeof node.value === 'string') {
99
+ if (node.type === 'Literal' && typeof node.value === 'string') {
110
100
  return node.value;
111
101
  }
112
- if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) {
102
+ if (node.type === 'Identifier') {
113
103
  return node.name;
114
104
  }
115
105
  return null;
116
106
  }
117
107
  function checkKeyframesKeys(node) {
118
108
  for (const prop of node.properties) {
119
- if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
109
+ if (prop.type !== 'Property')
120
110
  continue;
121
111
  const key = getKeyString(prop.key);
122
112
  if (key == null) {
@@ -125,7 +115,7 @@ exports.noInvalidSelector = {
125
115
  messageId: 'invalidKeyframesKey',
126
116
  });
127
117
  }
128
- if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
118
+ if (prop.value.type === 'ObjectExpression') {
129
119
  checkOnlyProperties(prop.value);
130
120
  }
131
121
  if (key !== null &&
@@ -139,7 +129,7 @@ exports.noInvalidSelector = {
139
129
  function checkViewTransitionKeys(node) {
140
130
  const allowed = new Set(['group', 'imagePair', 'new', 'old']);
141
131
  for (const prop of node.properties) {
142
- if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
132
+ if (prop.type !== 'Property')
143
133
  continue;
144
134
  const key = getKeyString(prop.key);
145
135
  if (key == null) {
@@ -148,7 +138,7 @@ exports.noInvalidSelector = {
148
138
  messageId: 'invalidViewTransitionKey',
149
139
  });
150
140
  }
151
- if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
141
+ if (prop.value.type === 'ObjectExpression') {
152
142
  checkOnlyProperties(prop.value);
153
143
  }
154
144
  if (key !== null && !allowed.has(key)) {
@@ -163,13 +153,12 @@ exports.noInvalidSelector = {
163
153
  ImportDeclaration(node) {
164
154
  if (node.source.value === '@plumeria/core') {
165
155
  node.specifiers.forEach((specifier) => {
166
- if (specifier.type ===
167
- utils_1.TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier ||
168
- specifier.type === utils_1.TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier) {
156
+ if (specifier.type === 'ImportNamespaceSpecifier' ||
157
+ specifier.type === 'ImportDefaultSpecifier') {
169
158
  plumeriaAliases[specifier.local.name] = 'NAMESPACE';
170
159
  }
171
160
  else {
172
- const importedName = specifier.imported.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier
161
+ const importedName = specifier.imported.type === 'Identifier'
173
162
  ? specifier.imported.name
174
163
  : String(specifier.imported.value);
175
164
  plumeriaAliases[specifier.local.name] = importedName;
@@ -181,10 +170,10 @@ exports.noInvalidSelector = {
181
170
  let isCssCreate = false;
182
171
  let isCssKeyframes = false;
183
172
  let isCssViewTransition = false;
184
- if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression) {
185
- if (node.callee.object.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
173
+ if (node.callee.type === 'MemberExpression') {
174
+ if (node.callee.object.type === 'Identifier' &&
186
175
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
187
- const propertyName = node.callee.property.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier
176
+ const propertyName = node.callee.property.type === 'Identifier'
188
177
  ? node.callee.property.name
189
178
  : null;
190
179
  if (propertyName === 'create')
@@ -195,7 +184,7 @@ exports.noInvalidSelector = {
195
184
  isCssViewTransition = true;
196
185
  }
197
186
  }
198
- else if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) {
187
+ else if (node.callee.type === 'Identifier') {
199
188
  const alias = plumeriaAliases[node.callee.name];
200
189
  if (alias === 'create')
201
190
  isCssCreate = true;
@@ -204,11 +193,10 @@ exports.noInvalidSelector = {
204
193
  if (alias === 'viewTransition')
205
194
  isCssViewTransition = true;
206
195
  }
207
- if (isCssCreate &&
208
- node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
196
+ if (isCssCreate && node.arguments[0]?.type === 'ObjectExpression') {
209
197
  const styleObj = node.arguments[0];
210
198
  styleObj.properties.forEach((prop) => {
211
- if (prop.type === utils_1.TSESTree.AST_NODE_TYPES.Property) {
199
+ if (prop.type === 'Property') {
212
200
  const currentType = getSelectorType(prop.key);
213
201
  if (currentType !== 'SKIP' && currentType === 'UNKNOWN') {
214
202
  context.report({
@@ -217,19 +205,18 @@ exports.noInvalidSelector = {
217
205
  });
218
206
  }
219
207
  }
220
- if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
208
+ if (prop.type !== 'Property')
221
209
  return;
222
210
  const style = (0, styleObject_1.styleObjectFromValue)(prop.value);
223
211
  if (style)
224
212
  checkNesting(style, 'CLASS');
225
213
  });
226
214
  }
227
- if (isCssKeyframes &&
228
- node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
215
+ if (isCssKeyframes && node.arguments[0]?.type === 'ObjectExpression') {
229
216
  checkKeyframesKeys(node.arguments[0]);
230
217
  }
231
218
  if (isCssViewTransition &&
232
- node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
219
+ node.arguments[0]?.type === 'ObjectExpression') {
233
220
  checkViewTransitionKeys(node.arguments[0]);
234
221
  }
235
222
  },
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateAtRules = void 0;
4
4
  exports.isValidAtRule = isValidAtRule;
5
- const utils_1 = require("@typescript-eslint/utils");
6
5
  const styleObject_1 = require("../util/styleObject");
6
+ const staticValue_1 = require("../util/staticValue");
7
7
  const VALID_AT_RULES = [
8
8
  '@media',
9
9
  '@container',
@@ -34,28 +34,17 @@ exports.validateAtRules = {
34
34
  },
35
35
  create(context) {
36
36
  const plumeriaAliases = {};
37
- const parserServices = context.sourceCode.parserServices;
38
- const checker = parserServices?.program?.getTypeChecker();
37
+ const staticValue = (0, staticValue_1.staticValueResolver)(context);
39
38
  function getKeyString(node) {
40
- if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Literal &&
41
- typeof node.value === 'string') {
39
+ if (node.type === 'Literal' && typeof node.value === 'string') {
42
40
  return node.value;
43
41
  }
44
- if (checker && parserServices?.esTreeNodeToTSNodeMap) {
45
- try {
46
- const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
47
- const type = checker.getTypeAtLocation(tsNode);
48
- if (type.isStringLiteral())
49
- return type.value;
50
- }
51
- catch (error) {
52
- }
53
- }
54
- return null;
42
+ const value = staticValue(node);
43
+ return typeof value === 'string' ? value : null;
55
44
  }
56
45
  function checkProperties(node) {
57
46
  for (const prop of node.properties) {
58
- if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
47
+ if (prop.type !== 'Property')
59
48
  continue;
60
49
  const key = getKeyString(prop.key);
61
50
  if (key?.startsWith('@') && !isValidAtRule(key)) {
@@ -65,7 +54,7 @@ exports.validateAtRules = {
65
54
  data: { rule: key },
66
55
  });
67
56
  }
68
- if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
57
+ if (prop.value.type === 'ObjectExpression') {
69
58
  checkProperties(prop.value);
70
59
  }
71
60
  }
@@ -75,13 +64,12 @@ exports.validateAtRules = {
75
64
  if (node.source.value !== '@plumeria/core')
76
65
  return;
77
66
  node.specifiers.forEach((specifier) => {
78
- if (specifier.type ===
79
- utils_1.TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier ||
80
- specifier.type === utils_1.TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier) {
67
+ if (specifier.type === 'ImportNamespaceSpecifier' ||
68
+ specifier.type === 'ImportDefaultSpecifier') {
81
69
  plumeriaAliases[specifier.local.name] = 'NAMESPACE';
82
70
  }
83
71
  else {
84
- const importedName = specifier.imported.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier
72
+ const importedName = specifier.imported.type === 'Identifier'
85
73
  ? specifier.imported.name
86
74
  : String(specifier.imported.value);
87
75
  plumeriaAliases[specifier.local.name] = importedName;
@@ -90,23 +78,22 @@ exports.validateAtRules = {
90
78
  },
91
79
  CallExpression(node) {
92
80
  let isCssCreate = false;
93
- if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression &&
94
- node.callee.object.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
81
+ if (node.callee.type === 'MemberExpression' &&
82
+ node.callee.object.type === 'Identifier' &&
95
83
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE' &&
96
- node.callee.property.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
84
+ node.callee.property.type === 'Identifier' &&
97
85
  node.callee.property.name === 'create') {
98
86
  isCssCreate = true;
99
87
  }
100
- else if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
88
+ else if (node.callee.type === 'Identifier' &&
101
89
  plumeriaAliases[node.callee.name] === 'create') {
102
90
  isCssCreate = true;
103
91
  }
104
- if (!isCssCreate ||
105
- node.arguments[0]?.type !== utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
92
+ if (!isCssCreate || node.arguments[0]?.type !== 'ObjectExpression') {
106
93
  return;
107
94
  }
108
95
  for (const prop of node.arguments[0].properties) {
109
- if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
96
+ if (prop.type !== 'Property')
110
97
  continue;
111
98
  const style = (0, styleObject_1.styleObjectFromValue)(prop.value);
112
99
  if (style)
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validatePseudos = void 0;
4
4
  exports.splitChainedPseudos = splitChainedPseudos;
5
5
  exports.isValidPseudo = isValidPseudo;
6
- const utils_1 = require("@typescript-eslint/utils");
7
6
  const styleObject_1 = require("../util/styleObject");
7
+ const staticValue_1 = require("../util/staticValue");
8
8
  const VALID_STATIC_PSEUDOS = new Set([
9
9
  ':hover',
10
10
  ':active',
@@ -147,34 +147,21 @@ exports.validatePseudos = {
147
147
  },
148
148
  create(context) {
149
149
  const plumeriaAliases = {};
150
- const parserServices = context.sourceCode.parserServices;
151
- const checker = parserServices?.program?.getTypeChecker();
150
+ const staticValue = (0, staticValue_1.staticValueResolver)(context);
152
151
  function getSelectorString(node) {
153
- if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Literal &&
154
- typeof node.value === 'string') {
152
+ if (node.type === 'Literal' && typeof node.value === 'string') {
155
153
  return node.value;
156
154
  }
157
- if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
158
- !(node.parent?.type === utils_1.TSESTree.AST_NODE_TYPES.Property &&
159
- node.parent.computed)) {
155
+ if (node.type === 'Identifier' &&
156
+ !(node.parent?.type === 'Property' && node.parent.computed)) {
160
157
  return node.name;
161
158
  }
162
- if (checker && parserServices?.esTreeNodeToTSNodeMap) {
163
- try {
164
- const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
165
- const type = checker.getTypeAtLocation(tsNode);
166
- if (type.isStringLiteral()) {
167
- return type.value;
168
- }
169
- }
170
- catch (error) {
171
- }
172
- }
173
- return null;
159
+ const value = staticValue(node);
160
+ return typeof value === 'string' ? value : null;
174
161
  }
175
162
  function checkProperties(node) {
176
163
  for (const prop of node.properties) {
177
- if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
164
+ if (prop.type !== 'Property')
178
165
  continue;
179
166
  const selectorString = getSelectorString(prop.key);
180
167
  if (selectorString !== null && selectorString.startsWith(':')) {
@@ -192,7 +179,7 @@ exports.validatePseudos = {
192
179
  }
193
180
  }
194
181
  }
195
- if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
182
+ if (prop.value.type === 'ObjectExpression') {
196
183
  checkProperties(prop.value);
197
184
  }
198
185
  }
@@ -201,13 +188,12 @@ exports.validatePseudos = {
201
188
  ImportDeclaration(node) {
202
189
  if (node.source.value === '@plumeria/core') {
203
190
  node.specifiers.forEach((specifier) => {
204
- if (specifier.type ===
205
- utils_1.TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier ||
206
- specifier.type === utils_1.TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier) {
191
+ if (specifier.type === 'ImportNamespaceSpecifier' ||
192
+ specifier.type === 'ImportDefaultSpecifier') {
207
193
  plumeriaAliases[specifier.local.name] = 'NAMESPACE';
208
194
  }
209
195
  else {
210
- const importedName = specifier.imported.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier
196
+ const importedName = specifier.imported.type === 'Identifier'
211
197
  ? specifier.imported.name
212
198
  : String(specifier.imported.value);
213
199
  plumeriaAliases[specifier.local.name] = importedName;
@@ -217,26 +203,25 @@ exports.validatePseudos = {
217
203
  },
218
204
  CallExpression(node) {
219
205
  let isCssCreate = false;
220
- if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression) {
221
- if (node.callee.object.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
206
+ if (node.callee.type === 'MemberExpression') {
207
+ if (node.callee.object.type === 'Identifier' &&
222
208
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
223
- const propertyName = node.callee.property.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier
209
+ const propertyName = node.callee.property.type === 'Identifier'
224
210
  ? node.callee.property.name
225
211
  : null;
226
212
  if (propertyName === 'create')
227
213
  isCssCreate = true;
228
214
  }
229
215
  }
230
- else if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) {
216
+ else if (node.callee.type === 'Identifier') {
231
217
  const alias = plumeriaAliases[node.callee.name];
232
218
  if (alias === 'create')
233
219
  isCssCreate = true;
234
220
  }
235
- if (isCssCreate &&
236
- node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
221
+ if (isCssCreate && node.arguments[0]?.type === 'ObjectExpression') {
237
222
  const styleObj = node.arguments[0];
238
223
  styleObj.properties.forEach((prop) => {
239
- if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
224
+ if (prop.type !== 'Property')
240
225
  return;
241
226
  const style = (0, styleObject_1.styleObjectFromValue)(prop.value);
242
227
  if (style)
@@ -0,0 +1,2 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare function staticValueResolver(context: Rule.RuleContext): (node: Rule.Node | any) => unknown;
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.staticValueResolver = staticValueResolver;
4
+ const utils_1 = require("@plumeria/utils");
5
+ const CORE = '@plumeria/core';
6
+ const unwrap = (node) => {
7
+ while (node &&
8
+ (node.type === 'TSAsExpression' ||
9
+ node.type === 'TSSatisfiesExpression' ||
10
+ node.type === 'TSNonNullExpression' ||
11
+ node.type === 'TSTypeAssertion'))
12
+ node = node.expression;
13
+ return node;
14
+ };
15
+ const findVariable = (scope, name) => {
16
+ for (let current = scope; current; current = current.upper) {
17
+ const variable = current.set.get(name);
18
+ if (variable)
19
+ return variable;
20
+ }
21
+ return undefined;
22
+ };
23
+ const importedNameOf = (specifier) => {
24
+ if (specifier.type === 'ImportNamespaceSpecifier')
25
+ return null;
26
+ if (specifier.type === 'ImportDefaultSpecifier')
27
+ return 'default';
28
+ return specifier.imported.type === 'Identifier'
29
+ ? specifier.imported.name
30
+ : String(specifier.imported.value);
31
+ };
32
+ function staticValueResolver(context) {
33
+ const { sourceCode, filename } = context;
34
+ const visiting = new Set();
35
+ const bindingOf = (node) => {
36
+ const definition = findVariable(sourceCode.getScope(node), node.name)
37
+ ?.defs[0];
38
+ if (!definition)
39
+ return undefined;
40
+ if (definition.type === 'Variable' &&
41
+ definition.parent.kind === 'const' &&
42
+ definition.node.init) {
43
+ return { kind: 'value', node: definition.node.init };
44
+ }
45
+ if (definition.type === 'ImportBinding') {
46
+ return {
47
+ kind: 'import',
48
+ source: String(definition.parent.source.value),
49
+ importedName: importedNameOf(definition.node),
50
+ };
51
+ }
52
+ return undefined;
53
+ };
54
+ const importedValue = (source, exportName) => {
55
+ const actualPath = (0, utils_1.resolveImportPath)(source, filename);
56
+ if (!actualPath)
57
+ return undefined;
58
+ return (0, utils_1.resolveExportValue)(actualPath, exportName);
59
+ };
60
+ const isCreateStatic = (callee) => {
61
+ callee = unwrap(callee);
62
+ if (callee.type === 'Identifier') {
63
+ const binding = bindingOf(callee);
64
+ return (binding?.kind === 'import' &&
65
+ binding.source === CORE &&
66
+ binding.importedName === 'createStatic');
67
+ }
68
+ if (callee.type === 'MemberExpression' &&
69
+ !callee.computed &&
70
+ callee.property.type === 'Identifier' &&
71
+ callee.property.name === 'createStatic' &&
72
+ callee.object.type === 'Identifier') {
73
+ const binding = bindingOf(callee.object);
74
+ return (binding?.kind === 'import' &&
75
+ binding.source === CORE &&
76
+ (binding.importedName === null || binding.importedName === 'default'));
77
+ }
78
+ return false;
79
+ };
80
+ const memberKey = (node) => {
81
+ if (!node.computed && node.property.type === 'Identifier') {
82
+ return node.property.name;
83
+ }
84
+ const key = resolve(node.property);
85
+ return typeof key === 'string' ? key : undefined;
86
+ };
87
+ const resolve = (node) => {
88
+ node = unwrap(node);
89
+ if (!node || visiting.has(node))
90
+ return undefined;
91
+ visiting.add(node);
92
+ try {
93
+ switch (node.type) {
94
+ case 'Literal':
95
+ return typeof node.value === 'string' ||
96
+ typeof node.value === 'number' ||
97
+ typeof node.value === 'boolean'
98
+ ? node.value
99
+ : undefined;
100
+ case 'BinaryExpression': {
101
+ const left = resolve(node.left);
102
+ const right = resolve(node.right);
103
+ if (left === undefined || right === undefined)
104
+ return undefined;
105
+ if (node.operator === '+') {
106
+ return typeof left === 'object' || typeof right === 'object'
107
+ ? undefined
108
+ : left + right;
109
+ }
110
+ if (typeof left !== 'number' || typeof right !== 'number')
111
+ return undefined;
112
+ switch (node.operator) {
113
+ case '-':
114
+ return left - right;
115
+ case '*':
116
+ return left * right;
117
+ case '/':
118
+ return left / right;
119
+ case '%':
120
+ return left % right;
121
+ case '**':
122
+ return left ** right;
123
+ default:
124
+ return undefined;
125
+ }
126
+ }
127
+ case 'TemplateLiteral':
128
+ return node.expressions.length === 0
129
+ ? node.quasis[0].value.cooked
130
+ : undefined;
131
+ case 'Identifier': {
132
+ const binding = bindingOf(node);
133
+ if (binding?.kind === 'value')
134
+ return resolve(binding.node);
135
+ if (binding?.kind === 'import' && binding.importedName) {
136
+ return importedValue(binding.source, binding.importedName);
137
+ }
138
+ return undefined;
139
+ }
140
+ case 'MemberExpression': {
141
+ const key = memberKey(node);
142
+ if (key === undefined)
143
+ return undefined;
144
+ const object = unwrap(node.object);
145
+ if (object.type === 'Identifier') {
146
+ const binding = bindingOf(object);
147
+ if (binding?.kind === 'import' && !binding.importedName) {
148
+ return importedValue(binding.source, key);
149
+ }
150
+ }
151
+ const value = resolve(object);
152
+ return value && typeof value === 'object' && Object.hasOwn(value, key)
153
+ ? value[key]
154
+ : undefined;
155
+ }
156
+ case 'ObjectExpression': {
157
+ const value = Object.create(null);
158
+ for (const property of node.properties) {
159
+ if (property.type !== 'Property' || property.kind !== 'init')
160
+ continue;
161
+ const key = !property.computed && property.key.type === 'Identifier'
162
+ ? property.key.name
163
+ : resolve(property.key);
164
+ if (typeof key !== 'string')
165
+ continue;
166
+ value[key] = resolve(property.value);
167
+ }
168
+ return value;
169
+ }
170
+ case 'CallExpression':
171
+ return isCreateStatic(node.callee)
172
+ ? resolve(node.arguments[0])
173
+ : undefined;
174
+ default:
175
+ return undefined;
176
+ }
177
+ }
178
+ finally {
179
+ visiting.delete(node);
180
+ }
181
+ };
182
+ return (node) => resolve(node);
183
+ }
package/oxlint.json CHANGED
@@ -22,7 +22,7 @@
22
22
  "@plumeria/sort-properties": "warn",
23
23
  "@plumeria/format-properties": "warn",
24
24
  "@plumeria/validate-values": "warn",
25
- "@plumeria/validate-pseudos": "error",
26
- "@plumeria/validate-at-rules": "error"
25
+ "@plumeria/validate-pseudos": "warn",
26
+ "@plumeria/validate-at-rules": "warn"
27
27
  }
28
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/eslint-plugin",
3
- "version": "19.3.0",
3
+ "version": "19.4.0",
4
4
  "description": "Plumeria ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -45,6 +45,7 @@
45
45
  "@types/estree": "^1.0.9",
46
46
  "@types/estree-jsx": "^1.0.5",
47
47
  "@typescript-eslint/parser": "^8.60.0",
48
+ "@typescript-eslint/utils": "^8.60.0",
48
49
  "eslint": "^10.4.0"
49
50
  },
50
51
  "publishConfig": {
@@ -52,10 +53,9 @@
52
53
  "provenance": true
53
54
  },
54
55
  "dependencies": {
55
- "@typescript-eslint/utils": "^8.60.0",
56
56
  "known-css-properties": "^0.37.0",
57
57
  "zss-engine": "2.8.0",
58
- "@plumeria/utils": "19.3.0"
58
+ "@plumeria/utils": "19.4.0"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "rimraf dist && pnpm cjs",