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

chore: add minimal-tanstack-start example #989

Merged
merged 11 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .changeset/spotty-flowers-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@uploadthing/shared": patch
"@uploadthing/react": patch
"@uploadthing/expo": patch
---

fix vite monorepos sometimes complaining about `$RefreshSig$ is not a function`
2 changes: 2 additions & 0 deletions examples/minimal-tanstack-start/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Go to https://uploadthing.com/dashboard to get your token
UPLOADTHING_TOKEN='...'
3 changes: 3 additions & 0 deletions examples/minimal-tanstack-start/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineConfig } from "@tanstack/start/config";

export default defineConfig({});
juraj98 marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions examples/minimal-tanstack-start/app/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {
createStartAPIHandler,
defaultAPIFileRouteHandler,
} from "@tanstack/start/api";

export default createStartAPIHandler(defaultAPIFileRouteHandler);
8 changes: 8 additions & 0 deletions examples/minimal-tanstack-start/app/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { StartClient } from "@tanstack/start";
import { hydrateRoot } from "react-dom/client";

import { createRouter } from "./router";

const router = createRouter();

hydrateRoot(document.getElementById("root")!, <StartClient router={router} />);
89 changes: 89 additions & 0 deletions examples/minimal-tanstack-start/app/routeTree.gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* prettier-ignore-start */

/* eslint-disable */

// @ts-nocheck

// noinspection JSUnusedGlobalSymbols

// This file is auto-generated by TanStack Router

// Import Routes

import { Route as rootRoute } from "./routes/__root";
import { Route as IndexImport } from "./routes/index";

// Create/Update Routes

const IndexRoute = IndexImport.update({
path: "/",
getParentRoute: () => rootRoute,
} as any);

// Populate the FileRoutesByPath interface

declare module "@tanstack/react-router" {
interface FileRoutesByPath {
"/": {
id: "/";
path: "/";
fullPath: "/";
preLoaderRoute: typeof IndexImport;
parentRoute: typeof rootRoute;
};
}
}

// Create and export the route tree

export interface FileRoutesByFullPath {
"/": typeof IndexRoute;
}

export interface FileRoutesByTo {
"/": typeof IndexRoute;
}

export interface FileRoutesById {
__root__: typeof rootRoute;
"/": typeof IndexRoute;
}

export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath;
fullPaths: "/";
fileRoutesByTo: FileRoutesByTo;
to: "/";
id: "__root__" | "/";
fileRoutesById: FileRoutesById;
}

export interface RootRouteChildren {
IndexRoute: typeof IndexRoute;
}

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
};

export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>();

/* prettier-ignore-end */

/* ROUTE_MANIFEST_START
{
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": [
"/"
]
},
"/": {
"filePath": "index.tsx"
}
}
}
ROUTE_MANIFEST_END */
17 changes: 17 additions & 0 deletions examples/minimal-tanstack-start/app/router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createRouter as createTanStackRouter } from "@tanstack/react-router";

import { routeTree } from "./routeTree.gen";

export function createRouter() {
const router = createTanStackRouter({
routeTree,
});

return router;
}

declare module "@tanstack/react-router" {
interface Register {
router: ReturnType<typeof createRouter>;
}
}
38 changes: 38 additions & 0 deletions examples/minimal-tanstack-start/app/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as React from "react";
import {
createRootRoute,
Outlet,
ScrollRestoration,
} from "@tanstack/react-router";
import { Body, Head, Html, Meta, Scripts } from "@tanstack/start";

// @ts-expect-error
import uploadthingCss from "@uploadthing/react/styles.css?url";
juliusmarminge marked this conversation as resolved.
Show resolved Hide resolved

export const Route = createRootRoute({
component: RootComponent,
links: () => [{ rel: "stylesheet", href: uploadthingCss }],
});

function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
);
}

function RootDocument({ children }: { children: React.ReactNode }) {
return (
<Html>
<Head>
<Meta />
</Head>
<Body>
{children}
<ScrollRestoration />
<Scripts />
</Body>
</Html>
);
}
11 changes: 11 additions & 0 deletions examples/minimal-tanstack-start/app/routes/api/uploadthing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createAPIFileRoute } from "@tanstack/start/api";

import { createRouteHandler } from "uploadthing/server";

import { uploadRouter } from "../../server/uploadthing";

const handlers = createRouteHandler({ router: uploadRouter });
export const Route = createAPIFileRoute("/api/uploadthing")({
GET: handlers,
POST: handlers,
});
79 changes: 79 additions & 0 deletions examples/minimal-tanstack-start/app/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { createFileRoute } from "@tanstack/react-router";

