@typestyles/eslint-plugin 0.2.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/dist/index.js ADDED
@@ -0,0 +1,670 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+
3
+ // src/utils/create-rule.ts
4
+ var createRule = ESLintUtils.RuleCreator(
5
+ (name) => `https://typestyles.dev/docs/eslint#${name}`
6
+ );
7
+
8
+ // src/rules/no-default-scope-in-package.ts
9
+ function memberPropertyName(node) {
10
+ if (node.property.type === "Identifier" && !node.computed) return node.property.name;
11
+ if (node.property.type === "Literal" && typeof node.property.value === "string") {
12
+ return node.property.value;
13
+ }
14
+ return null;
15
+ }
16
+ function isDefaultStylesCall(node) {
17
+ const { callee } = node;
18
+ if (callee.type !== "MemberExpression") return false;
19
+ const method = memberPropertyName(callee);
20
+ if (method !== "class" && method !== "component") return false;
21
+ if (callee.object.type === "Identifier") {
22
+ return callee.object.name === "styles";
23
+ }
24
+ return false;
25
+ }
26
+ var noDefaultScopeInPackage = createRule({
27
+ name: "no-default-scope-in-package",
28
+ meta: {
29
+ type: "suggestion",
30
+ docs: {
31
+ description: "Require a scoped styles factory (`createTypeStyles`/`createStyles` with `scopeId`) instead of the default `styles` export in publishable packages"
32
+ },
33
+ messages: {
34
+ unscopedInPackage: "Using the default `styles.{{method}}()` in a published package risks class-name collisions. Use `createTypeStyles({ scopeId: pkg.name })` or `createStyles({ scopeId })` instead."
35
+ },
36
+ schema: []
37
+ },
38
+ defaultOptions: [],
39
+ create(context) {
40
+ return {
41
+ CallExpression(node) {
42
+ if (!isDefaultStylesCall(node)) return;
43
+ const method = memberPropertyName(node.callee);
44
+ context.report({
45
+ node: node.callee,
46
+ messageId: "unscopedInPackage",
47
+ data: { method: method ?? "class" }
48
+ });
49
+ }
50
+ };
51
+ }
52
+ });
53
+
54
+ // src/utils/style-calls.ts
55
+ function memberPropertyName2(node) {
56
+ if (node.property.type === "Identifier" && !node.computed) return node.property.name;
57
+ if (node.property.type === "Literal" && typeof node.property.value === "string") {
58
+ return node.property.value;
59
+ }
60
+ return null;
61
+ }
62
+ function calleeRootName(node) {
63
+ if (node.type === "Identifier") return node.name;
64
+ if (node.type === "MemberExpression") {
65
+ if (node.object.type === "Identifier") return node.object.name;
66
+ if (node.object.type === "MemberExpression") {
67
+ const inner = memberPropertyName2(node.object);
68
+ const outer = calleeRootName(node.object);
69
+ if (outer && inner) return `${outer}.${inner}`;
70
+ }
71
+ }
72
+ return null;
73
+ }
74
+ function isMemberCall(callee, objectName, method) {
75
+ if (callee.type !== "MemberExpression") return false;
76
+ const methodName = memberPropertyName2(callee);
77
+ if (methodName !== method) return false;
78
+ if (callee.object.type === "Identifier") return callee.object.name === objectName;
79
+ return false;
80
+ }
81
+ function firstStringArg(args) {
82
+ const first = args[0];
83
+ if (!first || first.type === "SpreadElement") return null;
84
+ if (first.type === "Literal" && typeof first.value === "string") return first;
85
+ return null;
86
+ }
87
+ function getNamespaceCall(node) {
88
+ const { callee, arguments: args } = node;
89
+ if (isMemberCall(callee, "styles", "class")) {
90
+ const nameNode = firstStringArg(args);
91
+ if (!nameNode) return null;
92
+ return { kind: "styles.class", key: `.${nameNode.value}-`, nameNode };
93
+ }
94
+ if (isMemberCall(callee, "styles", "component")) {
95
+ const nameNode = firstStringArg(args);
96
+ if (!nameNode) return null;
97
+ return { kind: "styles.component", key: `.${nameNode.value}-`, nameNode };
98
+ }
99
+ if (isMemberCall(callee, "styles", "hashClass")) {
100
+ const first = args[0];
101
+ if (!first || first.type === "SpreadElement") return null;
102
+ if (first.type === "Literal" && typeof first.value === "string") {
103
+ return { kind: "styles.hashClass", key: `.${first.value}-`, nameNode: first };
104
+ }
105
+ const label = args[1];
106
+ if (label?.type === "Literal" && typeof label.value === "string") {
107
+ return { kind: "styles.hashClass", key: `.${label.value}-`, nameNode: label };
108
+ }
109
+ return null;
110
+ }
111
+ if (isMemberCall(callee, "tokens", "create")) {
112
+ const nameNode = firstStringArg(args);
113
+ if (!nameNode) return null;
114
+ return { kind: "tokens.create", key: `tokens:${nameNode.value}`, nameNode };
115
+ }
116
+ if (isMemberCall(callee, "tokens", "createTheme")) {
117
+ const nameNode = firstStringArg(args);
118
+ if (!nameNode) return null;
119
+ return { kind: "tokens.createTheme", key: `theme:${nameNode.value}`, nameNode };
120
+ }
121
+ if (callee.type === "Identifier" && callee.name === "createTheme") {
122
+ const nameNode = firstStringArg(args);
123
+ if (!nameNode) return null;
124
+ return { kind: "createTheme", key: `theme:${nameNode.value}`, nameNode };
125
+ }
126
+ if (isMemberCall(callee, "keyframes", "create")) {
127
+ const nameNode = firstStringArg(args);
128
+ if (!nameNode) return null;
129
+ return { kind: "keyframes.create", key: `keyframes:${nameNode.value}`, nameNode };
130
+ }
131
+ if (isMemberCall(callee, "global", "style")) {
132
+ const nameNode = firstStringArg(args);
133
+ if (!nameNode) return null;
134
+ return { kind: "global.style", key: nameNode.value, nameNode };
135
+ }
136
+ if (isMemberCall(callee, "global", "fontFace")) {
137
+ const nameNode = firstStringArg(args);
138
+ if (!nameNode) return null;
139
+ return { kind: "global.fontFace", key: `font-face:${nameNode.value}`, nameNode };
140
+ }
141
+ if (callee.type === "MemberExpression") {
142
+ const method = memberPropertyName2(callee);
143
+ const root = calleeRootName(callee);
144
+ if (method === "class" && root) {
145
+ const nameNode = firstStringArg(args);
146
+ if (!nameNode) return null;
147
+ return { kind: "styles.class", key: `.${nameNode.value}-`, nameNode };
148
+ }
149
+ if (method === "component" && root) {
150
+ const nameNode = firstStringArg(args);
151
+ if (!nameNode) return null;
152
+ return { kind: "styles.component", key: `.${nameNode.value}-`, nameNode };
153
+ }
154
+ if (method === "create" && root?.endsWith("tokens")) {
155
+ const nameNode = firstStringArg(args);
156
+ if (!nameNode) return null;
157
+ return { kind: "tokens.create", key: `tokens:${nameNode.value}`, nameNode };
158
+ }
159
+ }
160
+ return null;
161
+ }
162
+ function getStyleObjectArguments(node) {
163
+ const { callee, arguments: args } = node;
164
+ const objects = [];
165
+ const pushObject = (arg) => {
166
+ if (!arg || arg.type === "SpreadElement") return;
167
+ if (arg.type === "ObjectExpression") objects.push(arg);
168
+ if (arg.type === "ArrowFunctionExpression" && arg.body.type === "ObjectExpression") {
169
+ objects.push(arg.body);
170
+ }
171
+ };
172
+ if (callee.type === "MemberExpression") {
173
+ const method = memberPropertyName2(callee);
174
+ if (method === "class" || method === "component") {
175
+ pushObject(args[1]);
176
+ return objects;
177
+ }
178
+ if (method === "hashClass") {
179
+ const first = args[0];
180
+ if (first && first.type !== "SpreadElement" && first.type === "ObjectExpression") {
181
+ objects.push(first);
182
+ }
183
+ return objects;
184
+ }
185
+ }
186
+ return objects;
187
+ }
188
+
189
+ // src/rules/no-duplicate-namespace.ts
190
+ var namespaceRegistry = /* @__PURE__ */ new Map();
191
+ var noDuplicateNamespace = createRule({
192
+ name: "no-duplicate-namespace",
193
+ meta: {
194
+ type: "problem",
195
+ docs: {
196
+ description: "Disallow reusing the same TypeStyles namespace (`styles.class`, `styles.component`, `tokens.create`, \u2026) across the project"
197
+ },
198
+ messages: {
199
+ duplicateInFile: "Namespace `{{name}}` ({{kind}}) is already registered in this file at line {{line}}.",
200
+ duplicateAcrossFiles: "Namespace `{{name}}` ({{kind}}) is already registered in `{{otherFile}}` at line {{line}}."
201
+ },
202
+ schema: []
203
+ },
204
+ defaultOptions: [],
205
+ create(context) {
206
+ const fileSeen = /* @__PURE__ */ new Map();
207
+ const filename = context.filename;
208
+ return {
209
+ CallExpression(node) {
210
+ const call = getNamespaceCall(node);
211
+ if (!call) return;
212
+ const displayName = call.kind === "styles.class" || call.kind === "styles.component" ? call.nameNode.value : call.nameNode.value;
213
+ const line = call.nameNode.loc?.start.line ?? node.loc?.start.line ?? 0;
214
+ const column = call.nameNode.loc?.start.column ?? node.loc?.start.column ?? 0;
215
+ const priorInFile = fileSeen.get(call.key);
216
+ if (priorInFile) {
217
+ context.report({
218
+ node: call.nameNode,
219
+ messageId: "duplicateInFile",
220
+ data: {
221
+ name: displayName,
222
+ kind: call.kind,
223
+ line: String(priorInFile.line)
224
+ }
225
+ });
226
+ return;
227
+ }
228
+ fileSeen.set(call.key, { line, column });
229
+ const priorGlobal = namespaceRegistry.get(call.key);
230
+ if (priorGlobal && priorGlobal.filename !== filename) {
231
+ context.report({
232
+ node: call.nameNode,
233
+ messageId: "duplicateAcrossFiles",
234
+ data: {
235
+ name: displayName,
236
+ kind: call.kind,
237
+ otherFile: priorGlobal.filename,
238
+ line: String(priorGlobal.line)
239
+ }
240
+ });
241
+ return;
242
+ }
243
+ if (!priorGlobal) {
244
+ namespaceRegistry.set(call.key, { filename, line, column });
245
+ }
246
+ }
247
+ };
248
+ }
249
+ });
250
+
251
+ // src/utils/css-properties.ts
252
+ var UNITLESS_PROPERTIES = /* @__PURE__ */ new Set([
253
+ "animationIterationCount",
254
+ "aspectRatio",
255
+ "borderImageOutset",
256
+ "borderImageSlice",
257
+ "borderImageWidth",
258
+ "boxFlex",
259
+ "boxFlexGroup",
260
+ "boxOrdinalGroup",
261
+ "columnCount",
262
+ "columns",
263
+ "flex",
264
+ "flexGrow",
265
+ "flexPositive",
266
+ "flexShrink",
267
+ "flexNegative",
268
+ "flexOrder",
269
+ "fontSizeAdjust",
270
+ "fontWeight",
271
+ "gridArea",
272
+ "gridColumn",
273
+ "gridColumnEnd",
274
+ "gridColumnSpan",
275
+ "gridColumnStart",
276
+ "gridRow",
277
+ "gridRowEnd",
278
+ "gridRowSpan",
279
+ "gridRowStart",
280
+ "initialLetter",
281
+ "lineClamp",
282
+ "lineHeight",
283
+ "maskBorderOutset",
284
+ "maskBorderSlice",
285
+ "maskBorderWidth",
286
+ "opacity",
287
+ "order",
288
+ "orphans",
289
+ "scale",
290
+ "tabSize",
291
+ "widows",
292
+ "zIndex",
293
+ "zoom",
294
+ "fillOpacity",
295
+ "floodOpacity",
296
+ "stopOpacity",
297
+ "strokeDasharray",
298
+ "strokeDashoffset",
299
+ "strokeMiterlimit",
300
+ "strokeOpacity",
301
+ "strokeWidth"
302
+ ]);
303
+ var VENDOR_PREFIX_RE = /^(Webkit|Moz|ms|O)([A-Z])(.*)$/;
304
+ function isUnitlessProperty(prop) {
305
+ if (UNITLESS_PROPERTIES.has(prop)) return true;
306
+ const match = VENDOR_PREFIX_RE.exec(prop);
307
+ if (match) {
308
+ return UNITLESS_PROPERTIES.has(match[2].toLowerCase() + match[3]);
309
+ }
310
+ return false;
311
+ }
312
+ var SHORTHAND_TO_LONGHANDS = {
313
+ padding: [
314
+ "paddingTop",
315
+ "paddingRight",
316
+ "paddingBottom",
317
+ "paddingLeft",
318
+ "paddingInline",
319
+ "paddingBlock",
320
+ "paddingInlineStart",
321
+ "paddingInlineEnd",
322
+ "paddingBlockStart",
323
+ "paddingBlockEnd"
324
+ ],
325
+ margin: [
326
+ "marginTop",
327
+ "marginRight",
328
+ "marginBottom",
329
+ "marginLeft",
330
+ "marginInline",
331
+ "marginBlock",
332
+ "marginInlineStart",
333
+ "marginInlineEnd",
334
+ "marginBlockStart",
335
+ "marginBlockEnd"
336
+ ],
337
+ border: [
338
+ "borderWidth",
339
+ "borderStyle",
340
+ "borderColor",
341
+ "borderTop",
342
+ "borderRight",
343
+ "borderBottom",
344
+ "borderLeft",
345
+ "borderInline",
346
+ "borderBlock"
347
+ ],
348
+ borderWidth: [
349
+ "borderTopWidth",
350
+ "borderRightWidth",
351
+ "borderBottomWidth",
352
+ "borderLeftWidth",
353
+ "borderInlineWidth",
354
+ "borderBlockWidth"
355
+ ],
356
+ borderStyle: ["borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle"],
357
+ borderColor: ["borderTopColor", "borderRightColor", "borderBottomColor", "borderLeftColor"],
358
+ borderTop: ["borderTopWidth", "borderTopStyle", "borderTopColor"],
359
+ borderRight: ["borderRightWidth", "borderRightStyle", "borderRightColor"],
360
+ borderBottom: ["borderBottomWidth", "borderBottomStyle", "borderBottomColor"],
361
+ borderLeft: ["borderLeftWidth", "borderLeftStyle", "borderLeftColor"],
362
+ background: [
363
+ "backgroundColor",
364
+ "backgroundImage",
365
+ "backgroundPosition",
366
+ "backgroundSize",
367
+ "backgroundRepeat",
368
+ "backgroundAttachment",
369
+ "backgroundOrigin",
370
+ "backgroundClip"
371
+ ],
372
+ font: ["fontStyle", "fontVariant", "fontWeight", "fontSize", "lineHeight", "fontFamily"],
373
+ flex: ["flexGrow", "flexShrink", "flexBasis"],
374
+ gap: ["rowGap", "columnGap"],
375
+ overflow: ["overflowX", "overflowY"],
376
+ transition: [
377
+ "transitionProperty",
378
+ "transitionDuration",
379
+ "transitionTimingFunction",
380
+ "transitionDelay"
381
+ ],
382
+ animation: [
383
+ "animationName",
384
+ "animationDuration",
385
+ "animationTimingFunction",
386
+ "animationDelay",
387
+ "animationIterationCount",
388
+ "animationDirection",
389
+ "animationFillMode",
390
+ "animationPlayState"
391
+ ],
392
+ outline: ["outlineWidth", "outlineStyle", "outlineColor"],
393
+ listStyle: ["listStyleType", "listStylePosition", "listStyleImage"],
394
+ grid: [
395
+ "gridTemplateRows",
396
+ "gridTemplateColumns",
397
+ "gridTemplateAreas",
398
+ "gridAutoRows",
399
+ "gridAutoColumns",
400
+ "gridAutoFlow"
401
+ ],
402
+ gridTemplate: ["gridTemplateRows", "gridTemplateColumns", "gridTemplateAreas"],
403
+ inset: ["top", "right", "bottom", "left"],
404
+ insetBlock: ["insetBlockStart", "insetBlockEnd"],
405
+ insetInline: ["insetInlineStart", "insetInlineEnd"]
406
+ };
407
+ var BARE_NUMBER_STRING_RE = /^-?\d+(\.\d+)?$/;
408
+ function findShorthandLonghandConflicts(propertyNames) {
409
+ const keys = new Set(propertyNames);
410
+ const seen = /* @__PURE__ */ new Set();
411
+ const conflicts = [];
412
+ for (const key of keys) {
413
+ const longhands = SHORTHAND_TO_LONGHANDS[key];
414
+ if (longhands) {
415
+ for (const longhand of longhands) {
416
+ if (keys.has(longhand)) {
417
+ const id = `${key}|${longhand}`;
418
+ if (!seen.has(id)) {
419
+ seen.add(id);
420
+ conflicts.push({ shorthand: key, longhand });
421
+ }
422
+ }
423
+ }
424
+ }
425
+ }
426
+ return conflicts;
427
+ }
428
+
429
+ // src/utils/style-ast.ts
430
+ var NESTED_SELECTOR_KEY_RE = /^(&|@|\[)/;
431
+ var COMPONENT_STRUCTURAL_KEYS = /* @__PURE__ */ new Set([
432
+ "base",
433
+ "variants",
434
+ "defaultVariants",
435
+ "compoundVariants",
436
+ "slots"
437
+ ]);
438
+ function getStaticPropertyKey(prop) {
439
+ if (prop.computed) return null;
440
+ if (prop.key.type === "Identifier") return prop.key.name;
441
+ if (prop.key.type === "Literal" && typeof prop.key.value === "string") return prop.key.value;
442
+ return null;
443
+ }
444
+ function isCssPropertyKey(key) {
445
+ if (NESTED_SELECTOR_KEY_RE.test(key)) return false;
446
+ if (COMPONENT_STRUCTURAL_KEYS.has(key)) return false;
447
+ return /^[a-zA-Z]/.test(key);
448
+ }
449
+ function collectCssPropertyNames(obj) {
450
+ const names = [];
451
+ for (const prop of obj.properties) {
452
+ if (prop.type !== "Property") continue;
453
+ const key = getStaticPropertyKey(prop);
454
+ if (key && isCssPropertyKey(key)) names.push(key);
455
+ }
456
+ return names;
457
+ }
458
+ function visitStyleObjectLiterals(node, onStyleObject) {
459
+ onStyleObject(node);
460
+ for (const prop of node.properties) {
461
+ if (prop.type !== "Property") continue;
462
+ const key = getStaticPropertyKey(prop);
463
+ if (!key) continue;
464
+ if (NESTED_SELECTOR_KEY_RE.test(key)) {
465
+ if (prop.value.type === "ObjectExpression") {
466
+ visitStyleObjectLiterals(prop.value, onStyleObject);
467
+ }
468
+ continue;
469
+ }
470
+ if (key === "variants" && prop.value.type === "ObjectExpression") {
471
+ for (const groupProp of prop.value.properties) {
472
+ if (groupProp.type !== "Property" || groupProp.value.type !== "ObjectExpression") continue;
473
+ for (const optionProp of groupProp.value.properties) {
474
+ if (optionProp.type === "Property" && optionProp.value.type === "ObjectExpression") {
475
+ visitStyleObjectLiterals(optionProp.value, onStyleObject);
476
+ }
477
+ }
478
+ }
479
+ continue;
480
+ }
481
+ if (key === "compoundVariants" && prop.value.type === "ArrayExpression") {
482
+ for (const element of prop.value.elements) {
483
+ if (!element || element.type !== "ObjectExpression") continue;
484
+ for (const cvProp of element.properties) {
485
+ if (cvProp.type !== "Property") continue;
486
+ const cvKey = getStaticPropertyKey(cvProp);
487
+ if (cvKey === "css" && cvProp.value.type === "ObjectExpression") {
488
+ visitStyleObjectLiterals(cvProp.value, onStyleObject);
489
+ }
490
+ }
491
+ }
492
+ continue;
493
+ }
494
+ if ((COMPONENT_STRUCTURAL_KEYS.has(key) || prop.value.type === "ObjectExpression") && prop.value.type === "ObjectExpression") {
495
+ visitStyleObjectLiterals(prop.value, onStyleObject);
496
+ }
497
+ }
498
+ }
499
+ function getStringLiteralValue(node) {
500
+ if (node.type === "Literal" && typeof node.value === "string") return node.value;
501
+ return null;
502
+ }
503
+ function getNumericLiteralValue(node) {
504
+ if (node.type === "Literal" && typeof node.value === "number") return node.value;
505
+ return null;
506
+ }
507
+
508
+ // src/rules/no-invalid-unitless-value.ts
509
+ var noInvalidUnitlessValue = createRule({
510
+ name: "no-invalid-unitless-value",
511
+ meta: {
512
+ type: "problem",
513
+ docs: {
514
+ description: "Disallow bare numeric strings on properties that require CSS units (TypeStyles only auto-appends `px` to numbers, not strings)"
515
+ },
516
+ messages: {
517
+ bareNumberString: "`{{property}}` is set to bare number string `{{value}}`, which emits invalid CSS. Use a number (`{{numeric}}`) or a value with units (`{{value}}px`).",
518
+ suspiciousUnitlessNumber: '`{{property}}` is unitless in CSS \u2014 numeric `{{value}}` is emitted without a unit. Use a ratio (e.g. `1.5`) or an explicit length (`"24px"`).'
519
+ },
520
+ schema: [
521
+ {
522
+ type: "object",
523
+ properties: {
524
+ checkSuspiciousUnitlessNumbers: { type: "boolean" }
525
+ },
526
+ additionalProperties: false
527
+ }
528
+ ]
529
+ },
530
+ defaultOptions: [{ checkSuspiciousUnitlessNumbers: true }],
531
+ create(context, [options]) {
532
+ const SUSPICIOUS_UNITLESS_PROPERTIES = /* @__PURE__ */ new Set(["lineHeight"]);
533
+ function checkStyleObject(obj) {
534
+ for (const prop of obj.properties) {
535
+ if (prop.type !== "Property") continue;
536
+ const key = getStaticPropertyKey(prop);
537
+ if (!key || !isCssPropertyKey(key)) continue;
538
+ const stringValue = getStringLiteralValue(prop.value);
539
+ if (stringValue && BARE_NUMBER_STRING_RE.test(stringValue) && !isUnitlessProperty(key)) {
540
+ context.report({
541
+ node: prop.value,
542
+ messageId: "bareNumberString",
543
+ data: {
544
+ property: key,
545
+ value: stringValue,
546
+ numeric: stringValue
547
+ }
548
+ });
549
+ continue;
550
+ }
551
+ if (options.checkSuspiciousUnitlessNumbers && SUSPICIOUS_UNITLESS_PROPERTIES.has(key)) {
552
+ const num = getNumericLiteralValue(prop.value);
553
+ if (num != null && num >= 8 && Number.isInteger(num)) {
554
+ context.report({
555
+ node: prop.value,
556
+ messageId: "suspiciousUnitlessNumber",
557
+ data: { property: key, value: String(num) }
558
+ });
559
+ }
560
+ }
561
+ }
562
+ }
563
+ return {
564
+ CallExpression(node) {
565
+ for (const styleObj of getStyleObjectArguments(node)) {
566
+ visitStyleObjectLiterals(styleObj, checkStyleObject);
567
+ }
568
+ },
569
+ ObjectExpression(node) {
570
+ const parent = node.parent;
571
+ if (parent?.type === "Property" && (parent.key.type === "Identifier" || parent.key.type === "Literal")) {
572
+ const parentKey = parent.key.type === "Identifier" ? parent.key.name : typeof parent.key.value === "string" ? parent.key.value : null;
573
+ if (parentKey === "from" || parentKey === "to" || /^\d+%$/.test(parentKey ?? "")) {
574
+ visitStyleObjectLiterals(node, checkStyleObject);
575
+ }
576
+ }
577
+ }
578
+ };
579
+ }
580
+ });
581
+
582
+ // src/rules/no-shorthand-longhand-conflict.ts
583
+ var noShorthandLonghandConflict = createRule({
584
+ name: "no-shorthand-longhand-conflict",
585
+ meta: {
586
+ type: "problem",
587
+ docs: {
588
+ description: "Disallow mixing CSS shorthand and longhand properties in the same TypeStyles style object"
589
+ },
590
+ messages: {
591
+ conflict: "`{{shorthand}}` and `{{longhand}}` must not appear in the same style object \u2014 the longhand is ignored or overridden unpredictably."
592
+ },
593
+ schema: []
594
+ },
595
+ defaultOptions: [],
596
+ create(context) {
597
+ function checkStyleObject(obj) {
598
+ const propertyNames = collectCssPropertyNames(obj);
599
+ const conflicts = findShorthandLonghandConflicts(propertyNames);
600
+ for (const { shorthand, longhand } of conflicts) {
601
+ context.report({
602
+ node: obj,
603
+ messageId: "conflict",
604
+ data: { shorthand, longhand }
605
+ });
606
+ }
607
+ }
608
+ return {
609
+ CallExpression(node) {
610
+ for (const styleObj of getStyleObjectArguments(node)) {
611
+ visitStyleObjectLiterals(styleObj, checkStyleObject);
612
+ }
613
+ },
614
+ ObjectExpression(node) {
615
+ const parent = node.parent;
616
+ if (parent?.type === "Property" && (parent.key.type === "Identifier" || parent.key.type === "Literal")) {
617
+ const parentKey = parent.key.type === "Identifier" ? parent.key.name : typeof parent.key.value === "string" ? parent.key.value : null;
618
+ if (parentKey === "from" || parentKey === "to" || /^\d+%$/.test(parentKey ?? "")) {
619
+ visitStyleObjectLiterals(node, checkStyleObject);
620
+ }
621
+ }
622
+ }
623
+ };
624
+ }
625
+ });
626
+
627
+ // src/index.ts
628
+ var rules = {
629
+ "no-default-scope-in-package": noDefaultScopeInPackage,
630
+ "no-shorthand-longhand-conflict": noShorthandLonghandConflict,
631
+ "no-invalid-unitless-value": noInvalidUnitlessValue,
632
+ "no-duplicate-namespace": noDuplicateNamespace
633
+ };
634
+ var plugin = {
635
+ meta: {
636
+ name: "@typestyles/eslint-plugin",
637
+ version: "0.1.0"
638
+ },
639
+ rules
640
+ };
641
+ var configs = {
642
+ recommended: {
643
+ plugins: {
644
+ "@typestyles": plugin
645
+ },
646
+ rules: {
647
+ "@typestyles/no-shorthand-longhand-conflict": "error",
648
+ "@typestyles/no-invalid-unitless-value": "error",
649
+ "@typestyles/no-duplicate-namespace": "error"
650
+ }
651
+ },
652
+ /** Preset for publishable packages — adds `no-default-scope-in-package`. */
653
+ package: {
654
+ plugins: {
655
+ "@typestyles": plugin
656
+ },
657
+ rules: {
658
+ "@typestyles/no-shorthand-longhand-conflict": "error",
659
+ "@typestyles/no-invalid-unitless-value": "error",
660
+ "@typestyles/no-duplicate-namespace": "error",
661
+ "@typestyles/no-default-scope-in-package": "error"
662
+ }
663
+ }
664
+ };
665
+ Object.assign(plugin, { configs });
666
+ var src_default = plugin;
667
+
668
+ export { configs, src_default as default, noDefaultScopeInPackage, noDuplicateNamespace, noInvalidUnitlessValue, noShorthandLonghandConflict, rules };
669
+ //# sourceMappingURL=index.js.map
670
+ //# sourceMappingURL=index.js.map