@eslint-react/jsx 0.5.5

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.mjs ADDED
@@ -0,0 +1,554 @@
1
+ import { F, O as O$1, E as E$1 } from '@eslint-react/tools';
2
+ import memo from 'micro-memoize';
3
+ import { getVariablesUpToGlobal, findVariableByName, NodeType, traverseUp, is, isJSXTagNameExpression, findVariableByNameUpToGlobal, getVariableNthDefNodeInit, isOneOf, getNestedReturnStatements, traverseUpGuard, isStringLiteral, isMultiLine } from '@eslint-react/ast';
4
+ import { AST_NODE_TYPES } from '@typescript-eslint/types';
5
+
6
+ /**
7
+ * Check if a JSXElement or JSXFragment has children
8
+ * @param node The AST node to check
9
+ * @returns `true` if the node has children
10
+ */ function hasChildren(node) {
11
+ return node.children.length > 0;
12
+ }
13
+
14
+ const RE_JSX_ANNOTATION_REGEX = /@jsx\s+(\S+)/u;
15
+ // Does not check for reserved keywords or unicode characters
16
+ const RE_JS_IDENTIFIER_REGEX = /^[$A-Z_a-z][\w$]*$/u;
17
+ function getFragmentFromContext(context) {
18
+ // eslint-disable-next-line prefer-destructuring
19
+ const settings = context.settings;
20
+ const pragma = settings.react?.fragment ?? "Fragment";
21
+ if (!RE_JS_IDENTIFIER_REGEX.test(pragma)) {
22
+ return E$1.left(new Error(`Fragment pragma ${pragma} is not a valid identifier`));
23
+ }
24
+ return E$1.right(pragma);
25
+ }
26
+ const getPragmaFromContext = memo((context)=>{
27
+ // eslint-disable-next-line prefer-destructuring
28
+ const settings = context.settings;
29
+ const sourceCode = context.getSourceCode();
30
+ const pragmaNode = sourceCode.getAllComments().find((node)=>RE_JSX_ANNOTATION_REGEX.test(node.value));
31
+ const pragma = settings.react?.pragma ?? F.pipe(O$1.fromNullable(pragmaNode), O$1.map((node)=>RE_JSX_ANNOTATION_REGEX.exec(node.value)), O$1.flatMap((matches)=>O$1.fromNullable(matches?.[1]?.split(".")[0])), O$1.getOrElse(()=>"React"));
32
+ if (!RE_JS_IDENTIFIER_REGEX.test(pragma)) {
33
+ return E$1.left(new Error(`React pragma ${pragma} is not a valid identifier`));
34
+ }
35
+ return E$1.right(pragma);
36
+ });
37
+
38
+ const t=Symbol.for("@ts-pattern/matcher"),e=Symbol.for("@ts-pattern/isVariadic"),n="@ts-pattern/anonymous-select-key",r=t=>Boolean(t&&"object"==typeof t),i=e=>e&&!!e[t],s=(n,o,c)=>{if(i(n)){const e=n[t](),{matched:r,selections:i}=e.match(o);return r&&i&&Object.keys(i).forEach(t=>c(t,i[t])),r}if(r(n)){if(!r(o))return !1;if(Array.isArray(n)){if(!Array.isArray(o))return !1;let t=[],r=[],a=[];for(const s of n.keys()){const o=n[s];i(o)&&o[e]?a.push(o):a.length?r.push(o):t.push(o);}if(a.length){if(a.length>1)throw new Error("Pattern error: Using `...P.array(...)` several times in a single pattern is not allowed.");if(o.length<t.length+r.length)return !1;const e=o.slice(0,t.length),n=0===r.length?[]:o.slice(-r.length),i=o.slice(t.length,0===r.length?Infinity:-r.length);return t.every((t,n)=>s(t,e[n],c))&&r.every((t,e)=>s(t,n[e],c))&&(0===a.length||s(a[0],i,c))}return n.length===o.length&&n.every((t,e)=>s(t,o[e],c))}return Object.keys(n).every(e=>{const r=n[e];return (e in o||i(a=r)&&"optional"===a[t]().matcherType)&&s(r,o[e],c);var a;})}return Object.is(o,n)},o=e=>{var n,s,a;return r(e)?i(e)?null!=(n=null==(s=(a=e[t]()).getSelectionKeys)?void 0:s.call(a))?n:[]:Array.isArray(e)?c(e,o):c(Object.values(e),o):[]},c=(t,e)=>t.reduce((t,n)=>t.concat(e(n)),[]);function a(...t){if(1===t.length){const[e]=t;return t=>s(e,t,()=>{})}if(2===t.length){const[e,n]=t;return s(e,n,()=>{})}throw new Error(`isMatching wasn't given the right number of arguments: expected 1 or 2, received ${t.length}.`)}function u(t){return Object.assign(t,{optional:()=>l(t),and:e=>m(t,e),or:e=>y(t,e),select:e=>void 0===e?p(t):p(e,t)})}function h(t){return Object.assign((t=>Object.assign(t,{*[Symbol.iterator](){yield Object.assign(t,{[e]:!0});}}))(t),{optional:()=>h(l(t)),select:e=>h(void 0===e?p(t):p(e,t))})}function l(e){return u({[t]:()=>({match:t=>{let n={};const r=(t,e)=>{n[t]=e;};return void 0===t?(o(e).forEach(t=>r(t,void 0)),{matched:!0,selections:n}):{matched:s(e,t,r),selections:n}},getSelectionKeys:()=>o(e),matcherType:"optional"})})}const f=(t,e)=>{for(const n of t)if(!e(n))return !1;return !0},g=(t,e)=>{for(const[n,r]of t.entries())if(!e(r,n))return !1;return !0};function m(...e){return u({[t]:()=>({match:t=>{let n={};const r=(t,e)=>{n[t]=e;};return {matched:e.every(e=>s(e,t,r)),selections:n}},getSelectionKeys:()=>c(e,o),matcherType:"and"})})}function y(...e){return u({[t]:()=>({match:t=>{let n={};const r=(t,e)=>{n[t]=e;};return c(e,o).forEach(t=>r(t,void 0)),{matched:e.some(e=>s(e,t,r)),selections:n}},getSelectionKeys:()=>c(e,o),matcherType:"or"})})}function d(e){return {[t]:()=>({match:t=>({matched:Boolean(e(t))})})}}function p(...e){const r="string"==typeof e[0]?e[0]:void 0,i=2===e.length?e[1]:"string"==typeof e[0]?void 0:e[0];return u({[t]:()=>({match:t=>{let e={[null!=r?r:n]:t};return {matched:void 0===i||s(i,t,(t,n)=>{e[t]=n;}),selections:e}},getSelectionKeys:()=>[null!=r?r:n].concat(void 0===i?[]:o(i))})})}function v(t){return "number"==typeof t}function b(t){return "string"==typeof t}function w(t){return "bigint"==typeof t}const S=u(d(function(t){return !0})),O=S,j=t=>Object.assign(u(t),{startsWith:e=>{return j(m(t,(n=e,d(t=>b(t)&&t.startsWith(n)))));var n;},endsWith:e=>{return j(m(t,(n=e,d(t=>b(t)&&t.endsWith(n)))));var n;},minLength:e=>j(m(t,(t=>d(e=>b(e)&&e.length>=t))(e))),maxLength:e=>j(m(t,(t=>d(e=>b(e)&&e.length<=t))(e))),includes:e=>{return j(m(t,(n=e,d(t=>b(t)&&t.includes(n)))));var n;},regex:e=>{return j(m(t,(n=e,d(t=>b(t)&&Boolean(t.match(n))))));var n;}}),E=j(d(b)),K=t=>Object.assign(u(t),{between:(e,n)=>K(m(t,((t,e)=>d(n=>v(n)&&t<=n&&e>=n))(e,n))),lt:e=>K(m(t,(t=>d(e=>v(e)&&e<t))(e))),gt:e=>K(m(t,(t=>d(e=>v(e)&&e>t))(e))),lte:e=>K(m(t,(t=>d(e=>v(e)&&e<=t))(e))),gte:e=>K(m(t,(t=>d(e=>v(e)&&e>=t))(e))),int:()=>K(m(t,d(t=>v(t)&&Number.isInteger(t)))),finite:()=>K(m(t,d(t=>v(t)&&Number.isFinite(t)))),positive:()=>K(m(t,d(t=>v(t)&&t>0))),negative:()=>K(m(t,d(t=>v(t)&&t<0)))}),A=K(d(v)),x=t=>Object.assign(u(t),{between:(e,n)=>x(m(t,((t,e)=>d(n=>w(n)&&t<=n&&e>=n))(e,n))),lt:e=>x(m(t,(t=>d(e=>w(e)&&e<t))(e))),gt:e=>x(m(t,(t=>d(e=>w(e)&&e>t))(e))),lte:e=>x(m(t,(t=>d(e=>w(e)&&e<=t))(e))),gte:e=>x(m(t,(t=>d(e=>w(e)&&e>=t))(e))),positive:()=>x(m(t,d(t=>w(t)&&t>0))),negative:()=>x(m(t,d(t=>w(t)&&t<0)))}),P=x(d(w)),T=u(d(function(t){return "boolean"==typeof t})),k=u(d(function(t){return "symbol"==typeof t})),B=u(d(function(t){return null==t}));var _={__proto__:null,matcher:t,optional:l,array:function(...e){return h({[t]:()=>({match:t=>{if(!Array.isArray(t))return {matched:!1};if(0===e.length)return {matched:!0};const n=e[0];let r={};if(0===t.length)return o(n).forEach(t=>{r[t]=[];}),{matched:!0,selections:r};const i=(t,e)=>{r[t]=(r[t]||[]).concat([e]);};return {matched:t.every(t=>s(n,t,i)),selections:r}},getSelectionKeys:()=>0===e.length?[]:o(e[0])})})},set:function(...e){return u({[t]:()=>({match:t=>{if(!(t instanceof Set))return {matched:!1};let n={};if(0===t.size)return {matched:!0,selections:n};if(0===e.length)return {matched:!0};const r=(t,e)=>{n[t]=(n[t]||[]).concat([e]);},i=e[0];return {matched:f(t,t=>s(i,t,r)),selections:n}},getSelectionKeys:()=>0===e.length?[]:o(e[0])})})},map:function(...e){return u({[t]:()=>({match:t=>{if(!(t instanceof Map))return {matched:!1};let n={};if(0===t.size)return {matched:!0,selections:n};const r=(t,e)=>{n[t]=(n[t]||[]).concat([e]);};if(0===e.length)return {matched:!0};var i;if(1===e.length)throw new Error(`\`P.map\` wasn't given enough arguments. Expected (key, value), received ${null==(i=e[0])?void 0:i.toString()}`);const[o,c]=e;return {matched:g(t,(t,e)=>{const n=s(o,e,r),i=s(c,t,r);return n&&i}),selections:n}},getSelectionKeys:()=>0===e.length?[]:[...o(e[0]),...o(e[1])]})})},intersection:m,union:y,not:function(e){return u({[t]:()=>({match:t=>({matched:!s(e,t,()=>{})}),getSelectionKeys:()=>[],matcherType:"not"})})},when:d,select:p,any:S,_:O,string:E,number:A,bigint:P,boolean:T,symbol:k,nullish:B,instanceOf:function(t){return u(d(function(t){return e=>e instanceof t}(t)))},shape:function(t){return u(d(a(t)))}};const W={matched:!1,value:void 0};function N(t){return new $(t,W)}class ${constructor(t,e){this.input=void 0,this.state=void 0,this.input=t,this.state=e;}with(...t){if(this.state.matched)return this;const e=t[t.length-1],r=[t[0]];let i;3===t.length&&"function"==typeof t[1]?(r.push(t[0]),i=t[1]):t.length>2&&r.push(...t.slice(1,t.length-1));let o=!1,c={};const a=(t,e)=>{o=!0,c[t]=e;},u=!r.some(t=>s(t,this.input,a))||i&&!Boolean(i(this.input))?W:{matched:!0,value:e(o?n in c?c[n]:c:this.input,this.input)};return new $(this.input,u)}when(t,e){if(this.state.matched)return this;const n=Boolean(t(this.input));return new $(this.input,n?{matched:!0,value:e(this.input,this.input)}:W)}otherwise(t){return this.state.matched?this.state.value:t(this.input)}exhaustive(){return this.run()}run(){if(this.state.matched)return this.state.value;let t;try{t=JSON.stringify(this.input);}catch(e){t=this.input;}throw new Error(`Pattern matching error: no pattern matches value ${t}`)}returnType(){return this}}
39
+
40
+ function isDestructuredFromPragma(variableName, context) {
41
+ const maybePragma = getPragmaFromContext(context);
42
+ const variables = getVariablesUpToGlobal(context.getScope());
43
+ if (E$1.isLeft(maybePragma)) {
44
+ return false;
45
+ }
46
+ const pragma = maybePragma.right;
47
+ const maybeLatestDef = F.pipe(findVariableByName(variableName)(variables), O$1.flatMapNullable((variable)=>variable.defs.at(-1)));
48
+ if (O$1.isNone(maybeLatestDef)) {
49
+ return false;
50
+ }
51
+ const latestDef = maybeLatestDef.value;
52
+ const { node, parent } = latestDef;
53
+ if (node.type === NodeType.VariableDeclarator && node.init) {
54
+ const { init } = node;
55
+ // check for: `variable = pragma.variable`
56
+ if (a({
57
+ type: "MemberExpression",
58
+ object: {
59
+ type: "Identifier",
60
+ name: pragma
61
+ }
62
+ }, init)) {
63
+ return true;
64
+ }
65
+ // check for: `{ variable } = pragma`
66
+ if (a({
67
+ type: "Identifier",
68
+ name: pragma
69
+ }, init)) {
70
+ return true;
71
+ }
72
+ // check if from a require call: `require("react")`
73
+ const maybeRequireExpression = N(init).with({
74
+ type: NodeType.CallExpression
75
+ }, (exp)=>O$1.some(exp)).with({
76
+ type: NodeType.MemberExpression,
77
+ object: {
78
+ type: NodeType.CallExpression
79
+ }
80
+ }, ({ object })=>O$1.some(object)).otherwise(O$1.none);
81
+ if (O$1.isNone(maybeRequireExpression)) {
82
+ return false;
83
+ }
84
+ const requireExpression = maybeRequireExpression.value;
85
+ if (requireExpression.callee.type !== NodeType.Identifier) {
86
+ return false;
87
+ }
88
+ const calleeName = requireExpression.callee.name;
89
+ const [firstArg] = requireExpression.arguments;
90
+ if (calleeName !== "require" || firstArg?.type !== NodeType.Literal) {
91
+ return false;
92
+ }
93
+ return firstArg.value === pragma.toLowerCase();
94
+ }
95
+ // latest definition is an import declaration: import { variable } from 'react'
96
+ return a({
97
+ type: "ImportDeclaration",
98
+ source: {
99
+ value: pragma.toLowerCase()
100
+ }
101
+ }, parent);
102
+ }
103
+
104
+ /**
105
+ * Checks if the given node is a call expression to the given function or method of the pragma
106
+ * @param name The name of the function or method to check
107
+ * @returns A predicate that checks if the given node is a call expression to the given function or method
108
+ */ const isCallFromPragma = (name)=>(node, context)=>{
109
+ if (node.type !== NodeType.CallExpression || !("callee" in node)) {
110
+ return false;
111
+ }
112
+ const maybePragma = getPragmaFromContext(context);
113
+ if (E$1.isLeft(maybePragma)) {
114
+ return false;
115
+ }
116
+ const pragma = maybePragma.right;
117
+ return N(node.callee).with({
118
+ type: NodeType.MemberExpression,
119
+ object: {
120
+ name: pragma
121
+ },
122
+ property: {
123
+ name
124
+ }
125
+ }, F.constTrue).with({
126
+ name
127
+ }, ({ name })=>isDestructuredFromPragma(name, context)).otherwise(F.constFalse);
128
+ };
129
+
130
+ /**
131
+ * Checks if the given node is a call expression to `React.createElement`
132
+ * @param node The AST node to check
133
+ * @param context The rule context
134
+ * @returns `true` if the node is a call expression to `createElement`
135
+ */ const isCreateElementCall = isCallFromPragma("createElement");
136
+ /**
137
+ * Checks if the given node is a call expression to `React.cloneElement`
138
+ * @param node The AST node to check
139
+ * @param context The rule context
140
+ * @returns `true` if the node is a call expression to `cloneElement`
141
+ */ const isCloneElementCall = isCallFromPragma("cloneElement");
142
+
143
+ function isChildrenOfCreateElement(node, context) {
144
+ const maybeCallExpression = node.parent;
145
+ if (!maybeCallExpression || !isCreateElementCall(maybeCallExpression, context)) {
146
+ return false;
147
+ }
148
+ return maybeCallExpression.arguments.slice(2).some((child)=>child === node);
149
+ }
150
+
151
+ /**
152
+ * Determines whether inside createElement's props.
153
+ * @param node The AST node to check
154
+ * @param context The rule context
155
+ * @returns `true` if the node is inside createElement's props
156
+ */ function isInsideCreateElementProps(node, context) {
157
+ const parentCreateElement = traverseUp(node, (n)=>isCreateElementCall(n, context));
158
+ if (parentCreateElement?.type !== NodeType.CallExpression) {
159
+ return false;
160
+ }
161
+ return parentCreateElement.arguments.at(1) === traverseUp(node, is(NodeType.ObjectExpression));
162
+ }
163
+
164
+ function resolveMemberExpressions(object, property) {
165
+ if (object.type === AST_NODE_TYPES.JSXMemberExpression) {
166
+ return `${resolveMemberExpressions(object.object, object.property)}.${property.name}`;
167
+ }
168
+ if (object.type === AST_NODE_TYPES.JSXNamespacedName) {
169
+ return `${object.namespace.name}:${object.name.name}.${property.name}`;
170
+ }
171
+ return `${object.name}.${property.name}`;
172
+ }
173
+ /**
174
+ * Returns the tag name associated with a JSXOpeningElement.
175
+ * @param node The visited JSXOpeningElement node object.
176
+ * @returns The element's tag name.
177
+ */ function elementType(node) {
178
+ if (node.type === AST_NODE_TYPES.JSXOpeningFragment) {
179
+ return "<>";
180
+ }
181
+ const { name } = node;
182
+ if (name.type === AST_NODE_TYPES.JSXMemberExpression) {
183
+ const { object, property } = name;
184
+ return resolveMemberExpressions(object, property);
185
+ }
186
+ if (name.type === AST_NODE_TYPES.JSXNamespacedName) {
187
+ return `${name.namespace.name}:${name.name.name}`;
188
+ }
189
+ return name.name;
190
+ }
191
+
192
+ const hdlAnimation = [
193
+ "onAnimationStart",
194
+ "onAnimationEnd",
195
+ "onAnimationIteration"
196
+ ];
197
+ const hdlClipboard = [
198
+ "onCopy",
199
+ "onCut",
200
+ "onPaste"
201
+ ];
202
+ const hdlComposition = [
203
+ "onCompositionEnd",
204
+ "onCompositionStart",
205
+ "onCompositionUpdate"
206
+ ];
207
+ const hdlFocus = [
208
+ "onFocus",
209
+ "onBlur"
210
+ ];
211
+ const hdlForm = [
212
+ "onChange",
213
+ "onInput",
214
+ "onSubmit"
215
+ ];
216
+ const hdlImage = [
217
+ "onLoad",
218
+ "onError"
219
+ ];
220
+ const hdlKeyboard = [
221
+ "onKeyDown",
222
+ "onKeyPress",
223
+ "onKeyUp"
224
+ ];
225
+ const hdlMedia = [
226
+ "onAbort",
227
+ "onCanPlay",
228
+ "onCanPlayThrough",
229
+ "onDurationChange",
230
+ "onEmptied",
231
+ "onEncrypted",
232
+ "onEnded",
233
+ "onError",
234
+ "onLoadedData",
235
+ "onLoadedMetadata",
236
+ "onLoadStart",
237
+ "onPause",
238
+ "onPlay",
239
+ "onPlaying",
240
+ "onProgress",
241
+ "onRateChange",
242
+ "onSeeked",
243
+ "onSeeking",
244
+ "onStalled",
245
+ "onSuspend",
246
+ "onTimeUpdate",
247
+ "onVolumeChange",
248
+ "onWaiting"
249
+ ];
250
+ const hdlMouse = [
251
+ "onClick",
252
+ "onContextMenu",
253
+ "onDblClick",
254
+ "onDoubleClick",
255
+ "onDrag",
256
+ "onDragEnd",
257
+ "onDragEnter",
258
+ "onDragExit",
259
+ "onDragLeave",
260
+ "onDragOver",
261
+ "onDragStart",
262
+ "onDrop",
263
+ "onMouseDown",
264
+ "onMouseEnter",
265
+ "onMouseLeave",
266
+ "onMouseMove",
267
+ "onMouseOut",
268
+ "onMouseOver",
269
+ "onMouseUp"
270
+ ];
271
+ const hdlSelection = [
272
+ "onSelect"
273
+ ];
274
+ const hdlTouch = [
275
+ "onTouchCancel",
276
+ "onTouchEnd",
277
+ "onTouchMove",
278
+ "onTouchStart"
279
+ ];
280
+ const hdlTransition = [
281
+ "onTransitionEnd"
282
+ ];
283
+ const hdlScroll = [
284
+ "onScroll"
285
+ ];
286
+ const hdlWheel = [
287
+ "onWheel"
288
+ ];
289
+ [
290
+ ...hdlAnimation,
291
+ ...hdlClipboard,
292
+ ...hdlComposition,
293
+ ...hdlFocus,
294
+ ...hdlForm,
295
+ ...hdlImage,
296
+ ...hdlKeyboard,
297
+ ...hdlMedia,
298
+ ...hdlMouse,
299
+ ...hdlSelection,
300
+ ...hdlTouch,
301
+ ...hdlTransition,
302
+ ...hdlScroll,
303
+ ...hdlWheel
304
+ ];
305
+
306
+ /**
307
+ * Tests if a value is a `string`.
308
+ *
309
+ * @param input - The value to test.
310
+ *
311
+ * @example
312
+ * import { isString } from "effect/Predicate"
313
+ *
314
+ * assert.deepStrictEqual(isString("a"), true)
315
+ *
316
+ * assert.deepStrictEqual(isString(1), false)
317
+ *
318
+ * @category guards
319
+ * @since 2.0.0
320
+ */
321
+ const isString = input => typeof input === "string";
322
+
323
+ const defaultJSXValueCheckOptions = {
324
+ ignoreNull: false,
325
+ strict: false
326
+ };
327
+ /**
328
+ * Check if a node is a JSX value
329
+ * @param node The AST node to check
330
+ * @param context The rule context
331
+ * @param options The `JSXValueCheckOptions` to use
332
+ * @returns boolean
333
+ */ function isJSXValue(node, context, options = defaultJSXValueCheckOptions) {
334
+ if (!node) {
335
+ return false;
336
+ }
337
+ // TODO: implement strict mode
338
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
339
+ const { ignoreNull = false, strict = false } = options;
340
+ return N(node.type).with(NodeType.JSXElement, F.constTrue).with(NodeType.JSXFragment, F.constTrue).with(NodeType.Literal, ()=>{
341
+ if (!("value" in node) || ignoreNull) {
342
+ return false;
343
+ }
344
+ return node.value === null;
345
+ }).with(NodeType.ConditionalExpression, ()=>{
346
+ if (!("consequent" in node)) {
347
+ return false;
348
+ }
349
+ const leftHasJSX = N(node.consequent).with(_.array(), (n)=>n.some((n)=>isJSXValue(n, context, options))).with(_.any, (n)=>isJSXValue(n, context, options)).exhaustive();
350
+ if (leftHasJSX) {
351
+ return true;
352
+ }
353
+ return "alternate" in node && isJSXValue(node.alternate, context, options);
354
+ }).with(NodeType.LogicalExpression, ()=>{
355
+ if (!("left" in node)) {
356
+ return false;
357
+ }
358
+ return isJSXValue(node.left, context, options) || isJSXValue(node.right, context, options);
359
+ }).with(NodeType.SequenceExpression, ()=>{
360
+ if (!("expressions" in node)) {
361
+ return false;
362
+ }
363
+ const exp = node.expressions.at(-1);
364
+ return isJSXValue(exp, context, options);
365
+ }).with(NodeType.CallExpression, ()=>isCreateElementCall(node, context)).with(NodeType.Identifier, ()=>{
366
+ if (!("name" in node)) {
367
+ return false;
368
+ }
369
+ if (isJSXTagNameExpression(node)) {
370
+ return true;
371
+ }
372
+ if (!isString(node.name) && node.name.type !== NodeType.Identifier) {
373
+ return isJSXTagNameExpression(node.name);
374
+ }
375
+ const name = N(node.name).with(_.string, F.identity).with({
376
+ type: NodeType.Identifier
377
+ }, (n)=>n.name).exhaustive();
378
+ const variable = findVariableByNameUpToGlobal(name, context.getScope());
379
+ return F.pipe(variable, O$1.flatMap(getVariableNthDefNodeInit(0)), O$1.map(isOneOf([
380
+ NodeType.JSXElement,
381
+ NodeType.JSXFragment
382
+ ])), O$1.getOrElse(F.constFalse));
383
+ }).otherwise(F.constFalse);
384
+ }
385
+
386
+ /**
387
+ * Check if function is returning JSX
388
+ * @param node The return statement node to check
389
+ * @param context The rule context
390
+ * @param options The JSX value check options
391
+ * @returns boolean
392
+ */ function isFunctionReturningJSXValue(node, context, options) {
393
+ if (node.body.type !== NodeType.BlockStatement) {
394
+ return isJSXValue(node.body, context, options);
395
+ }
396
+ const statements = getNestedReturnStatements(node.body);
397
+ return statements.some((statement)=>isJSXValue(statement.argument, context, options));
398
+ }
399
+
400
+ /**
401
+ * Check if the given prop name is present in the given attributes
402
+ * @param attributes The attributes to search in
403
+ * @param propName The prop name to search for
404
+ * @param context The rule context
405
+ * @returns `true` if the given prop name is present in the given properties
406
+ */ function hasProp(attributes, propName, context) {
407
+ return O$1.isSome(findPropInAttributes(attributes, context)(propName));
408
+ }
409
+ /**
410
+ * Check if any of the given prop names are present in the given attributes
411
+ * @param attributes The attributes to search in
412
+ * @param propNames The prop names to search for
413
+ * @param context The rule context
414
+ * @returns `true` if any of the given prop names are present in the given attributes
415
+ */ function hasAnyProp(attributes, propNames, context) {
416
+ return propNames.some((propName)=>hasProp(attributes, propName, context));
417
+ }
418
+ /**
419
+ * Check if all of the given prop names are present in the given attributes
420
+ * @param attributes The attributes to search in
421
+ * @param propNames The prop names to search for
422
+ * @param context The rule context
423
+ * @returns `true` if all of the given prop names are present in the given attributes
424
+ */ function hasEveryProp(attributes, propNames, context) {
425
+ return propNames.every((propName)=>hasProp(attributes, propName, context));
426
+ }
427
+ /**
428
+ * Get the name of a JSX attribute
429
+ * @param node The JSX attribute node
430
+ * @returns string
431
+ */ function getPropName(node) {
432
+ return N(node.name).when(is(NodeType.JSXIdentifier), (n)=>n.name).when(is(NodeType.JSXNamespacedName), (n)=>n.name.name).exhaustive();
433
+ }
434
+ /**
435
+ * Get the name of a JSX attribute with namespace
436
+ * @param node The JSX attribute node
437
+ * @returns string
438
+ */ function getPropNameWithNamespace(node) {
439
+ return N(node.name).when(is(NodeType.JSXIdentifier), (n)=>n.name).when(is(NodeType.JSXNamespacedName), (n)=>`${n.namespace.name}:${n.name.name}`).exhaustive();
440
+ }
441
+ /**
442
+ * Traverses up prop node
443
+ * @param node The AST node to start traversing from
444
+ * @param predicate The predicate to check each node
445
+ * @returns prop node if found
446
+ */ function traverseUpProp(node, predicate = F.constTrue) {
447
+ const matcher = (node)=>{
448
+ return node.type === NodeType.JSXAttribute && predicate(node);
449
+ };
450
+ return O$1.fromNullable(traverseUpGuard(node, matcher));
451
+ }
452
+ /**
453
+ * Checks if the node is inside a prop's value
454
+ * @param node The AST node to check
455
+ * @returns `true` if the node is inside a prop's value
456
+ */ function isInsidePropValue(node) {
457
+ if (isStringLiteral(node)) {
458
+ return node.parent.type === NodeType.JSXAttribute;
459
+ }
460
+ return O$1.isSome(traverseUpProp(node, (n)=>n.value?.type === NodeType.JSXExpressionContainer));
461
+ }
462
+ /**
463
+ * @param properties The properties to search in
464
+ * @param context The rule context
465
+ * @param seenProps The properties that have already been seen
466
+ * @returns A function that searches for a property in the given properties
467
+ */ function findPropInProperties(properties, context, seenProps = []) {
468
+ /**
469
+ * Search for a property in the given properties
470
+ * @param propName The name of the property to search for
471
+ * @returns The property if found
472
+ */ return (propName)=>{
473
+ return O$1.fromNullable(properties.find((prop)=>{
474
+ if (prop.type === NodeType.Property) {
475
+ return "name" in prop.key && prop.key.name === propName;
476
+ }
477
+ if (prop.type === NodeType.SpreadElement) {
478
+ if (!("argument" in prop && "name" in prop.argument)) {
479
+ return false;
480
+ }
481
+ const { name } = prop.argument;
482
+ const maybeFirstDefNodeInit = F.pipe(findVariableByNameUpToGlobal(name, context.getScope()), O$1.flatMap(getVariableNthDefNodeInit(0)));
483
+ if (O$1.isNone(maybeFirstDefNodeInit)) {
484
+ return false;
485
+ }
486
+ const firstDefNodeInit = maybeFirstDefNodeInit.value;
487
+ if (firstDefNodeInit.type !== NodeType.ObjectExpression) {
488
+ return false;
489
+ }
490
+ if (seenProps.includes(name)) {
491
+ return false;
492
+ }
493
+ return O$1.isSome(findPropInProperties(firstDefNodeInit.properties, context, [
494
+ ...seenProps,
495
+ name
496
+ ])(propName));
497
+ }
498
+ return false;
499
+ }));
500
+ };
501
+ }
502
+ /**
503
+ * @param attributes The attributes to search in
504
+ * @param context The rule context
505
+ * @returns A function that searches for a property in the given attributes
506
+ */ function findPropInAttributes(attributes, context) {
507
+ /**
508
+ * Search for a property in the given attributes
509
+ * @param propName The name of the property to search for
510
+ * @returns The property if found
511
+ */ return (propName)=>{
512
+ return O$1.fromNullable(attributes.find((attr)=>{
513
+ return N(attr).when(is(NodeType.JSXAttribute), (attr)=>getPropName(attr) === propName).when(is(NodeType.JSXSpreadAttribute), (attr)=>{
514
+ if (!("argument" in attr && "name" in attr.argument)) {
515
+ return false;
516
+ }
517
+ const { name } = attr.argument;
518
+ const maybeFirstDefNodeInit = F.pipe(findVariableByNameUpToGlobal(name, context.getScope()), O$1.flatMap(getVariableNthDefNodeInit(0)));
519
+ if (O$1.isNone(maybeFirstDefNodeInit)) {
520
+ return false;
521
+ }
522
+ if (!("properties" in maybeFirstDefNodeInit.value)) {
523
+ return false;
524
+ }
525
+ return O$1.isSome(findPropInProperties(maybeFirstDefNodeInit.value.properties, context)(propName));
526
+ }).otherwise(F.constFalse);
527
+ }));
528
+ };
529
+ }
530
+
531
+ /**
532
+ * Check if a node is a Literal or JSXText
533
+ * @param node The AST node to check
534
+ * @returns boolean `true` if the node is a Literal or JSXText
535
+ */ const isLiteral = isOneOf([
536
+ NodeType.Literal,
537
+ NodeType.JSXText
538
+ ]);
539
+ /**
540
+ * Check if a Literal or JSXText node is whitespace
541
+ * @param node The AST node to check
542
+ * @returns boolean `true` if the node is whitespace
543
+ */ function isWhiteSpace(node) {
544
+ return isString(node.value) && node.value.trim() === "";
545
+ }
546
+ /**
547
+ * Check if a Literal or JSXText node is a line break
548
+ * @param node The AST node to check
549
+ * @returns boolean
550
+ */ function isLineBreak(node) {
551
+ return isLiteral(node) && isWhiteSpace(node) && isMultiLine(node);
552
+ }
553
+
554
+ export { defaultJSXValueCheckOptions, elementType, findPropInAttributes, findPropInProperties, getFragmentFromContext, getPragmaFromContext, getPropName, getPropNameWithNamespace, hasAnyProp, hasChildren, hasEveryProp, hasProp, hdlAnimation, hdlClipboard, hdlComposition, hdlFocus, hdlForm, hdlImage, hdlKeyboard, hdlMedia, hdlMouse, hdlScroll, hdlSelection, hdlTouch, hdlTransition, hdlWheel, isCallFromPragma, isChildrenOfCreateElement, isCloneElementCall, isCreateElementCall, isDestructuredFromPragma, isFunctionReturningJSXValue, isInsideCreateElementProps, isInsidePropValue, isJSXValue, isLineBreak, isLiteral, isWhiteSpace, traverseUpProp };
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@eslint-react/jsx",
3
+ "version": "0.5.5",
4
+ "description": "",
5
+ "keywords": [
6
+ "eslint-config"
7
+ ],
8
+ "homepage": "https://github.com/Rel1cx/eslint-react",
9
+ "bugs": {
10
+ "url": "https://github.com/Rel1cx/eslint-react/issues"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/Rel1cx/eslint-react.git",
15
+ "directory": "packages/jsx"
16
+ },
17
+ "license": "MIT",
18
+ "author": "Eva1ent<let@ik.me>",
19
+ "exports": {
20
+ ".": {
21
+ "import": {
22
+ "types": "./dist/index.d.mts",
23
+ "default": "./dist/index.mjs"
24
+ },
25
+ "require": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ }
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "main": "dist/index.js",
33
+ "module": "dist/index.mjs",
34
+ "types": "dist/index.d.ts",
35
+ "files": [
36
+ "dist",
37
+ "./package.json"
38
+ ],
39
+ "dependencies": {
40
+ "@types/eslint": "8.44.6",
41
+ "@typescript-eslint/scope-manager": "6.9.0",
42
+ "@typescript-eslint/types": "6.9.0",
43
+ "@typescript-eslint/utils": "6.9.0",
44
+ "micro-memoize": "4.1.2",
45
+ "@eslint-react/shared": "0.5.5",
46
+ "@eslint-react/types": "0.5.5",
47
+ "@eslint-react/tools": "0.5.5",
48
+ "@eslint-react/ast": "0.5.5"
49
+ },
50
+ "scripts": {
51
+ "build": "rollup -c rollup.config.ts --configPlugin swc3 && cp dist/index.d.ts dist/index.d.mts",
52
+ "build:docs": "typedoc && dprint fmt ./docs/**/*.md",
53
+ "lint:publish": "pnpm publint",
54
+ "lint:type": "tsc --noEmit"
55
+ }
56
+ }