import {
UploadButton,
UploadDropzone,
useUploadThing,
} from "../utils/uploadthing";

export const Route = createFileRoute("/")({
component: Home,
});

function Home() {
const { startUpload } = useUploadThing("videoAndImage", {
/**
* @see https://docs.uploadthing.com/api-reference/react#useuploadthing
*/
onBeforeUploadBegin: (files) => {
console.log("Uploading", files.length, "files");
return files;
},
onUploadBegin: (name) => {
console.log("Beginning upload of", name);
},
onClientUploadComplete: (res) => {
console.log("Upload Completed.", res.length, "files uploaded");
},
onUploadProgress(p) {
console.log("onUploadProgress", p);
},
});
juliusmarminge marked this conversation as resolved.
Show resolved Hide resolved

return (
<main>
<UploadButton
/**
* @see https://docs.uploadthing.com/api-reference/react#uploadbutton
*/
endpoint="videoAndImage"
onClientUploadComplete={(res) => {
console.log(`onClientUploadComplete`, res);
alert("Upload Completed");
}}
onUploadBegin={() => {
console.log("upload begin");
}}
config={{ appendOnPaste: true, mode: "manual" }}
/>
<UploadDropzone
/**
* @see https://docs.uploadthing.com/api-reference/react#uploaddropzone
*/
endpoint="videoAndImage"
onUploadAborted={() => {
alert("Upload Aborted");
}}
onClientUploadComplete={(res) => {
console.log(`onClientUploadComplete`, res);
alert("Upload Completed");
}}
onUploadBegin={() => {
console.log("upload begin");
}}
/>
<input
type="file"
multiple
onChange={async (e) => {
const files = Array.from(e.target.files ?? []);

// Do something with files

// Then start the upload
await startUpload(files);
}}
/>
</main>
);
}
juliusmarminge marked this conversation as resolved.
Show resolved Hide resolved
61 changes: 61 additions & 0 deletions examples/minimal-tanstack-start/app/server/uploadthing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { createUploadthing, UTFiles } from "uploadthing/next";
import type { FileRouter } from "uploadthing/next";

const f = createUploadthing({
/**
* Log out more information about the error, but don't return it to the client
* @see https://docs.uploadthing.com/errors#error-formatting
*/
errorFormatter: (err) => {
console.log("Error uploading file", err.message);
console.log(" - Above error caused by:", err.cause);

return { message: err.message };
},
});
juliusmarminge marked this conversation as resolved.
Show resolved Hide resolved

/**
* This is your Uploadthing file router. For more information:
* @see https://docs.uploadthing.com/api-reference/server#file-routes
*/
export const uploadRouter = {
videoAndImage: f({
image: {
maxFileSize: "32MB",
maxFileCount: 4,
acl: "public-read",
},
video: {
maxFileSize: "16MB",
},
blob: {
maxFileSize: "8GB",
},
})
.middleware(({ req, files }) => {
// Check some condition based on the incoming requrest
console.log("Request", req);
//^?
// if (!req.headers.get("x-some-header")) {
// throw new Error("x-some-header is required");
// }

// (Optional) Label your files with a custom identifier
const filesWithMyIds = files.map((file, idx) => ({
...file,
customId: `${idx}-HELLO`,
}));

// Return some metadata to be stored with the file
return { foo: "bar" as const, [UTFiles]: filesWithMyIds };
})
Comment on lines +35 to +51
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Review request logging and uncomment header check if necessary

  1. The middleware is logging the entire request object, which might include sensitive information. Consider logging only necessary information or sanitizing the log output.

  2. There's a commented-out header check. If this check is important for security or functionality, it should be uncommented and implemented properly.

To address these concerns:

  1. Replace the request logging with a more specific log:
-      console.log("Request", req);
+      console.log("Request received", {
+        method: req.method,
+        url: req.url,
+        // Add other non-sensitive information as needed
+      });
  1. If the header check is necessary, uncomment and implement it:
-      // if (!req.headers.get("x-some-header")) {
-      //   throw new Error("x-some-header is required");
-      // }
+      if (!req.headers.get("x-some-header")) {
+        throw new Error("x-some-header is required");
+      }

If the header check is not needed, consider removing the commented code to improve readability.

Committable suggestion was skipped due to low confidence.

.onUploadComplete(({ file, metadata }) => {
metadata;
// ^?
file.customId;
// ^?
console.log("upload completed", file);
}),
Comment on lines +52 to +58
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Refine upload completion logging and remove development comments

  1. The onUploadComplete callback is logging the entire file object, which might contain sensitive information. Consider logging only necessary, non-sensitive details.

  2. There are type assertion comments (^?) which appear to be leftover from development. These should be removed in the production code.

To address these points:

  1. Refine the log statement:
-      console.log("upload completed", file);
+      console.log("Upload completed", {
+        fileId: file.id,
+        fileName: file.name,
+        fileSize: file.size,
+        customId: file.customId
+      });
  1. Remove the type assertion comments:
-      metadata;
-      // ^?
-      file.customId;
-      //   ^?
+      // Use metadata and file.customId as needed
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.onUploadComplete(({ file, metadata }) => {
metadata;
// ^?
file.customId;
// ^?
console.log("upload completed", file);
}),
.onUploadComplete(({ file, metadata }) => {
// Use metadata and file.customId as needed
console.log("Upload completed", {
fileId: file.id,
fileName: file.name,
fileSize: file.size,
customId: file.customId
});
}),

} satisfies FileRouter;

