Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add react-router SDK #4621

Merged
merged 53 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
f414fcc
add modified react-router playground app
LekoArts Nov 21, 2024
cc77f7d
add initial (unmodified) react-router pkg
LekoArts Nov 21, 2024
b0df0a1
get playground working
LekoArts Nov 21, 2024
7fbc46a
more playground updates
LekoArts Nov 21, 2024
bcd5231
run codemod on playground for react-router
LekoArts Nov 21, 2024
349b892
use new package
LekoArts Nov 21, 2024
4540b05
update imports
LekoArts Nov 22, 2024
4e3a4df
fix(react-router): Update playground & pkg (#4628)
wobsoriano Nov 22, 2024
f538061
remove workspace usage used for debugging
LekoArts Nov 22, 2024
dfb3807
add name
LekoArts Nov 22, 2024
dcc22e4
add changeset
LekoArts Nov 22, 2024
91c2a5f
remove unused files
LekoArts Nov 22, 2024
de98050
remove unused script
LekoArts Nov 22, 2024
74fabec
make pkg ESM-only
LekoArts Nov 22, 2024
ead6304
update some remix => react-router instances
LekoArts Nov 22, 2024
2c53767
fix build artifacts
LekoArts Nov 22, 2024
885a9b1
update eslint
LekoArts Nov 22, 2024
bd67155
fix routerData
LekoArts Nov 22, 2024
fc2ddc3
use declarative routes instead of fs
LekoArts Nov 22, 2024
2b9d829
fix types
LekoArts Nov 22, 2024
ef306a6
update packages
LekoArts Nov 25, 2024
94e4944
Merge branch 'main' into lekoarts/eco-247-bootstrap-new-package
LekoArts Nov 25, 2024
a033b66
update tsup config
LekoArts Nov 25, 2024
62b7c8e
Use ClerkProvider instead of HoC
LekoArts Nov 25, 2024
57c6675
maybe now correct tsup config?
LekoArts Nov 25, 2024
e5a070c
cleanup
LekoArts Nov 26, 2024
b8b615c
yey, RR fix
LekoArts Dec 3, 2024
13fa112
Merge branch 'main' into lekoarts/eco-247-bootstrap-new-package
LekoArts Dec 3, 2024
700f48e
improve types
LekoArts Dec 3, 2024
a3703f9
Merge branch 'main' into lekoarts/eco-247-bootstrap-new-package
LekoArts Dec 4, 2024
0fd2a70
fix attw linting
LekoArts Dec 4, 2024
efb7f0b
add recent remix improvements
LekoArts Dec 4, 2024
7cd4cf5
update README
LekoArts Dec 4, 2024
eebfcdd
update types, access __reactRouterContext
LekoArts Dec 4, 2024
b9407c1
update isSpaMode
LekoArts Dec 4, 2024
bb6953d
update changelog
LekoArts Dec 4, 2024
2fa5342
update renovate
LekoArts Dec 4, 2024
c368713
update renovate
LekoArts Dec 4, 2024
d7b5253
update renovate workflow
LekoArts Dec 4, 2024
e4fdd0e
update renovate workflow
LekoArts Dec 4, 2024
de53583
try stuff
LekoArts Dec 4, 2024
488d407
update changeset
LekoArts Dec 5, 2024
295ed51
updates
LekoArts Dec 5, 2024
d56e76d
add new playground
LekoArts Dec 5, 2024
106741d
add basic E2E test
LekoArts Dec 9, 2024
91dca0f
inSpaMode -> isSpaMode
LekoArts Dec 9, 2024
5a1b747
add turbo task
LekoArts Dec 9, 2024
01bd359
Merge branch 'main' into lekoarts/eco-247-bootstrap-new-package
LekoArts Dec 9, 2024
622af80
add PORT env var
LekoArts Dec 9, 2024
765213c
use VITE_ prefix
LekoArts Dec 9, 2024
969f0cb
split up utils/utils file
LekoArts Dec 9, 2024
d9a35ef
make "isSpaMode" usage more robust
LekoArts Dec 9, 2024
12ddee6
skip e2e tests for now
LekoArts Dec 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/clean-pigs-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@clerk/react-router': patch
---

Initial beta release of `@clerk/react-router`.

[React Router v7](https://remix.run/blog/react-router-v7) was released and Clerk's existing `@clerk/remix` SDK isn't compatible anymore. Thus the need for a brand new SDK came up. `@clerk/react-router` allows you to use React Router v7 + Clerk both in framework/library mode.

Read the [React Router quickstart](https://clerk.com/docs/quickstarts/react-router) and [reference documenation](https://clerk.com/docs/references/react-router/overview) to learn more.
2 changes: 1 addition & 1 deletion .github/workflows/validate-renovate-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ jobs:
- name: Validate Renovate Config
env:
RENOVATE_VERSION: 37.440.7 # Last version compatible with Node 18
run: pnpm dlx renovate@${{ env.RENOVATE_VERSION }} renovate-config-validator
run: npx --yes --package renovate@${{ env.RENOVATE_VERSION }} renovate-config-validator
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous version didn't work so I just reverted the pnpm change here

32 changes: 32 additions & 0 deletions integration/.keys.json.sample
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,37 @@
"core-2-all-enabled": {
"pk": "",
"sk": ""
},
"sessions-prod-1": {
"pk": "",
"sk": ""
},
"sessions-dev-1": {
"pk": "",
"sk": ""
},
"sessions-prod-2": {
"pk": "",
"sk": ""
},
"sessions-dev-2": {
"pk": "",
"sk": ""
},
"with-restricted-mode": {
"pk": "",
"sk": ""
},
"with-reverification": {
"pk": "",
"sk": ""
},
"with-legal-consent": {
"pk": "",
"sk": ""
},
"with-waitlist-mode": {
"pk": "",
"sk": ""
}
}
2 changes: 2 additions & 0 deletions integration/presets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createLongRunningApps } from './longRunningApps';
import { next } from './next';
import { nuxt } from './nuxt';
import { react } from './react';
import { reactRouter } from './react-router';
import { remix } from './remix';
import { tanstack } from './tanstack';

Expand All @@ -22,6 +23,7 @@ export const appConfigs = {
astro,
tanstack,
nuxt,
reactRouter,
secrets: {
instanceKeys,
},
Expand Down
2 changes: 2 additions & 0 deletions integration/presets/longRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { express } from './express';
import { next } from './next';
import { nuxt } from './nuxt';
import { react } from './react';
import { reactRouter } from './react-router';
import { remix } from './remix';
import { tanstack } from './tanstack';
import { vue } from './vue';
Expand Down Expand Up @@ -42,6 +43,7 @@ export const createLongRunningApps = () => {
{ id: 'tanstack.router', config: tanstack.router, env: envs.withEmailCodes },
{ id: 'vue.vite', config: vue.vite, env: envs.withCustomRoles },
{ id: 'nuxt.node', config: nuxt.node, env: envs.withCustomRoles },
{ id: 'react-router.node', config: reactRouter.reactRouterNode, env: envs.withEmailCodes },
] as const;

const apps = configs.map(longRunningApplication);
Expand Down
17 changes: 17 additions & 0 deletions integration/presets/react-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { constants } from '../constants';
import { applicationConfig } from '../models/applicationConfig.js';
import { templates } from '../templates/index.js';

const reactRouterNode = applicationConfig()
.setName('react-router-node')
.useTemplate(templates['react-router-node'])
.setEnvFormatter('public', key => `VITE_${key}`)
.addScript('setup', 'pnpm install')
.addScript('dev', 'pnpm dev')
.addScript('build', 'pnpm build')
.addScript('serve', 'pnpm start')
.addDependency('@clerk/react-router', constants.E2E_CLERK_VERSION || '*');

export const reactRouter = {
reactRouterNode,
} as const;
1 change: 1 addition & 0 deletions integration/templates/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const templates = {
'tanstack-router': resolve(__dirname, './tanstack-router'),
'vue-vite': resolve(__dirname, './vue-vite'),
'nuxt-node': resolve(__dirname, './nuxt-node'),
'react-router-node': resolve(__dirname, './react-router-node'),
} as const;

if (new Set([...Object.values(templates)]).size !== Object.values(templates).length) {
Expand Down
8 changes: 8 additions & 0 deletions integration/templates/react-router-node/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.env
!.env.example
.DS_Store
.react-router
build
node_modules
*.tsbuildinfo
.seccorc
3 changes: 3 additions & 0 deletions integration/templates/react-router-node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# react-router

Example playground for React Router (Framework mode) and Clerk.
66 changes: 66 additions & 0 deletions integration/templates/react-router-node/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router';
import { rootAuthLoader } from '@clerk/react-router/ssr.server';
import { ClerkProvider } from '@clerk/react-router';

import type { Route } from './+types/root';

export async function loader(args: Route.LoaderArgs) {
return rootAuthLoader(args);
}

export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang='en'>
<head>
<meta charSet='utf-8' />
<meta
name='viewport'
content='width=device-width, initial-scale=1'
/>
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}

export default function App({ loaderData }: Route.ComponentProps) {
return (
<ClerkProvider loaderData={loaderData}>
<main>
<Outlet />
</main>
</ClerkProvider>
);
}

export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
let message = 'Oops!';
let details = 'An unexpected error occurred.';
let stack: string | undefined;

if (isRouteErrorResponse(error)) {
message = error.status === 404 ? '404' : 'Error';
details = error.status === 404 ? 'The requested page could not be found.' : error.statusText || details;
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message;
stack = error.stack;
}

return (
<main>
<h1>{message}</h1>
<p>{details}</p>
{stack && (
<pre>
<code>{stack}</code>
</pre>
)}
</main>
);
}
8 changes: 8 additions & 0 deletions integration/templates/react-router-node/app/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { type RouteConfig, index, route } from '@react-router/dev/routes';

export default [
index('routes/home.tsx'),
route('sign-in/*', 'routes/sign-in.tsx'),
route('sign-up/*', 'routes/sign-up.tsx'),
route('protected', 'routes/protected.tsx'),
] satisfies RouteConfig;
16 changes: 16 additions & 0 deletions integration/templates/react-router-node/app/routes/home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { SignedIn, SignedOut, UserButton } from '@clerk/react-router';
import type { Route } from './+types/home';

export function meta({}: Route.MetaArgs) {
return [{ title: 'New React Router App' }, { name: 'description', content: 'Welcome to React Router!' }];
}

export default function Home() {
return (
<div>
<UserButton />
<SignedIn>SignedIn</SignedIn>
<SignedOut>SignedOut</SignedOut>
</div>
);
}
21 changes: 21 additions & 0 deletions integration/templates/react-router-node/app/routes/protected.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { redirect } from 'react-router';
import { getAuth } from '@clerk/react-router/ssr.server';
import type { Route } from './+types/profile';

export async function loader(args: Route.LoaderArgs) {
const { userId } = await getAuth(args);

if (!userId) {
return redirect('/sign-in?redirect_url=' + args.request.url);
}

return {};
}

export default function Profile(args: Route.ComponentProps) {
return (
<div>
<h1>Protected</h1>
</div>
);
}
14 changes: 14 additions & 0 deletions integration/templates/react-router-node/app/routes/sign-in.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { SignIn } from '@clerk/react-router';

export default function SignInPage() {
return (
<div>
<h1>Sign In route</h1>
<SignIn
routing={'path'}
path={'/sign-in'}
signUpUrl={'/sign-up'}
/>
</div>
);
}
14 changes: 14 additions & 0 deletions integration/templates/react-router-node/app/routes/sign-up.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { SignUp } from '@clerk/react-router';

export default function SignUpPage() {
return (
<div>
<h1>Sign Up route</h1>
<SignUp
routing={'path'}
path={'/sign-up'}
signInUrl={'/sign-in'}
/>
</div>
);
}
29 changes: 29 additions & 0 deletions integration/templates/react-router-node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "clerk-react-router-quickstart",
"private": true,
"type": "module",
"scripts": {
"build": "react-router build",
"dev": "react-router dev --port $PORT",
"start": "react-router-serve ./build/server/index.js",
"typecheck": "react-router typegen && tsc --build --noEmit"
},
"dependencies": {
"@clerk/react-router": "latest",
"@react-router/node": "^7.0.2",
"@react-router/serve": "^7.0.2",
"isbot": "^5.1.17",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router": "^7.0.2"
},
"devDependencies": {
"@react-router/dev": "^7.0.2",
"@types/node": "^20",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"typescript": "^5.6.3",
"vite": "^5.4.11",
"vite-tsconfig-paths": "^5.1.2"
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { Config } from '@react-router/dev/config';

export default {
// Config options...
// Server-side render by default, to enable SPA mode set this to `false`
ssr: true,
} satisfies Config;
23 changes: 23 additions & 0 deletions integration/templates/react-router-node/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"include": ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["node", "vite/client"],
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"rootDirs": [".", "./.react-router/types"],
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"noEmit": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true
}
}
15 changes: 15 additions & 0 deletions integration/templates/react-router-node/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { reactRouter } from '@react-router/dev/vite';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
plugins: [
reactRouter(),
tsconfigPaths({
projects: ['./tsconfig.json'],
}),
],
server: {
port: Number(process.env.PORT),
},
});
Loading
Loading