@@ -109,9 +109,7 @@ describe('@knighted/displayName', () => {
109109
110110 it ( 'requires memo, forwardRef or React to be in scope' , async ( ) => {
111111 const components = `
112- const Foo = memo(() => {
113- return <div>foo</div>
114- })
112+ const Foo = memo(() => <div>foo</div>)
115113 const Bar = forwardRef(() => {
116114 return <span>bar</span>
117115 })
@@ -272,6 +270,41 @@ describe('@knighted/displayName', () => {
272270 assert . ok ( code . indexOf ( "Qux.displayName = 'Qux'" ) !== - 1 )
273271 assert . ok ( code . indexOf ( "Qux2.displayName = 'Qux2'" ) !== - 1 )
274272 assert . ok ( code . indexOf ( "Qux3.displayName = 'Qux3'" ) !== - 1 )
273+
274+ src = `
275+ import ReactAlias, { memo as me, forwardRef as fr } from 'react'
276+
277+ function ShadowedReact() {
278+ const ReactAlias = { memo: () => {}, forwardRef: () => {} }
279+ const Foo = ReactAlias.memo(() => {
280+ return <div>foo</div>
281+ })
282+ const Qux = ReactAlias.forwardRef(() => {
283+ return <span>qux</span>
284+ })
285+ const Memo = me(() => {
286+ return <div>foo</div>
287+ })
288+ }
289+ function ShadowedMemo() {
290+ const me = () => {}
291+ const Bar = me(() => {
292+ return <div>bar</div>
293+ })
294+ }
295+ function ShadowedForwardRef() {
296+ const fr = () => {}
297+ const Baz = fr(() => {
298+ return <div>baz</div>
299+ })
300+ }
301+ `
302+ code = await modify ( src )
303+ assert . ok ( code . indexOf ( "Foo.displayName = 'Foo'" ) === - 1 )
304+ assert . ok ( code . indexOf ( "Bar.displayName = 'Bar'" ) === - 1 )
305+ assert . ok ( code . indexOf ( "Baz.displayName = 'Baz'" ) === - 1 )
306+ assert . ok ( code . indexOf ( "Qux.displayName = 'Qux'" ) === - 1 )
307+ assert . ok ( code . indexOf ( "Memo.displayName = 'Memo'" ) !== - 1 )
275308 } )
276309
277310 it ( 'works with params shadowing' , async ( ) => {
@@ -336,4 +369,166 @@ describe('@knighted/displayName', () => {
336369 assert . ok ( code . indexOf ( "Quxxx.displayName = 'Quxxx'" ) === - 1 )
337370 assert . ok ( code . indexOf ( "Memo.displayName = 'Memo'" ) !== - 1 )
338371 } )
372+
373+ it ( 'has option to use named function expressions' , async ( ) => {
374+ let src = `
375+ import { memo, forwardRef } from 'react'
376+ const arePropsEqual = () => true
377+ const Foo = memo((props) => {
378+ return <div>foo</div>
379+ }, arePropsEqual)
380+ const Bar = forwardRef((props, ref) => {
381+ return <span>bar</span>
382+ })
383+ const Baz = forwardRef<HTMLSpanElement, { foo: string }>((props, ref) => {
384+ return <span ref={ref}>baz</span>
385+ })
386+ `
387+ let code = await modify ( src , { style : 'namedFuncExpr' } )
388+
389+ assert . equal (
390+ code . replace ( / \s + / g, '' ) ,
391+ `
392+ import { memo, forwardRef } from 'react'
393+ const arePropsEqual = () => true
394+ const Foo = memo(function Foo(props) {
395+ return <div>foo</div>
396+ }, arePropsEqual)
397+ const Bar = forwardRef(function Bar(props, ref) {
398+ return <span>bar</span>
399+ })
400+ const Baz = forwardRef<HTMLSpanElement, { foo: string }>(function Baz(props, ref) {
401+ return <span ref={ref}>baz</span>
402+ })
403+ ` . replace ( / \s + / g, '' ) ,
404+ )
405+
406+ src = `
407+ import { memo, forwardRef } from 'react'
408+ const MemoWrapped = memo(forwardRef((props, ref) => {
409+ return <p>foo</p>
410+ }))
411+ `
412+ code = await modify ( src , {
413+ style : 'namedFuncExpr' ,
414+ modifyNestedForwardRef : true ,
415+ } )
416+ assert . equal (
417+ code . replace ( / \s + / g, '' ) ,
418+ `
419+ import { memo, forwardRef } from 'react'
420+ const MemoWrapped = memo(forwardRef(function MemoWrapped(props, ref) {
421+ return <p>foo</p>
422+ }))
423+ ` . replace ( / \s + / g, '' ) ,
424+ )
425+
426+ src = `
427+ import { memo, forwardRef } from 'react'
428+ const arePropsEqual = (prevProps: object, nextProps: object) => {
429+ return prevProps === nextProps
430+ }
431+ const Namespaced = {
432+ Foo: {
433+ Bar: memo((props) => {
434+ return <div>bar</div>
435+ }, arePropsEqual),
436+ Baz: forwardRef((props, ref) => {
437+ return <span>baz</span>
438+ }),
439+ }
440+ }
441+ `
442+ code = await modify ( src , { style : 'namedFuncExpr' } )
443+ assert . equal (
444+ code . replace ( / \s + / g, '' ) ,
445+ `
446+ import { memo, forwardRef } from 'react'
447+ const arePropsEqual = (prevProps: object, nextProps: object) => {
448+ return prevProps === nextProps
449+ }
450+ const Namespaced = {
451+ Foo: {
452+ Bar: memo(function Bar(props) {
453+ return <div>bar</div>
454+ }, arePropsEqual),
455+ Baz: forwardRef(function Baz(props, ref) {
456+ return <span>baz</span>
457+ }),
458+ }
459+ }
460+ ` . replace ( / \s + / g, '' ) ,
461+ )
462+
463+ src = `
464+ import { memo, forwardRef } from 'react'
465+ const A = memo(function (props) {
466+ return <p>a</p>
467+ }, arePropsEqual)
468+ const B = forwardRef(function () {
469+ return <p>b</p>
470+ })
471+ `
472+ code = await modify ( src , { style : 'namedFuncExpr' } )
473+ assert . equal (
474+ code . replace ( / \s + / g, '' ) ,
475+ `
476+ import { memo, forwardRef } from 'react'
477+ const A = memo(function A(props) {
478+ return <p>a</p>
479+ }, arePropsEqual)
480+ const B = forwardRef(function B() {
481+ return <p>b</p>
482+ })
483+ ` . replace ( / \s + / g, '' ) ,
484+ )
485+ } )
486+
487+ it ( 'the style option works with namedFuncExpr' , async t => {
488+ const read = resolve ( import . meta. dirname , './fixtures/style.tsx' )
489+ const write = resolve ( import . meta. dirname , './fixtures/style-modified.tsx' )
490+ const code = await modifyFile ( read , { style : 'namedFuncExpr' } )
491+ const normalized = code . replace ( / \s + / g, '' )
492+
493+ t . after ( async ( ) => {
494+ await rm ( write , { force : true } )
495+ } )
496+
497+ await writeFile ( write , code )
498+
499+ // A present displayName should not be modified
500+ assert . ok ( code . indexOf ( 'function MemoDisplayName(props: Props)' ) === - 1 )
501+ assert . ok ( code . indexOf ( 'function ReactMemoDisplayName(props: Props)' ) === - 1 )
502+
503+ // Check function expressions
504+ assert . ok (
505+ normalized . indexOf (
506+ `
507+ const FuncExpr = memo(function FuncExpr(props: Props) {
508+ return <p>{props.foo}</p>
509+ })
510+ ` . replace ( / \s + / g, '' ) ,
511+ ) !== - 1 ,
512+ )
513+
514+ // Check function generators
515+ assert . ok (
516+ normalized . indexOf (
517+ `
518+ const GeneratorFuncExpr: FC<Props> = memo(function* GeneratorFuncExpr(props) {
519+ yield <p>foo</p>
520+ }, arePropsEqual)
521+ ` . replace ( / \s + / g, '' ) ,
522+ ) !== - 1 ,
523+ )
524+
525+ const { status : lint } = spawnSync ( 'eslint' , [ write ] , { stdio : 'inherit' } )
526+ assert . equal ( lint , 0 )
527+ const { status : types } = spawnSync (
528+ 'tsc' ,
529+ [ '--noEmit' , '--project' , 'test/tsconfig.json' ] ,
530+ { stdio : 'inherit' } ,
531+ )
532+ assert . equal ( types , 0 )
533+ } )
339534} )
0 commit comments