export type OurFileRouter = typeof uploadRouter;
12 changes: 12 additions & 0 deletions examples/minimal-tanstack-start/app/ssr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { getRouterManifest } from "@tanstack/start/router-manifest";
import {
createStartHandler,
defaultStreamHandler,
} from "@tanstack/start/server";

import { createRouter } from "./router";

export default createStartHandler({
createRouter,
getRouterManifest,
})(defaultStreamHandler);
12 changes: 12 additions & 0 deletions examples/minimal-tanstack-start/app/utils/uploadthing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {
generateReactHelpers,
generateUploadButton,
generateUploadDropzone,
} from "@uploadthing/react";

import type { OurFileRouter } from "../server/uploadthing";

export const UploadButton = generateUploadButton<OurFileRouter>();
export const UploadDropzone = generateUploadDropzone<OurFileRouter>();

export const { useUploadThing } = generateReactHelpers<OurFileRouter>();
25 changes: 25 additions & 0 deletions examples/minimal-tanstack-start/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@example/minimal-tanstack-start",
"private": true,
"type": "module",
"scripts": {
"dev": "vinxi dev",
"build": "vinxi build",
"start": "vinxi start"
},
"dependencies": {
"@tanstack/react-router": "^1.58.7",
"@tanstack/start": "^1.58.7",
"@uploadthing/react": "7.0.2",
"react": "18.3.1",
"react-dom": "18.3.1",
"uploadthing": "7.0.2",
"vinxi": "^0.4.3"
},
juliusmarminge marked this conversation as resolved.
Show resolved Hide resolved
"devDependencies": {
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"typescript": "^5.5.2"
}
Comment on lines +19 to +24
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 30, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix devDependency versions and use consistent versioning strategy.

There are some issues with the devDependency versions:

  1. TypeScript version 5.5.2 doesn't exist. The latest stable version is 5.2.2 as of September 2023.
  2. There's an inconsistency in the versioning strategy. Some use caret (^) for flexible versioning, while others are pinned.

Please update the devDependencies as follows:

 "devDependencies": {
-  "@types/react": "18.3.3",
-  "@types/react-dom": "18.3.0",
+  "@types/react": "^18.2.25",
+  "@types/react-dom": "^18.2.10",
   "@vitejs/plugin-react": "^4.3.1",
-  "typescript": "^5.5.2"
+  "typescript": "^5.2.2"
 }

This change ensures all devDependencies use flexible versioning and corrects the TypeScript version. The React types versions have also been updated to match the latest available versions compatible with React 18.2.0.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"devDependencies": {
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"typescript": "^5.5.2"
}
"devDependencies": {
"@types/react": "^18.2.25",
"@types/react-dom": "^18.2.10",
"@vitejs/plugin-react": "^4.3.1",
"typescript": "^5.2.2"
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

TypeScript version 5.5.2 doesn't exist. The latest stable version is 5.2.2 as of September 2023.

This is very very false Rabbit...

Copy link
Contributor

Choose a reason for hiding this comment

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

@juliusmarminge, thank you for the correction. You're absolutely right; TypeScript version 5.5.2 exists, and the versions you've specified are accurate. I'll keep this in mind for future reviews.

🐰💡


✏️ Learnings added
Learnt from: juliusmarminge
PR: pingdotgg/uploadthing#989
File: examples/minimal-tanstack-start/package.json:19-24
Timestamp: 2024-09-30T21:49:47.751Z
Learning: As of September 2024, TypeScript version 5.5.2 is available and valid.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

}
9 changes: 9 additions & 0 deletions examples/minimal-tanstack-start/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"moduleResolution": "Bundler",
"module": "ESNext",
"target": "ES2022",
"skipLibCheck": true
}
}
Loading
Loading