@@ -19,6 +19,75 @@ const ECMA_VERSION = 2021,
19
19
TEST_FILES = [ '**/*.test.js' , '**/*.test.jsx' , '**/*.test.ts' , '**/*.test.tsx' , '**/test/**' , '**/__tests__/**' ] ,
20
20
TYPESCRIPT_FILES = [ '**/*.cts' , '**/*.mts' , '**/*.ts' , '**/*.tsx' ] ;
21
21
22
+ const noNavigateUseClerk = {
23
+ meta : {
24
+ type : 'problem' ,
25
+ docs : {
26
+ description : 'Disallow any usage of `navigate` from `useClerk()`' ,
27
+ recommended : false ,
28
+ } ,
29
+ messages : {
30
+ noNavigate :
31
+ 'Usage of `navigate` from `useClerk()` is not allowed.\nUse `useRouter().navigate` to navigate in-between flows or `setActive({ redirectUrl })`.' ,
32
+ } ,
33
+ schema : [ ] ,
34
+ } ,
35
+ create ( context ) {
36
+ const sourceCode = context . getSourceCode ( ) ;
37
+
38
+ return {
39
+ // Case 1: Destructuring `navigate` from `useClerk()`
40
+ VariableDeclarator ( node ) {
41
+ if (
42
+ node . id . type === 'ObjectPattern' && // Checks if it's an object destructuring
43
+ node . init ?. type === 'CallExpression' &&
44
+ node . init . callee . name === 'useClerk'
45
+ ) {
46
+ for ( const property of node . id . properties ) {
47
+ if ( property . type === 'Property' && property . key . name === 'navigate' ) {
48
+ context . report ( {
49
+ node : property ,
50
+ messageId : 'noNavigate' ,
51
+ } ) ;
52
+ }
53
+ }
54
+ }
55
+ } ,
56
+
57
+ // Case 2 & 3: Accessing `navigate` on a variable or directly calling `useClerk().navigate`
58
+ MemberExpression ( node ) {
59
+ if (
60
+ node . property . name === 'navigate' &&
61
+ node . object . type === 'CallExpression' &&
62
+ node . object . callee . name === 'useClerk'
63
+ ) {
64
+ // Case 3: Direct `useClerk().navigate`
65
+ context . report ( {
66
+ node,
67
+ messageId : 'noNavigate' ,
68
+ } ) ;
69
+ } else if ( node . property . name === 'navigate' && node . object . type === 'Identifier' ) {
70
+ // Case 2: `clerk.navigate` where `clerk` is assigned `useClerk()`
71
+ const scope = sourceCode . scopeManager . acquire ( node ) ;
72
+ if ( ! scope ) return ;
73
+
74
+ const variable = scope . variables . find ( v => v . name === node . object . name ) ;
75
+
76
+ if (
77
+ variable ?. defs ?. [ 0 ] ?. node ?. init ?. type === 'CallExpression' &&
78
+ variable . defs [ 0 ] . node . init . callee . name === 'useClerk'
79
+ ) {
80
+ context . report ( {
81
+ node,
82
+ messageId : 'noNavigate' ,
83
+ } ) ;
84
+ }
85
+ }
86
+ } ,
87
+ } ;
88
+ } ,
89
+ } ;
90
+
22
91
export default tseslint . config ( [
23
92
{
24
93
name : 'repo/ignores' ,
@@ -285,6 +354,20 @@ export default tseslint.config([
285
354
'react-hooks/rules-of-hooks' : 'warn' ,
286
355
} ,
287
356
} ,
357
+ {
358
+ name : 'packages/clerk-js' ,
359
+ files : [ 'packages/clerk-js/src/ui/**/*' ] ,
360
+ plugins : {
361
+ 'custom-rules' : {
362
+ rules : {
363
+ 'no-navigate-useClerk' : noNavigateUseClerk ,
364
+ } ,
365
+ } ,
366
+ } ,
367
+ rules : {
368
+ 'custom-rules/no-navigate-useClerk' : 'error' ,
369
+ } ,
370
+ } ,
288
371
{
289
372
name : 'packages/expo-passkeys' ,
290
373
files : [ 'packages/expo-passkeys/src/**/*' ] ,
0 commit comments