Repository: TanStack/router (ts)

Issue:
Experimental splitting: Wrapping component in `React.memo()` causes `unexpected splitNode` error
### Describe the bug

Wrapping the `component:` in `React.memo()` causes the following error:
```
[plugin:router-code-splitter-plugin] /home/projects/tanstack-router-5quplm/src/routes/posts.tsx?tsr-split: 
Unexpected splitNode type ☝️: CallExpression
```

This does not work:
```tsx
export const Route = createFileRoute('/posts')({
  component: React.memo(PostsComponent),
});

function PostsComponent() {
  return null
}
```

Omitting the `React.memo()` call does not result in an error:
```tsx
export const Route = createFileRoute('/posts')({
  component: PostsComponent,
});

function PostsComponent() {
  return null
}
```

### Your Example Website or App

https://stackblitz.com/edit/tanstack-router-5quplm?file=src%2Froutes%2Fposts.tsx

### Steps to Reproduce the Bug or Issue

1. Click on **Posts** (or just hover over it)
2. Crashes (see screenshot below)

### Expected behavior

As a user I expected the code would not crash.

### Screenshots or Videos

![CleanShot 2024-06-21 at 00 27 51@2x](https://github.com/TanStack/router/assets/178117/c83f7549-0449-413a-a300-ff652f3f0717)


### Platform

- OS: macOS, Arc 1.47

### Additional context

_No response_

Pull request description:
fix(router-plugin): `experimental.enableCodeSplitting` - allow the use of inlined wrappers for the route options when splitting nodes
It's 4:00 AM here, I really should be sleeping...

fixes #1801 

Patch:
diff --git a/packages/router-plugin/src/compilers.ts b/packages/router-plugin/src/compilers.ts
index c8fc7aaeb..f7c86bd7e 100644
--- a/packages/router-plugin/src/compilers.ts
+++ b/packages/router-plugin/src/compilers.ts
@@ -1,8 +1,9 @@
 import * as t from '@babel/types'
+import babel from '@babel/core'
+import generate from '@babel/generator'
 import * as template from '@babel/template'
 import { splitPrefix } from './constants'
 import { eliminateUnreferencedIdentifiers } from './eliminateUnreferencedIdentifiers'
-import type * as babel from '@babel/core'
 import type { CompileAstFn } from './ast'
 
 type SplitModulesById = Record<
@@ -431,8 +432,42 @@ export async function splitFile(opts: {
                             ),
                           ]),
                         )
