@typedcss/eslint-plugin 22.0.7 → 22.0.9

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.
@@ -77,6 +77,8 @@ exports.noUnresolvedComposition = {
77
77
  return null;
78
78
  }
79
79
  function meaningful(node) {
80
+ if (typeof node === 'string')
81
+ return node.trim().length > 0;
80
82
  if (node.type === 'Literal' && typeof node.value === 'string') {
81
83
  return node.value.trim().length > 0;
82
84
  }
@@ -97,7 +99,178 @@ exports.noUnresolvedComposition = {
97
99
  (0, coreImport_1.memberName)(node) === 'className' &&
98
100
  isProps(node.object));
99
101
  }
102
+ const markups = new WeakMap();
103
+ function markup(node) {
104
+ if (node.type !== 'TemplateLiteral')
105
+ return null;
106
+ if (markups.has(node))
107
+ return markups.get(node);
108
+ const values = [];
109
+ let state = 'text';
110
+ let tagged = false;
111
+ let name = '';
112
+ let quote = '';
113
+ let value = null;
114
+ let text = '';
115
+ let tail = '';
116
+ const begin = () => {
117
+ value = [];
118
+ text = '';
119
+ };
120
+ const end = () => {
121
+ if (value === null)
122
+ return;
123
+ if (text)
124
+ value.push(text);
125
+ const attribute = name.toLowerCase();
126
+ if (attribute === 'class' || attribute === 'classname')
127
+ values.push(value);
128
+ value = null;
129
+ };
130
+ node.quasis.forEach((quasi, index) => {
131
+ for (const char of quasi.value.cooked ?? quasi.value.raw) {
132
+ const space = /\s/.test(char);
133
+ switch (state) {
134
+ case 'text':
135
+ if (char === '<')
136
+ state = 'open';
137
+ break;
138
+ case 'open':
139
+ if (/[a-z]/i.test(char)) {
140
+ state = 'tagName';
141
+ tagged = true;
142
+ }
143
+ else if (char === '!') {
144
+ state = 'declaration';
145
+ tagged = true;
146
+ tail = '';
147
+ }
148
+ else if (char === '/') {
149
+ state = 'skip';
150
+ tagged = true;
151
+ }
152
+ else
153
+ state = 'text';
154
+ break;
155
+ case 'declaration':
156
+ tail += char;
157
+ if (tail === '--') {
158
+ state = 'comment';
159
+ tail = '';
160
+ }
161
+ else if (char === '>')
162
+ state = 'text';
163
+ else if (!'--'.startsWith(tail))
164
+ state = 'skip';
165
+ break;
166
+ case 'comment':
167
+ tail = (tail + char).slice(-3);
168
+ if (tail === '-->')
169
+ state = 'text';
170
+ break;
171
+ case 'skip':
172
+ if (char === '>')
173
+ state = 'text';
174
+ break;
175
+ case 'tagName':
176
+ if (char === '>')
177
+ state = 'text';
178
+ else if (space || char === '/')
179
+ state = 'tag';
180
+ break;
181
+ case 'tag':
182
+ if (char === '>')
183
+ state = 'text';
184
+ else if (!space && char !== '/') {
185
+ state = 'attribute';
186
+ name = char;
187
+ }
188
+ break;
189
+ case 'attribute':
190
+ if (char === '=')
191
+ state = 'beforeValue';
192
+ else if (char === '>')
193
+ state = 'text';
194
+ else if (char === '/')
195
+ state = 'tag';
196
+ else if (space)
197
+ state = 'afterAttribute';
198
+ else
199
+ name += char;
200
+ break;
201
+ case 'afterAttribute':
202
+ if (char === '=')
203
+ state = 'beforeValue';
204
+ else if (char === '>')
205
+ state = 'text';
206
+ else if (char === '/')
207
+ state = 'tag';
208
+ else if (!space) {
209
+ state = 'attribute';
210
+ name = char;
211
+ }
212
+ break;
213
+ case 'beforeValue':
214
+ if (char === '"' || char === "'") {
215
+ state = 'quoted';
216
+ quote = char;
217
+ begin();
218
+ }
219
+ else if (char === '>')
220
+ state = 'text';
221
+ else if (!space) {
222
+ state = 'unquoted';
223
+ begin();
224
+ text = char;
225
+ }
226
+ break;
227
+ case 'quoted':
228
+ if (char === quote) {
229
+ end();
230
+ state = 'tag';
231
+ }
232
+ else
233
+ text += char;
234
+ break;
235
+ case 'unquoted':
236
+ if (space || char === '>') {
237
+ end();
238
+ state = char === '>' ? 'text' : 'tag';
239
+ }
240
+ else
241
+ text += char;
242
+ break;
243
+ }
244
+ }
245
+ const expression = node.expressions[index];
246
+ if (!expression)
247
+ return;
248
+ if (state === 'beforeValue') {
249
+ state = 'unquoted';
250
+ begin();
251
+ }
252
+ if (state === 'quoted' || state === 'unquoted') {
253
+ if (text)
254
+ value.push(text);
255
+ text = '';
256
+ value.push(expression);
257
+ }
258
+ else if (state === 'open') {
259
+ state = 'tagName';
260
+ tagged = true;
261
+ }
262
+ else if (state === 'attribute' || state === 'afterAttribute') {
263
+ name = '';
264
+ }
265
+ });
266
+ end();
267
+ const result = tagged ? values : null;
268
+ markups.set(node, result);
269
+ return result;
270
+ }
100
271
  function uses(node, seen = new Set()) {
272
+ if (typeof node === 'string' || markup(node))
273
+ return [];
101
274
  if (isResult(node))
102
275
  return [node];
103
276
  if ((node.type === 'Identifier' || node.type === 'MemberExpression') &&
@@ -146,7 +319,17 @@ exports.noUnresolvedComposition = {
146
319
  context.report({ node, messageId: 'stringified' });
147
320
  return;
148
321
  }
149
- const values = children.flatMap(flatten).filter(meaningful);
322
+ const attributes = markup(node);
323
+ if (attributes) {
324
+ for (const pieces of attributes) {
325
+ compose(node, pieces.flatMap((piece) => typeof piece === 'string' ? [piece] : flatten(piece)));
326
+ }
327
+ return;
328
+ }
329
+ compose(node, children.flatMap(flatten));
330
+ }
331
+ function compose(node, pieces) {
332
+ const values = pieces.filter(meaningful);
150
333
  const calls = values.flatMap((value) => uses(value));
151
334
  if (values.length < 2 || !calls.some((call) => !reported.has(call)))
152
335
  return;
@@ -156,9 +339,11 @@ exports.noUnresolvedComposition = {
156
339
  return;
157
340
  }
158
341
  const label = (value) => {
159
- const text = value.type === 'TemplateElement'
160
- ? JSON.stringify((value.value.cooked ?? value.value.raw).trim())
161
- : context.sourceCode.getText(value);
342
+ const text = typeof value === 'string'
343
+ ? JSON.stringify(value.trim())
344
+ : value.type === 'TemplateElement'
345
+ ? JSON.stringify((value.value.cooked ?? value.value.raw).trim())
346
+ : context.sourceCode.getText(value);
162
347
  return text.length > 40 ? `${text.slice(0, 39)}…` : text;
163
348
  };
164
349
  context.report({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typedcss/eslint-plugin",
3
- "version": "22.0.7",
3
+ "version": "22.0.9",
4
4
  "description": "TypedCSS ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "oxlint": "1.87.0",
57
57
  "zss-engine": "3.1.2",
58
- "@typedcss/oxide": "22.0.7"
58
+ "@typedcss/oxide": "22.0.9"
59
59
  },
60
60
  "publishConfig": {
61
61
  "access": "public",