eslint-plugin-motionwind 2.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 ADDED
@@ -0,0 +1,30 @@
1
+ # eslint-plugin-motionwind
2
+
3
+ ESLint rules for [motionwind](https://github.com/piyushzingade/motionwind) animation classes.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -D eslint-plugin-motionwind
9
+ ```
10
+
11
+ ## Usage (flat config)
12
+
13
+ ```js
14
+ // eslint.config.js
15
+ import motionwind from "eslint-plugin-motionwind";
16
+
17
+ export default [motionwind.configs.recommended];
18
+ ```
19
+
20
+ ## Rules
21
+
22
+ | Rule | Description |
23
+ | --------------------------------------- | ------------------------------------------------------------------------ |
24
+ | `motionwind/no-unknown-classes` | Flags `animate-*` classes the parser doesn't recognize. |
25
+ | `motionwind/no-duplicate-gesture-props` | Flags a property set more than once within one gesture/variant. |
26
+ | `motionwind/prefer-mw-for-dynamic` | Flags `animate-*` in a dynamic className on a host element (use `mw.*`). |
27
+ | `motionwind/exit-requires-presence` | Flags `animate-exit:*` used without importing `AnimatePresence`. |
28
+
29
+ All rules are `warn` in the recommended config and reuse motionwind's own parser, so
30
+ they never drift from what the Babel plugin actually compiles.
package/dist/index.cjs ADDED
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ default: () => src_default
24
+ });
25
+ module.exports = __toCommonJS(src_exports);
26
+
27
+ // src/rules/no-unknown-classes.ts
28
+ var import_motionwind_core = require("motionwind-core");
29
+
30
+ // src/utils.ts
31
+ function staticClassName(value) {
32
+ if (!value) return null;
33
+ if (value.type === "Literal" && typeof value.value === "string") {
34
+ return value.value;
35
+ }
36
+ if (value.type === "JSXExpressionContainer") {
37
+ const expr = value.expression;
38
+ if (expr?.type === "Literal" && typeof expr.value === "string") {
39
+ return expr.value;
40
+ }
41
+ if (expr?.type === "TemplateLiteral") {
42
+ return expr.quasis.map((q) => q.value.cooked ?? q.value.raw ?? "").join(" ");
43
+ }
44
+ }
45
+ return null;
46
+ }
47
+ function isDynamicClassName(value) {
48
+ if (!value || value.type !== "JSXExpressionContainer") return false;
49
+ const expr = value.expression;
50
+ if (expr?.type === "Literal" && typeof expr.value === "string") return false;
51
+ return expr?.type !== "JSXEmptyExpression";
52
+ }
53
+ function getClassNameAttr(opening) {
54
+ for (const attr of opening.attributes ?? []) {
55
+ if (attr.type === "JSXAttribute" && attr.name?.name === "className") {
56
+ return attr;
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+ function getTagName(opening) {
62
+ const name = opening.name;
63
+ if (!name) return "";
64
+ if (name.type === "JSXIdentifier") return name.name;
65
+ if (name.type === "JSXMemberExpression") {
66
+ return `${name.object?.name ?? ""}.${name.property?.name ?? ""}`;
67
+ }
68
+ return "";
69
+ }
70
+
71
+ // src/rules/no-unknown-classes.ts
72
+ var rule = {
73
+ meta: {
74
+ type: "problem",
75
+ fixable: "code",
76
+ docs: {
77
+ description: "Disallow animate-* classes that motionwind's parser does not recognize."
78
+ },
79
+ schema: [],
80
+ messages: {
81
+ unknown: 'Unknown motionwind class "{{token}}". It starts with "animate-" but matches no known pattern.'
82
+ }
83
+ },
84
+ create(context) {
85
+ return {
86
+ JSXAttribute(node) {
87
+ if (node.name?.name !== "className") return;
88
+ const cls = staticClassName(node.value);
89
+ if (!cls || !cls.includes("animate-")) return;
90
+ const { unknown } = (0, import_motionwind_core.analyzeClassName)(cls);
91
+ if (unknown.length === 0) return;
92
+ const unknownSet = new Set(unknown);
93
+ const fixed = cls.split(/\s+/).filter((t) => t && !unknownSet.has(t)).join(" ");
94
+ const valueNode = node.value ?? node;
95
+ for (const token of unknown) {
96
+ context.report({
97
+ node: valueNode,
98
+ messageId: "unknown",
99
+ data: { token },
100
+ fix(fixer) {
101
+ if (valueNode.type === "Literal" && typeof valueNode.value === "string") {
102
+ const q = valueNode.raw?.[0] ?? '"';
103
+ return fixer.replaceText(valueNode, `${q}${fixed}${q}`);
104
+ }
105
+ if (valueNode.type === "JSXExpressionContainer" && valueNode.expression?.type === "Literal" && typeof valueNode.expression.value === "string") {
106
+ const litNode = valueNode.expression;
107
+ const q = litNode.raw?.[0] ?? '"';
108
+ return fixer.replaceText(litNode, `${q}${fixed}${q}`);
109
+ }
110
+ return null;
111
+ }
112
+ });
113
+ }
114
+ }
115
+ };
116
+ }
117
+ };
118
+ var no_unknown_classes_default = rule;
119
+
120
+ // src/rules/no-duplicate-gesture-props.ts
121
+ var import_motionwind_core2 = require("motionwind-core");
122
+ var rule2 = {
123
+ meta: {
124
+ type: "problem",
125
+ docs: {
126
+ description: "Disallow setting the same property more than once within a gesture or variant."
127
+ },
128
+ schema: [],
129
+ messages: {
130
+ duplicate: 'Property "{{prop}}" is set more than once in "{{gesture}}". The last value wins.'
131
+ }
132
+ },
133
+ create(context) {
134
+ return {
135
+ JSXAttribute(node) {
136
+ if (node.name?.name !== "className") return;
137
+ const cls = staticClassName(node.value);
138
+ if (!cls || !cls.includes("animate-")) return;
139
+ for (const dup of (0, import_motionwind_core2.analyzeClassName)(cls).duplicates) {
140
+ context.report({
141
+ node: node.value ?? node,
142
+ messageId: "duplicate",
143
+ data: { prop: dup.prop, gesture: dup.gesture }
144
+ });
145
+ }
146
+ }
147
+ };
148
+ }
149
+ };
150
+ var no_duplicate_gesture_props_default = rule2;
151
+
152
+ // src/rules/prefer-mw-for-dynamic.ts
153
+ var rule3 = {
154
+ meta: {
155
+ type: "suggestion",
156
+ docs: {
157
+ description: "Use the mw.* runtime for dynamic classNames that contain animate-* classes (the Babel plugin can only transform static strings)."
158
+ },
159
+ schema: [],
160
+ messages: {
161
+ preferMw: "animate-* classes in a dynamic className on <{{tag}}> won't be compiled by the Babel plugin. Use <mw.{{tag}}> instead."
162
+ }
163
+ },
164
+ create(context) {
165
+ return {
166
+ JSXOpeningElement(node) {
167
+ const attr = getClassNameAttr(node);
168
+ if (!attr || !isDynamicClassName(attr.value)) return;
169
+ const cls = staticClassName(attr.value);
170
+ if (!cls || !cls.includes("animate-")) return;
171
+ const tag = getTagName(node);
172
+ if (!/^[a-z]/.test(tag)) return;
173
+ if (tag.startsWith("mw.") || tag.startsWith("motion.")) return;
174
+ context.report({ node: attr, messageId: "preferMw", data: { tag } });
175
+ }
176
+ };
177
+ }
178
+ };
179
+ var prefer_mw_for_dynamic_default = rule3;
180
+
181
+ // src/rules/exit-requires-presence.ts
182
+ var rule4 = {
183
+ meta: {
184
+ type: "problem",
185
+ docs: {
186
+ description: "Warn when animate-exit:* is used without importing AnimatePresence \u2014 exit animations require it."
187
+ },
188
+ schema: [],
189
+ messages: {
190
+ needsPresence: "animate-exit:* only runs when the element is wrapped in <AnimatePresence>, but no AnimatePresence import was found in this file."
191
+ }
192
+ },
193
+ create(context) {
194
+ let hasPresenceImport = false;
195
+ const exitNodes = [];
196
+ return {
197
+ ImportDeclaration(node) {
198
+ for (const spec of node.specifiers ?? []) {
199
+ if (spec.type === "ImportSpecifier" && spec.imported?.name === "AnimatePresence") {
200
+ hasPresenceImport = true;
201
+ }
202
+ }
203
+ },
204
+ JSXAttribute(node) {
205
+ if (node.name?.name !== "className") return;
206
+ const cls = staticClassName(node.value);
207
+ if (cls && cls.includes("animate-exit:")) {
208
+ exitNodes.push(node.value ?? node);
209
+ }
210
+ },
211
+ "Program:exit"() {
212
+ if (hasPresenceImport) return;
213
+ for (const n of exitNodes) {
214
+ context.report({ node: n, messageId: "needsPresence" });
215
+ }
216
+ }
217
+ };
218
+ }
219
+ };
220
+ var exit_requires_presence_default = rule4;
221
+
222
+ // src/index.ts
223
+ var rules = {
224
+ "no-unknown-classes": no_unknown_classes_default,
225
+ "no-duplicate-gesture-props": no_duplicate_gesture_props_default,
226
+ "prefer-mw-for-dynamic": prefer_mw_for_dynamic_default,
227
+ "exit-requires-presence": exit_requires_presence_default
228
+ };
229
+ var plugin = {
230
+ meta: { name: "eslint-plugin-motionwind", version: "2.0.0" },
231
+ rules,
232
+ configs: {}
233
+ };
234
+ plugin.configs.recommended = {
235
+ plugins: { motionwind: plugin },
236
+ rules: {
237
+ "motionwind/no-unknown-classes": "warn",
238
+ "motionwind/no-duplicate-gesture-props": "warn",
239
+ "motionwind/prefer-mw-for-dynamic": "warn",
240
+ "motionwind/exit-requires-presence": "warn"
241
+ }
242
+ };
243
+ var src_default = plugin;
244
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/rules/no-unknown-classes.ts","../src/utils.ts","../src/rules/no-duplicate-gesture-props.ts","../src/rules/prefer-mw-for-dynamic.ts","../src/rules/exit-requires-presence.ts"],"sourcesContent":["import type { ESLint, Linter, Rule } from \"eslint\";\nimport noUnknownClasses from \"./rules/no-unknown-classes.js\";\nimport noDuplicateGestureProps from \"./rules/no-duplicate-gesture-props.js\";\nimport preferMwForDynamic from \"./rules/prefer-mw-for-dynamic.js\";\nimport exitRequiresPresence from \"./rules/exit-requires-presence.js\";\n\nconst rules: Record<string, Rule.RuleModule> = {\n \"no-unknown-classes\": noUnknownClasses,\n \"no-duplicate-gesture-props\": noDuplicateGestureProps,\n \"prefer-mw-for-dynamic\": preferMwForDynamic,\n \"exit-requires-presence\": exitRequiresPresence,\n};\n\nconst plugin: ESLint.Plugin & {\n configs: Record<string, Linter.Config>;\n} = {\n meta: { name: \"eslint-plugin-motionwind\", version: \"2.0.0\" },\n rules,\n configs: {},\n};\n\n// Flat config preset: `...motionwind.configs.recommended`\nplugin.configs.recommended = {\n plugins: { motionwind: plugin as ESLint.Plugin },\n rules: {\n \"motionwind/no-unknown-classes\": \"warn\",\n \"motionwind/no-duplicate-gesture-props\": \"warn\",\n \"motionwind/prefer-mw-for-dynamic\": \"warn\",\n \"motionwind/exit-requires-presence\": \"warn\",\n },\n};\n\nexport default plugin;\n","import type { Rule } from \"eslint\";\nimport { analyzeClassName } from \"motionwind-core\";\nimport { staticClassName } from \"../utils.js\";\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n fixable: \"code\",\n docs: {\n description:\n \"Disallow animate-* classes that motionwind's parser does not recognize.\",\n },\n schema: [],\n messages: {\n unknown:\n 'Unknown motionwind class \"{{token}}\". It starts with \"animate-\" but matches no known pattern.',\n },\n },\n create(context) {\n return {\n JSXAttribute(node: any) {\n if (node.name?.name !== \"className\") return;\n const cls = staticClassName(node.value);\n if (!cls || !cls.includes(\"animate-\")) return;\n const { unknown } = analyzeClassName(cls);\n if (unknown.length === 0) return;\n\n const unknownSet = new Set(unknown);\n const fixed = cls\n .split(/\\s+/)\n .filter((t: string) => t && !unknownSet.has(t))\n .join(\" \");\n\n const valueNode = node.value ?? node;\n\n for (const token of unknown) {\n context.report({\n node: valueNode,\n messageId: \"unknown\",\n data: { token },\n fix(fixer) {\n // Plain string literal: className=\"...\"\n if (\n valueNode.type === \"Literal\" &&\n typeof valueNode.value === \"string\"\n ) {\n const q = (valueNode.raw as string)?.[0] ?? '\"';\n return fixer.replaceText(valueNode, `${q}${fixed}${q}`);\n }\n // JSX expression container wrapping a string literal: className={\"...\"}\n if (\n valueNode.type === \"JSXExpressionContainer\" &&\n valueNode.expression?.type === \"Literal\" &&\n typeof valueNode.expression.value === \"string\"\n ) {\n const litNode = valueNode.expression;\n const q = (litNode.raw as string)?.[0] ?? '\"';\n return fixer.replaceText(litNode, `${q}${fixed}${q}`);\n }\n return null;\n },\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Extract the static className string from a JSXAttribute value node.\n * Handles string literals and the static parts of template literals.\n * Returns null when there is no analyzable static string.\n */\nexport function staticClassName(value: any): string | null {\n if (!value) return null;\n if (value.type === \"Literal\" && typeof value.value === \"string\") {\n return value.value;\n }\n if (value.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n if (expr?.type === \"Literal\" && typeof expr.value === \"string\") {\n return expr.value;\n }\n if (expr?.type === \"TemplateLiteral\") {\n return expr.quasis\n .map((q: any) => q.value.cooked ?? q.value.raw ?? \"\")\n .join(\" \");\n }\n }\n return null;\n}\n\n/** Whether the className value is a dynamic expression (not a plain string). */\nexport function isDynamicClassName(value: any): boolean {\n if (!value || value.type !== \"JSXExpressionContainer\") return false;\n const expr = value.expression;\n if (expr?.type === \"Literal\" && typeof expr.value === \"string\") return false;\n return expr?.type !== \"JSXEmptyExpression\";\n}\n\n/** Find the className attribute on a JSXOpeningElement, if present. */\nexport function getClassNameAttr(opening: any): any | null {\n for (const attr of opening.attributes ?? []) {\n if (attr.type === \"JSXAttribute\" && attr.name?.name === \"className\") {\n return attr;\n }\n }\n return null;\n}\n\n/** Resolve the tag name of a JSXOpeningElement (e.g. \"div\", \"Card\", \"mw.div\"). */\nexport function getTagName(opening: any): string {\n const name = opening.name;\n if (!name) return \"\";\n if (name.type === \"JSXIdentifier\") return name.name;\n if (name.type === \"JSXMemberExpression\") {\n return `${name.object?.name ?? \"\"}.${name.property?.name ?? \"\"}`;\n }\n return \"\";\n}\n","import type { Rule } from \"eslint\";\nimport { analyzeClassName } from \"motionwind-core\";\nimport { staticClassName } from \"../utils.js\";\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow setting the same property more than once within a gesture or variant.\",\n },\n schema: [],\n messages: {\n duplicate:\n 'Property \"{{prop}}\" is set more than once in \"{{gesture}}\". The last value wins.',\n },\n },\n create(context) {\n return {\n JSXAttribute(node: any) {\n if (node.name?.name !== \"className\") return;\n const cls = staticClassName(node.value);\n if (!cls || !cls.includes(\"animate-\")) return;\n for (const dup of analyzeClassName(cls).duplicates) {\n context.report({\n node: node.value ?? node,\n messageId: \"duplicate\",\n data: { prop: dup.prop, gesture: dup.gesture },\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from \"eslint\";\nimport {\n getClassNameAttr,\n getTagName,\n isDynamicClassName,\n staticClassName,\n} from \"../utils.js\";\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Use the mw.* runtime for dynamic classNames that contain animate-* classes (the Babel plugin can only transform static strings).\",\n },\n schema: [],\n messages: {\n preferMw:\n \"animate-* classes in a dynamic className on <{{tag}}> won't be compiled by the Babel plugin. Use <mw.{{tag}}> instead.\",\n },\n },\n create(context) {\n return {\n JSXOpeningElement(node: any) {\n const attr = getClassNameAttr(node);\n if (!attr || !isDynamicClassName(attr.value)) return;\n const cls = staticClassName(attr.value);\n if (!cls || !cls.includes(\"animate-\")) return;\n const tag = getTagName(node);\n // Only host elements — components and mw.*/motion.* are already fine.\n if (!/^[a-z]/.test(tag)) return;\n if (tag.startsWith(\"mw.\") || tag.startsWith(\"motion.\")) return;\n context.report({ node: attr, messageId: \"preferMw\", data: { tag } });\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from \"eslint\";\nimport { staticClassName } from \"../utils.js\";\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Warn when animate-exit:* is used without importing AnimatePresence — exit animations require it.\",\n },\n schema: [],\n messages: {\n needsPresence:\n \"animate-exit:* only runs when the element is wrapped in <AnimatePresence>, but no AnimatePresence import was found in this file.\",\n },\n },\n create(context) {\n let hasPresenceImport = false;\n const exitNodes: any[] = [];\n\n return {\n ImportDeclaration(node: any) {\n for (const spec of node.specifiers ?? []) {\n if (\n spec.type === \"ImportSpecifier\" &&\n spec.imported?.name === \"AnimatePresence\"\n ) {\n hasPresenceImport = true;\n }\n }\n },\n JSXAttribute(node: any) {\n if (node.name?.name !== \"className\") return;\n const cls = staticClassName(node.value);\n if (cls && cls.includes(\"animate-exit:\")) {\n exitNodes.push(node.value ?? node);\n }\n },\n \"Program:exit\"() {\n if (hasPresenceImport) return;\n for (const n of exitNodes) {\n context.report({ node: n, messageId: \"needsPresence\" });\n }\n },\n };\n },\n};\n\nexport default rule;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,6BAAiC;;;ACI1B,SAAS,gBAAgB,OAA2B;AACzD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,SAAS,aAAa,OAAO,MAAM,UAAU,UAAU;AAC/D,WAAO,MAAM;AAAA,EACf;AACA,MAAI,MAAM,SAAS,0BAA0B;AAC3C,UAAM,OAAO,MAAM;AACnB,QAAI,MAAM,SAAS,aAAa,OAAO,KAAK,UAAU,UAAU;AAC9D,aAAO,KAAK;AAAA,IACd;AACA,QAAI,MAAM,SAAS,mBAAmB;AACpC,aAAO,KAAK,OACT,IAAI,CAAC,MAAW,EAAE,MAAM,UAAU,EAAE,MAAM,OAAO,EAAE,EACnD,KAAK,GAAG;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,mBAAmB,OAAqB;AACtD,MAAI,CAAC,SAAS,MAAM,SAAS,yBAA0B,QAAO;AAC9D,QAAM,OAAO,MAAM;AACnB,MAAI,MAAM,SAAS,aAAa,OAAO,KAAK,UAAU,SAAU,QAAO;AACvE,SAAO,MAAM,SAAS;AACxB;AAGO,SAAS,iBAAiB,SAA0B;AACzD,aAAW,QAAQ,QAAQ,cAAc,CAAC,GAAG;AAC3C,QAAI,KAAK,SAAS,kBAAkB,KAAK,MAAM,SAAS,aAAa;AACnE,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,WAAW,SAAsB;AAC/C,QAAM,OAAO,QAAQ;AACrB,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,SAAS,gBAAiB,QAAO,KAAK;AAC/C,MAAI,KAAK,SAAS,uBAAuB;AACvC,WAAO,GAAG,KAAK,QAAQ,QAAQ,EAAE,IAAI,KAAK,UAAU,QAAQ,EAAE;AAAA,EAChE;AACA,SAAO;AACT;;;AD/CA,IAAM,OAAwB;AAAA,EAC5B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,SACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,OAAO,SAAS;AACd,WAAO;AAAA,MACL,aAAa,MAAW;AACtB,YAAI,KAAK,MAAM,SAAS,YAAa;AACrC,cAAM,MAAM,gBAAgB,KAAK,KAAK;AACtC,YAAI,CAAC,OAAO,CAAC,IAAI,SAAS,UAAU,EAAG;AACvC,cAAM,EAAE,QAAQ,QAAI,yCAAiB,GAAG;AACxC,YAAI,QAAQ,WAAW,EAAG;AAE1B,cAAM,aAAa,IAAI,IAAI,OAAO;AAClC,cAAM,QAAQ,IACX,MAAM,KAAK,EACX,OAAO,CAAC,MAAc,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC,EAC7C,KAAK,GAAG;AAEX,cAAM,YAAY,KAAK,SAAS;AAEhC,mBAAW,SAAS,SAAS;AAC3B,kBAAQ,OAAO;AAAA,YACb,MAAM;AAAA,YACN,WAAW;AAAA,YACX,MAAM,EAAE,MAAM;AAAA,YACd,IAAI,OAAO;AAET,kBACE,UAAU,SAAS,aACnB,OAAO,UAAU,UAAU,UAC3B;AACA,sBAAM,IAAK,UAAU,MAAiB,CAAC,KAAK;AAC5C,uBAAO,MAAM,YAAY,WAAW,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE;AAAA,cACxD;AAEA,kBACE,UAAU,SAAS,4BACnB,UAAU,YAAY,SAAS,aAC/B,OAAO,UAAU,WAAW,UAAU,UACtC;AACA,sBAAM,UAAU,UAAU;AAC1B,sBAAM,IAAK,QAAQ,MAAiB,CAAC,KAAK;AAC1C,uBAAO,MAAM,YAAY,SAAS,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE;AAAA,cACtD;AACA,qBAAO;AAAA,YACT;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,6BAAQ;;;AEnEf,IAAAA,0BAAiC;AAGjC,IAAMC,QAAwB;AAAA,EAC5B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,WACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,OAAO,SAAS;AACd,WAAO;AAAA,MACL,aAAa,MAAW;AACtB,YAAI,KAAK,MAAM,SAAS,YAAa;AACrC,cAAM,MAAM,gBAAgB,KAAK,KAAK;AACtC,YAAI,CAAC,OAAO,CAAC,IAAI,SAAS,UAAU,EAAG;AACvC,mBAAW,WAAO,0CAAiB,GAAG,EAAE,YAAY;AAClD,kBAAQ,OAAO;AAAA,YACb,MAAM,KAAK,SAAS;AAAA,YACpB,WAAW;AAAA,YACX,MAAM,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,QAAQ;AAAA,UAC/C,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,qCAAQA;;;AC3Bf,IAAMC,QAAwB;AAAA,EAC5B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,UACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,OAAO,SAAS;AACd,WAAO;AAAA,MACL,kBAAkB,MAAW;AAC3B,cAAM,OAAO,iBAAiB,IAAI;AAClC,YAAI,CAAC,QAAQ,CAAC,mBAAmB,KAAK,KAAK,EAAG;AAC9C,cAAM,MAAM,gBAAgB,KAAK,KAAK;AACtC,YAAI,CAAC,OAAO,CAAC,IAAI,SAAS,UAAU,EAAG;AACvC,cAAM,MAAM,WAAW,IAAI;AAE3B,YAAI,CAAC,SAAS,KAAK,GAAG,EAAG;AACzB,YAAI,IAAI,WAAW,KAAK,KAAK,IAAI,WAAW,SAAS,EAAG;AACxD,gBAAQ,OAAO,EAAE,MAAM,MAAM,WAAW,YAAY,MAAM,EAAE,IAAI,EAAE,CAAC;AAAA,MACrE;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gCAAQA;;;ACnCf,IAAMC,QAAwB;AAAA,EAC5B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,eACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,OAAO,SAAS;AACd,QAAI,oBAAoB;AACxB,UAAM,YAAmB,CAAC;AAE1B,WAAO;AAAA,MACL,kBAAkB,MAAW;AAC3B,mBAAW,QAAQ,KAAK,cAAc,CAAC,GAAG;AACxC,cACE,KAAK,SAAS,qBACd,KAAK,UAAU,SAAS,mBACxB;AACA,gCAAoB;AAAA,UACtB;AAAA,QACF;AAAA,MACF;AAAA,MACA,aAAa,MAAW;AACtB,YAAI,KAAK,MAAM,SAAS,YAAa;AACrC,cAAM,MAAM,gBAAgB,KAAK,KAAK;AACtC,YAAI,OAAO,IAAI,SAAS,eAAe,GAAG;AACxC,oBAAU,KAAK,KAAK,SAAS,IAAI;AAAA,QACnC;AAAA,MACF;AAAA,MACA,iBAAiB;AACf,YAAI,kBAAmB;AACvB,mBAAW,KAAK,WAAW;AACzB,kBAAQ,OAAO,EAAE,MAAM,GAAG,WAAW,gBAAgB,CAAC;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,iCAAQA;;;AL1Cf,IAAM,QAAyC;AAAA,EAC7C,sBAAsB;AAAA,EACtB,8BAA8B;AAAA,EAC9B,yBAAyB;AAAA,EACzB,0BAA0B;AAC5B;AAEA,IAAM,SAEF;AAAA,EACF,MAAM,EAAE,MAAM,4BAA4B,SAAS,QAAQ;AAAA,EAC3D;AAAA,EACA,SAAS,CAAC;AACZ;AAGA,OAAO,QAAQ,cAAc;AAAA,EAC3B,SAAS,EAAE,YAAY,OAAwB;AAAA,EAC/C,OAAO;AAAA,IACL,iCAAiC;AAAA,IACjC,yCAAyC;AAAA,IACzC,oCAAoC;AAAA,IACpC,qCAAqC;AAAA,EACvC;AACF;AAEA,IAAO,cAAQ;","names":["import_motionwind_core","rule","rule","rule"]}
@@ -0,0 +1,7 @@
1
+ import { ESLint, Linter } from 'eslint';
2
+
3
+ declare const plugin: ESLint.Plugin & {
4
+ configs: Record<string, Linter.Config>;
5
+ };
6
+
7
+ export = plugin;
@@ -0,0 +1,7 @@
1
+ import { ESLint, Linter } from 'eslint';
2
+
3
+ declare const plugin: ESLint.Plugin & {
4
+ configs: Record<string, Linter.Config>;
5
+ };
6
+
7
+ export { plugin as default };
package/dist/index.js ADDED
@@ -0,0 +1,221 @@
1
+ // src/rules/no-unknown-classes.ts
2
+ import { analyzeClassName } from "motionwind-core";
3
+
4
+ // src/utils.ts
5
+ function staticClassName(value) {
6
+ if (!value) return null;
7
+ if (value.type === "Literal" && typeof value.value === "string") {
8
+ return value.value;
9
+ }
10
+ if (value.type === "JSXExpressionContainer") {
11
+ const expr = value.expression;
12
+ if (expr?.type === "Literal" && typeof expr.value === "string") {
13
+ return expr.value;
14
+ }
15
+ if (expr?.type === "TemplateLiteral") {
16
+ return expr.quasis.map((q) => q.value.cooked ?? q.value.raw ?? "").join(" ");
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+ function isDynamicClassName(value) {
22
+ if (!value || value.type !== "JSXExpressionContainer") return false;
23
+ const expr = value.expression;
24
+ if (expr?.type === "Literal" && typeof expr.value === "string") return false;
25
+ return expr?.type !== "JSXEmptyExpression";
26
+ }
27
+ function getClassNameAttr(opening) {
28
+ for (const attr of opening.attributes ?? []) {
29
+ if (attr.type === "JSXAttribute" && attr.name?.name === "className") {
30
+ return attr;
31
+ }
32
+ }
33
+ return null;
34
+ }
35
+ function getTagName(opening) {
36
+ const name = opening.name;
37
+ if (!name) return "";
38
+ if (name.type === "JSXIdentifier") return name.name;
39
+ if (name.type === "JSXMemberExpression") {
40
+ return `${name.object?.name ?? ""}.${name.property?.name ?? ""}`;
41
+ }
42
+ return "";
43
+ }
44
+
45
+ // src/rules/no-unknown-classes.ts
46
+ var rule = {
47
+ meta: {
48
+ type: "problem",
49
+ fixable: "code",
50
+ docs: {
51
+ description: "Disallow animate-* classes that motionwind's parser does not recognize."
52
+ },
53
+ schema: [],
54
+ messages: {
55
+ unknown: 'Unknown motionwind class "{{token}}". It starts with "animate-" but matches no known pattern.'
56
+ }
57
+ },
58
+ create(context) {
59
+ return {
60
+ JSXAttribute(node) {
61
+ if (node.name?.name !== "className") return;
62
+ const cls = staticClassName(node.value);
63
+ if (!cls || !cls.includes("animate-")) return;
64
+ const { unknown } = analyzeClassName(cls);
65
+ if (unknown.length === 0) return;
66
+ const unknownSet = new Set(unknown);
67
+ const fixed = cls.split(/\s+/).filter((t) => t && !unknownSet.has(t)).join(" ");
68
+ const valueNode = node.value ?? node;
69
+ for (const token of unknown) {
70
+ context.report({
71
+ node: valueNode,
72
+ messageId: "unknown",
73
+ data: { token },
74
+ fix(fixer) {
75
+ if (valueNode.type === "Literal" && typeof valueNode.value === "string") {
76
+ const q = valueNode.raw?.[0] ?? '"';
77
+ return fixer.replaceText(valueNode, `${q}${fixed}${q}`);
78
+ }
79
+ if (valueNode.type === "JSXExpressionContainer" && valueNode.expression?.type === "Literal" && typeof valueNode.expression.value === "string") {
80
+ const litNode = valueNode.expression;
81
+ const q = litNode.raw?.[0] ?? '"';
82
+ return fixer.replaceText(litNode, `${q}${fixed}${q}`);
83
+ }
84
+ return null;
85
+ }
86
+ });
87
+ }
88
+ }
89
+ };
90
+ }
91
+ };
92
+ var no_unknown_classes_default = rule;
93
+
94
+ // src/rules/no-duplicate-gesture-props.ts
95
+ import { analyzeClassName as analyzeClassName2 } from "motionwind-core";
96
+ var rule2 = {
97
+ meta: {
98
+ type: "problem",
99
+ docs: {
100
+ description: "Disallow setting the same property more than once within a gesture or variant."
101
+ },
102
+ schema: [],
103
+ messages: {
104
+ duplicate: 'Property "{{prop}}" is set more than once in "{{gesture}}". The last value wins.'
105
+ }
106
+ },
107
+ create(context) {
108
+ return {
109
+ JSXAttribute(node) {
110
+ if (node.name?.name !== "className") return;
111
+ const cls = staticClassName(node.value);
112
+ if (!cls || !cls.includes("animate-")) return;
113
+ for (const dup of analyzeClassName2(cls).duplicates) {
114
+ context.report({
115
+ node: node.value ?? node,
116
+ messageId: "duplicate",
117
+ data: { prop: dup.prop, gesture: dup.gesture }
118
+ });
119
+ }
120
+ }
121
+ };
122
+ }
123
+ };
124
+ var no_duplicate_gesture_props_default = rule2;
125
+
126
+ // src/rules/prefer-mw-for-dynamic.ts
127
+ var rule3 = {
128
+ meta: {
129
+ type: "suggestion",
130
+ docs: {
131
+ description: "Use the mw.* runtime for dynamic classNames that contain animate-* classes (the Babel plugin can only transform static strings)."
132
+ },
133
+ schema: [],
134
+ messages: {
135
+ preferMw: "animate-* classes in a dynamic className on <{{tag}}> won't be compiled by the Babel plugin. Use <mw.{{tag}}> instead."
136
+ }
137
+ },
138
+ create(context) {
139
+ return {
140
+ JSXOpeningElement(node) {
141
+ const attr = getClassNameAttr(node);
142
+ if (!attr || !isDynamicClassName(attr.value)) return;
143
+ const cls = staticClassName(attr.value);
144
+ if (!cls || !cls.includes("animate-")) return;
145
+ const tag = getTagName(node);
146
+ if (!/^[a-z]/.test(tag)) return;
147
+ if (tag.startsWith("mw.") || tag.startsWith("motion.")) return;
148
+ context.report({ node: attr, messageId: "preferMw", data: { tag } });
149
+ }
150
+ };
151
+ }
152
+ };
153
+ var prefer_mw_for_dynamic_default = rule3;
154
+
155
+ // src/rules/exit-requires-presence.ts
156
+ var rule4 = {
157
+ meta: {
158
+ type: "problem",
159
+ docs: {
160
+ description: "Warn when animate-exit:* is used without importing AnimatePresence \u2014 exit animations require it."
161
+ },
162
+ schema: [],
163
+ messages: {
164
+ needsPresence: "animate-exit:* only runs when the element is wrapped in <AnimatePresence>, but no AnimatePresence import was found in this file."
165
+ }
166
+ },
167
+ create(context) {
168
+ let hasPresenceImport = false;
169
+ const exitNodes = [];
170
+ return {
171
+ ImportDeclaration(node) {
172
+ for (const spec of node.specifiers ?? []) {
173
+ if (spec.type === "ImportSpecifier" && spec.imported?.name === "AnimatePresence") {
174
+ hasPresenceImport = true;
175
+ }
176
+ }
177
+ },
178
+ JSXAttribute(node) {
179
+ if (node.name?.name !== "className") return;
180
+ const cls = staticClassName(node.value);
181
+ if (cls && cls.includes("animate-exit:")) {
182
+ exitNodes.push(node.value ?? node);
183
+ }
184
+ },
185
+ "Program:exit"() {
186
+ if (hasPresenceImport) return;
187
+ for (const n of exitNodes) {
188
+ context.report({ node: n, messageId: "needsPresence" });
189
+ }
190
+ }
191
+ };
192
+ }
193
+ };
194
+ var exit_requires_presence_default = rule4;
195
+
196
+ // src/index.ts
197
+ var rules = {
198
+ "no-unknown-classes": no_unknown_classes_default,
199
+ "no-duplicate-gesture-props": no_duplicate_gesture_props_default,
200
+ "prefer-mw-for-dynamic": prefer_mw_for_dynamic_default,
201
+ "exit-requires-presence": exit_requires_presence_default
202
+ };
203
+ var plugin = {
204
+ meta: { name: "eslint-plugin-motionwind", version: "2.0.0" },
205
+ rules,
206
+ configs: {}
207
+ };
208
+ plugin.configs.recommended = {
209
+ plugins: { motionwind: plugin },
210
+ rules: {
211
+ "motionwind/no-unknown-classes": "warn",
212
+ "motionwind/no-duplicate-gesture-props": "warn",
213
+ "motionwind/prefer-mw-for-dynamic": "warn",
214
+ "motionwind/exit-requires-presence": "warn"
215
+ }
216
+ };
217
+ var src_default = plugin;
218
+ export {
219
+ src_default as default
220
+ };
221
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rules/no-unknown-classes.ts","../src/utils.ts","../src/rules/no-duplicate-gesture-props.ts","../src/rules/prefer-mw-for-dynamic.ts","../src/rules/exit-requires-presence.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from \"eslint\";\nimport { analyzeClassName } from \"motionwind-core\";\nimport { staticClassName } from \"../utils.js\";\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n fixable: \"code\",\n docs: {\n description:\n \"Disallow animate-* classes that motionwind's parser does not recognize.\",\n },\n schema: [],\n messages: {\n unknown:\n 'Unknown motionwind class \"{{token}}\". It starts with \"animate-\" but matches no known pattern.',\n },\n },\n create(context) {\n return {\n JSXAttribute(node: any) {\n if (node.name?.name !== \"className\") return;\n const cls = staticClassName(node.value);\n if (!cls || !cls.includes(\"animate-\")) return;\n const { unknown } = analyzeClassName(cls);\n if (unknown.length === 0) return;\n\n const unknownSet = new Set(unknown);\n const fixed = cls\n .split(/\\s+/)\n .filter((t: string) => t && !unknownSet.has(t))\n .join(\" \");\n\n const valueNode = node.value ?? node;\n\n for (const token of unknown) {\n context.report({\n node: valueNode,\n messageId: \"unknown\",\n data: { token },\n fix(fixer) {\n // Plain string literal: className=\"...\"\n if (\n valueNode.type === \"Literal\" &&\n typeof valueNode.value === \"string\"\n ) {\n const q = (valueNode.raw as string)?.[0] ?? '\"';\n return fixer.replaceText(valueNode, `${q}${fixed}${q}`);\n }\n // JSX expression container wrapping a string literal: className={\"...\"}\n if (\n valueNode.type === \"JSXExpressionContainer\" &&\n valueNode.expression?.type === \"Literal\" &&\n typeof valueNode.expression.value === \"string\"\n ) {\n const litNode = valueNode.expression;\n const q = (litNode.raw as string)?.[0] ?? '\"';\n return fixer.replaceText(litNode, `${q}${fixed}${q}`);\n }\n return null;\n },\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Extract the static className string from a JSXAttribute value node.\n * Handles string literals and the static parts of template literals.\n * Returns null when there is no analyzable static string.\n */\nexport function staticClassName(value: any): string | null {\n if (!value) return null;\n if (value.type === \"Literal\" && typeof value.value === \"string\") {\n return value.value;\n }\n if (value.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n if (expr?.type === \"Literal\" && typeof expr.value === \"string\") {\n return expr.value;\n }\n if (expr?.type === \"TemplateLiteral\") {\n return expr.quasis\n .map((q: any) => q.value.cooked ?? q.value.raw ?? \"\")\n .join(\" \");\n }\n }\n return null;\n}\n\n/** Whether the className value is a dynamic expression (not a plain string). */\nexport function isDynamicClassName(value: any): boolean {\n if (!value || value.type !== \"JSXExpressionContainer\") return false;\n const expr = value.expression;\n if (expr?.type === \"Literal\" && typeof expr.value === \"string\") return false;\n return expr?.type !== \"JSXEmptyExpression\";\n}\n\n/** Find the className attribute on a JSXOpeningElement, if present. */\nexport function getClassNameAttr(opening: any): any | null {\n for (const attr of opening.attributes ?? []) {\n if (attr.type === \"JSXAttribute\" && attr.name?.name === \"className\") {\n return attr;\n }\n }\n return null;\n}\n\n/** Resolve the tag name of a JSXOpeningElement (e.g. \"div\", \"Card\", \"mw.div\"). */\nexport function getTagName(opening: any): string {\n const name = opening.name;\n if (!name) return \"\";\n if (name.type === \"JSXIdentifier\") return name.name;\n if (name.type === \"JSXMemberExpression\") {\n return `${name.object?.name ?? \"\"}.${name.property?.name ?? \"\"}`;\n }\n return \"\";\n}\n","import type { Rule } from \"eslint\";\nimport { analyzeClassName } from \"motionwind-core\";\nimport { staticClassName } from \"../utils.js\";\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow setting the same property more than once within a gesture or variant.\",\n },\n schema: [],\n messages: {\n duplicate:\n 'Property \"{{prop}}\" is set more than once in \"{{gesture}}\". The last value wins.',\n },\n },\n create(context) {\n return {\n JSXAttribute(node: any) {\n if (node.name?.name !== \"className\") return;\n const cls = staticClassName(node.value);\n if (!cls || !cls.includes(\"animate-\")) return;\n for (const dup of analyzeClassName(cls).duplicates) {\n context.report({\n node: node.value ?? node,\n messageId: \"duplicate\",\n data: { prop: dup.prop, gesture: dup.gesture },\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from \"eslint\";\nimport {\n getClassNameAttr,\n getTagName,\n isDynamicClassName,\n staticClassName,\n} from \"../utils.js\";\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Use the mw.* runtime for dynamic classNames that contain animate-* classes (the Babel plugin can only transform static strings).\",\n },\n schema: [],\n messages: {\n preferMw:\n \"animate-* classes in a dynamic className on <{{tag}}> won't be compiled by the Babel plugin. Use <mw.{{tag}}> instead.\",\n },\n },\n create(context) {\n return {\n JSXOpeningElement(node: any) {\n const attr = getClassNameAttr(node);\n if (!attr || !isDynamicClassName(attr.value)) return;\n const cls = staticClassName(attr.value);\n if (!cls || !cls.includes(\"animate-\")) return;\n const tag = getTagName(node);\n // Only host elements — components and mw.*/motion.* are already fine.\n if (!/^[a-z]/.test(tag)) return;\n if (tag.startsWith(\"mw.\") || tag.startsWith(\"motion.\")) return;\n context.report({ node: attr, messageId: \"preferMw\", data: { tag } });\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from \"eslint\";\nimport { staticClassName } from \"../utils.js\";\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Warn when animate-exit:* is used without importing AnimatePresence — exit animations require it.\",\n },\n schema: [],\n messages: {\n needsPresence:\n \"animate-exit:* only runs when the element is wrapped in <AnimatePresence>, but no AnimatePresence import was found in this file.\",\n },\n },\n create(context) {\n let hasPresenceImport = false;\n const exitNodes: any[] = [];\n\n return {\n ImportDeclaration(node: any) {\n for (const spec of node.specifiers ?? []) {\n if (\n spec.type === \"ImportSpecifier\" &&\n spec.imported?.name === \"AnimatePresence\"\n ) {\n hasPresenceImport = true;\n }\n }\n },\n JSXAttribute(node: any) {\n if (node.name?.name !== \"className\") return;\n const cls = staticClassName(node.value);\n if (cls && cls.includes(\"animate-exit:\")) {\n exitNodes.push(node.value ?? node);\n }\n },\n \"Program:exit\"() {\n if (hasPresenceImport) return;\n for (const n of exitNodes) {\n context.report({ node: n, messageId: \"needsPresence\" });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { ESLint, Linter, Rule } from \"eslint\";\nimport noUnknownClasses from \"./rules/no-unknown-classes.js\";\nimport noDuplicateGestureProps from \"./rules/no-duplicate-gesture-props.js\";\nimport preferMwForDynamic from \"./rules/prefer-mw-for-dynamic.js\";\nimport exitRequiresPresence from \"./rules/exit-requires-presence.js\";\n\nconst rules: Record<string, Rule.RuleModule> = {\n \"no-unknown-classes\": noUnknownClasses,\n \"no-duplicate-gesture-props\": noDuplicateGestureProps,\n \"prefer-mw-for-dynamic\": preferMwForDynamic,\n \"exit-requires-presence\": exitRequiresPresence,\n};\n\nconst plugin: ESLint.Plugin & {\n configs: Record<string, Linter.Config>;\n} = {\n meta: { name: \"eslint-plugin-motionwind\", version: \"2.0.0\" },\n rules,\n configs: {},\n};\n\n// Flat config preset: `...motionwind.configs.recommended`\nplugin.configs.recommended = {\n plugins: { motionwind: plugin as ESLint.Plugin },\n rules: {\n \"motionwind/no-unknown-classes\": \"warn\",\n \"motionwind/no-duplicate-gesture-props\": \"warn\",\n \"motionwind/prefer-mw-for-dynamic\": \"warn\",\n \"motionwind/exit-requires-presence\": \"warn\",\n },\n};\n\nexport default plugin;\n"],"mappings":"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","names":["analyzeClassName","rule","analyzeClassName","rule","rule"]}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "eslint-plugin-motionwind",
3
+ "version": "2.1.0",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "description": "ESLint rules for motionwind animation classes — catch unknown classes, duplicate props, and dynamic-className pitfalls.",
7
+ "author": "piyush555",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/piyushzingade/motionwind.git",
12
+ "directory": "packages/eslint-plugin-motionwind"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/piyushzingade/motionwind/issues"
16
+ },
17
+ "homepage": "https://github.com/piyushzingade/motionwind#readme",
18
+ "keywords": [
19
+ "eslint",
20
+ "eslintplugin",
21
+ "eslint-plugin",
22
+ "motionwind",
23
+ "motion",
24
+ "animation",
25
+ "tailwind"
26
+ ],
27
+ "exports": {
28
+ ".": {
29
+ "import": "./dist/index.js",
30
+ "require": "./dist/index.cjs"
31
+ }
32
+ },
33
+ "main": "./dist/index.cjs",
34
+ "module": "./dist/index.js",
35
+ "types": "./dist/index.d.ts",
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "dev": "tsup --watch",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest",
44
+ "lint": "eslint --max-warnings 0",
45
+ "check-types": "tsc --noEmit"
46
+ },
47
+ "peerDependencies": {
48
+ "eslint": "^8.57.0 || ^9.0.0"
49
+ },
50
+ "dependencies": {
51
+ "motionwind-core": "^2.1.0"
52
+ },
53
+ "devDependencies": {
54
+ "@repo/eslint-config": "*",
55
+ "@repo/typescript-config": "*",
56
+ "@types/eslint": "^9.6.1",
57
+ "@typescript-eslint/parser": "^8.46.3",
58
+ "eslint": "^9.39.1",
59
+ "tsup": "^8.5.0",
60
+ "typescript": "5.9.2",
61
+ "vitest": "^3.2.1"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ }
66
+ }