+                      } else if (t.isCallExpression(splitNode)) {
+                        const outputSplitNodeCode = generate(splitNode).code
+                        const splitNodeAst = babel.parse(outputSplitNodeCode)
+
+                        if (!splitNodeAst) {
+                          throw new Error(
+                            `Failed to parse the generated code for "${splitType}" in the node type "${splitNode.type}"`,
+                          )
+                        }
+
+                        const statement = splitNodeAst.program.body[0]
+
+                        if (!statement) {
+                          throw new Error(
+                            `Failed to parse the generated code for "${splitType}" in the node type "${splitNode.type}" as no statement was found in the program body`,
+                          )
+                        }
+
+                        if (t.isExpressionStatement(statement)) {
+                          const expression = statement.expression
+                          programPath.pushContainer(
+                            'body',
+                            t.variableDeclaration('const', [
+                              t.variableDeclarator(
+                                t.identifier(splitType),
+                                expression,
+                              ),
+                            ]),
+                          )
+                        } else {
+                          throw new Error(
+                            `Unexpected expression type encounter for "${splitType}" in the node type "${splitNode.type}"`,
+                          )
+                        }
                       } else {
-                        console.info(splitNode)
+                        console.info('Unexpected splitNode type:', splitNode)
                         throw new Error(
                           `Unexpected splitNode type ☝️: ${splitNode.type}`,
                         )


Test patch:
diff --git a/packages/router-plugin/tests/code-splitter/snapshots/destructured-react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/destructured-react-memo-imported-component.tsx
new file mode 100644
index 000000000..446bb1234
--- /dev/null
+++ b/packages/router-plugin/tests/code-splitter/snapshots/destructured-react-memo-imported-component.tsx
@@ -0,0 +1,9 @@
+const $$splitLoaderImporter = () => import('tsr-split:destructured-react-memo-imported-component.tsx?tsr-split');
+import { lazyFn } from '@tanstack/react-router';
+const $$splitComponentImporter = () => import('tsr-split:destructured-react-memo-imported-component.tsx?tsr-split');
+import { lazyRouteComponent } from '@tanstack/react-router';
+import { createFileRoute } from '@tanstack/react-router';
+export const Route = createFileRoute('/')({
+  component: lazyRouteComponent($$splitComponentImporter, 'component'),
+  loader: lazyFn($$splitLoaderImporter, 'loader')
+});
\ No newline at end of file
diff --git a/packages/router-plugin/tests/code-splitter/snapshots/destructured-react-memo-imported-component@split.tsx b/packages/router-plugin/tests/code-splitter/snapshots/destructured-react-memo-imported-component@split.tsx
new file mode 100644
index 000000000..93f0e29a1
--- /dev/null
+++ b/packages/router-plugin/tests/code-splitter/snapshots/destructured-react-memo-imported-component@split.tsx
@@ -0,0 +1,9 @@
+import { memo } from 'react';
+import { importedLoader } from '../shared/imported';
+function Component() {
+  return <div>Component</div>;
+}
+const component = memo(Component);
+export { component };
+const loader = importedLoader;
+export { loader };
\ No newline at end of file
diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react-memo-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react-memo-component.tsx
new file mode 100644
index 000000000..31d5f1f3b
--- /dev/null
+++ b/packages/router-plugin/tests/code-splitter/snapshots/react-memo-component.tsx
@@ -0,0 +1,9 @@
+const $$splitLoaderImporter = () => import('tsr-split:react-memo-component.tsx?tsr-split');
+import { lazyFn } from '@tanstack/react-router';
+const $$splitComponentImporter = () => import('tsr-split:react-memo-component.tsx?tsr-split');
+import { lazyRouteComponent } from '@tanstack/react-router';
+import { createFileRoute } from '@tanstack/react-router';
+export const Route = createFileRoute('/')({
+  component: lazyRouteComponent($$splitComponentImporter, 'component'),
+  loader: lazyFn($$splitLoaderImporter, 'loader')
+});
\ No newline at end of file
diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react-memo-component@split.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react-memo-component@split.tsx
new file mode 100644
index 000000000..99d8095bb
--- /dev/null
+++ b/packages/router-plugin/tests/code-splitter/snapshots/react-memo-component@split.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+import { importedLoader } from '../shared/imported';
+function Component() {
+  return <div>Component</div>;
+}
+const component = React.memo(Component);
+export { component };
+const loader = importedLoader;
+export { loader };
\ No newline at end of file
diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react-memo-imported-component.tsx
new file mode 100644
index 000000000..ea3ecc703
--- /dev/null
+++ b/packages/router-plugin/tests/code-splitter/snapshots/react-memo-imported-component.tsx
@@ -0,0 +1,9 @@
+const $$splitLoaderImporter = () => import('tsr-split:react-memo-imported-component.tsx?tsr-split');
+import { lazyFn } from '@tanstack/react-router';
+const $$splitComponentImporter = () => import('tsr-split:react-memo-imported-component.tsx?tsr-split');
+import { lazyRouteComponent } from '@tanstack/react-router';
+import { createFileRoute } from '@tanstack/react-router';
+export const Route = createFileRoute('/')({
+  component: lazyRouteComponent($$splitComponentImporter, 'component'),
+  loader: lazyFn($$splitLoaderImporter, 'loader')
+});
\ No newline at end of file
diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react-memo-imported-component@split.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react-memo-imported-component@split.tsx
new file mode 100644
index 000000000..57b51bb7b
--- /dev/null
+++ b/packages/router-plugin/tests/code-splitter/snapshots/react-memo-imported-component@split.tsx
@@ -0,0 +1,6 @@
+import React from 'react';
+import { importedLoader, importedComponent } from '../shared/imported';
+const component = React.memo(importedComponent);
+export { component };
+const loader = importedLoader;
+export { loader };
\ No newline at end of file
diff --git a/packages/router-plugin/tests/code-splitter/test-files/destructured-react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/test-files/destructured-react-memo-imported-component.tsx
new file mode 100644
index 000000000..05502585c
--- /dev/null
+++ b/packages/router-plugin/tests/code-splitter/test-files/destructured-react-memo-imported-component.tsx
@@ -0,0 +1,13 @@
+import { memo } from 'react'
+import { createFileRoute } from '@tanstack/react-router'
+
+import { importedLoader } from '../shared/imported'
+
+export const Route = createFileRoute('/')({
+  component: memo(Component),
+  loader: importedLoader,
+})
+
+function Component() {
+  return <div>Component</div>
+}
diff --git a/packages/router-plugin/tests/code-splitter/test-files/react-memo-component.tsx b/packages/router-plugin/tests/code-splitter/test-files/react-memo-component.tsx
new file mode 100644
index 000000000..d2a84dbb5
--- /dev/null
+++ b/packages/router-plugin/tests/code-splitter/test-files/react-memo-component.tsx
@@ -0,0 +1,13 @@
+import React from 'react'
+import { createFileRoute } from '@tanstack/react-router'
+
+import { importedLoader } from '../shared/imported'
+
+export const Route = createFileRoute('/')({
+  component: React.memo(Component),
+  loader: importedLoader,
+})
+
+function Component() {
+  return <div>Component</div>
+}
diff --git a/packages/router-plugin/tests/code-splitter/test-files/react-memo-imported-component.tsx b/packages/router-plugin/tests/code-splitter/test-files/react-memo-imported-component.tsx
new file mode 100644
index 000000000..520eda88f
--- /dev/null
+++ b/packages/router-plugin/tests/code-splitter/test-files/react-memo-imported-component.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+import { createFileRoute } from '@tanstack/react-router'
+
+import { importedLoader, importedComponent } from '../shared/imported'
+
+export const Route = createFileRoute('/')({
+  component: React.memo(importedComponent),
+  loader: importedLoader,
+})