diff --git a/.changeset/neat-impalas-divide.md b/.changeset/neat-impalas-divide.md new file mode 100644 index 00000000000..1af68b5ca4b --- /dev/null +++ b/.changeset/neat-impalas-divide.md @@ -0,0 +1,5 @@ +--- +"@remix-run/dev": major +--- + +Remove `replace-remix-magic-imports` codemod diff --git a/packages/remix-dev/__tests__/codemod-replaceRemixMagicImports-test.ts b/packages/remix-dev/__tests__/codemod-replaceRemixMagicImports-test.ts deleted file mode 100644 index 044f88e819c..00000000000 --- a/packages/remix-dev/__tests__/codemod-replaceRemixMagicImports-test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import NpmCliPackageJson from "@npmcli/package-json"; -import fs from "fs"; -import glob from "fast-glob"; -import path from "path"; -import shell from "shelljs"; -import stripAnsi from "strip-ansi"; - -import { readConfig } from "../config"; -import * as cli from "./utils/cli"; -import * as eol from "./utils/eol"; -import * as git from "./utils/git"; -import withApp from "./utils/withApp"; - -let CODEMOD = "replace-remix-magic-imports"; -let FIXTURE = path.join(__dirname, "fixtures/replace-remix-magic-imports"); - -it("replaces `remix` magic imports", async () => { - await withApp(FIXTURE, async (projectDir) => { - await git.initialCommit(projectDir); - let result = await cli.run(["codemod", CODEMOD, projectDir]); - let stderr = stripAnsi(result.stderr); - expect(result.exitCode).toBe(0); - - let successes = [ - `✔ Found codemod: ${CODEMOD}`, - "✔ No Remix server adapter detected", - "✔ Detected Remix server runtime: node", - "✔ Removed magic `remix` package from dependencies", - "✔ Removed `remix setup` from postinstall script", - "✔ Replaced magic `remix` imports | 24/24 files", - ]; - for (let success of successes) { - expect(stderr).toContain(success); - } - - expect(result.stdout).toContain( - "👉 To update your lockfile, run `yarn install`" - ); - - let pkg = await NpmCliPackageJson.load(projectDir); - let packageJson = pkg.content; - - // check that `remix` dependency was removed - expect(packageJson.dependencies).not.toContain("remix"); - expect(packageJson.devDependencies).not.toContain("remix"); - - // check that Remix packages were standardized - expect(packageJson.dependencies).toEqual( - expect.objectContaining({ - "@remix-run/node": "1.3.4", - "@remix-run/react": "1.3.4", - "@remix-run/serve": "1.3.4", - }) - ); - expect(packageJson.devDependencies).toEqual( - expect.objectContaining({ - "@remix-run/dev": "1.3.4", - }) - ); - - // check that postinstall was removed - expect(packageJson.scripts).not.toContain("postinstall"); - - // check that `from "remix"` magic imports were removed - let config = await readConfig(projectDir); - let files = await glob("**/*.{js,jsx,ts,tsx}", { - cwd: config.appDirectory, - absolute: true, - }); - let remixMagicImports = shell.grep("-l", /from ('remix'|"remix")/, files); - expect(remixMagicImports.code).toBe(0); - expect(remixMagicImports.stdout.trim()).toBe(""); - expect(remixMagicImports.stderr).toBeNull(); - - // check that imports look good for a specific file - let loginRoute = eol.normalize( - fs.readFileSync(path.join(projectDir, "app/routes/login.tsx"), "utf8") - ); - expect(loginRoute).toContain( - [ - "import {", - " type ActionFunction,", - " type LoaderFunction,", - " type MetaFunction,", - " json,", - " redirect,", - '} from "@remix-run/node";', - 'import { Form, Link, useActionData, useSearchParams } from "@remix-run/react";', - ].join("\n") - ); - }); -}); diff --git a/packages/remix-dev/__tests__/codemod-test.ts b/packages/remix-dev/__tests__/codemod-test.ts index 40642e31118..a0abf1129ef 100644 --- a/packages/remix-dev/__tests__/codemod-test.ts +++ b/packages/remix-dev/__tests__/codemod-test.ts @@ -7,7 +7,7 @@ import withApp from "./utils/withApp"; jest.setTimeout(1000 * 20); -let FIXTURE = path.join(__dirname, "fixtures/replace-remix-magic-imports"); +let FIXTURE = path.join(__dirname, "fixtures/indie-stack"); it("checks that project is a clean git repository", async () => { await withApp(FIXTURE, async (projectDir) => { diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/db.server.ts b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/db.server.ts deleted file mode 100644 index 9a34a275c83..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/db.server.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { PrismaClient } from "@prisma/client"; - -let prisma: PrismaClient; - -declare global { - var __db__: PrismaClient; -} - -// this is needed because in development we don't want to restart -// the server with every change, but we want to make sure we don't -// create a new connection to the DB with every change either. -// in production we'll have a single connection to the DB. -if (process.env.NODE_ENV === "production") { - prisma = new PrismaClient(); -} else { - if (!global.__db__) { - global.__db__ = new PrismaClient(); - } - prisma = global.__db__; -} - -export { prisma }; diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/entry.client.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/entry.client.tsx deleted file mode 100644 index ffef95f66db..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/entry.client.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import { RemixBrowser } from "@remix-run/react"; -import { hydrateRoot } from "react-dom/client"; - -hydrateRoot(document, ); diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/entry.server.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/entry.server.tsx deleted file mode 100644 index 91f16ddd846..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/entry.server.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { PassThrough } from "stream"; -import type { AppLoadContext, EntryContext } from "@remix-run/node"; -import { Response } from "@remix-run/node"; -import { RemixServer } from "@remix-run/react"; -import { renderToPipeableStream } from "react-dom/server"; - -const ABORT_DELAY = 5000; - -export default function handleRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext, - loadContext: AppLoadContext -) { - return new Promise((resolve, reject) => { - let { pipe, abort } = renderToPipeableStream( - , - { - onShellReady: () => { - let body = new PassThrough(); - - responseHeaders.set("Content-Type", "text/html"); - - resolve( - new Response(body, { - headers: responseHeaders, - status: responseStatusCode, - }) - ); - - pipe(body); - }, - onShellError(error) { - reject(error); - }, - onError(error) { - responseStatusCode = 500; - console.error(error); - }, - } - ); - - setTimeout(abort, ABORT_DELAY); - }); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/models/note.server.ts b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/models/note.server.ts deleted file mode 100644 index ba56b532eb1..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/models/note.server.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { User, Note } from "@prisma/client"; - -import { prisma } from "~/db.server"; - -export type { Note } from "@prisma/client"; - -export function getNote({ - id, - userId, -}: Pick & { - userId: User["id"]; -}) { - return prisma.note.findFirst({ - where: { id, userId }, - }); -} - -export function getNoteListItems({ userId }: { userId: User["id"] }) { - return prisma.note.findMany({ - where: { userId }, - select: { id: true, title: true }, - orderBy: { updatedAt: "desc" }, - }); -} - -export function createNote({ - body, - title, - userId, -}: Pick & { - userId: User["id"]; -}) { - return prisma.note.create({ - data: { - title, - body, - user: { - connect: { - id: userId, - }, - }, - }, - }); -} - -export function deleteNote({ - id, - userId, -}: Pick & { userId: User["id"] }) { - return prisma.note.deleteMany({ - where: { id, userId }, - }); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/models/post.server.ts b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/models/post.server.ts deleted file mode 100644 index 66dc324f29b..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/models/post.server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { prisma } from "~/db.server"; -import type { Post } from "@prisma/client"; -export type { Post }; - -export async function getPosts() { - return prisma.post.findMany(); -} - -export async function getPost(slug: string) { - return prisma.post.findUnique({ where: { slug } }); -} - -export async function createPost( - post: Pick -) { - return prisma.post.create({ data: post }); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/models/user.server.ts b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/models/user.server.ts deleted file mode 100644 index 645da047587..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/models/user.server.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { Password, User } from "@prisma/client"; -import bcrypt from "@node-rs/bcrypt"; - -import { prisma } from "~/db.server"; - -export type { User } from "@prisma/client"; - -export async function getUserById(id: User["id"]) { - return prisma.user.findUnique({ where: { id } }); -} - -export async function getUserByEmail(email: User["email"]) { - return prisma.user.findUnique({ where: { email } }); -} - -export async function createUser(email: User["email"], password: string) { - const hashedPassword = await bcrypt.hash(password, 10); - - return prisma.user.create({ - data: { - email, - password: { - create: { - hash: hashedPassword, - }, - }, - }, - }); -} - -export async function deleteUserByEmail(email: User["email"]) { - return prisma.user.delete({ where: { email } }); -} - -export async function verifyLogin( - email: User["email"], - password: Password["hash"] -) { - const userWithPassword = await prisma.user.findUnique({ - where: { email }, - include: { - password: true, - }, - }); - - if (!userWithPassword || !userWithPassword.password) { - return null; - } - - const isValid = await bcrypt.verify(password, userWithPassword.password.hash); - - if (!isValid) { - return null; - } - - const { password: _password, ...userWithoutPassword } = userWithPassword; - - return userWithoutPassword; -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/root.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/root.tsx deleted file mode 100644 index b2e3158b02b..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/root.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { - json, - Links, - LiveReload, - Meta, - Outlet, - Scripts, - ScrollRestoration, -} from "remix"; -import type { LinksFunction, MetaFunction, LoaderFunction } from "remix"; - -import tailwindStylesheetUrl from "./styles/tailwind.css"; -import { getUser } from "./session.server"; - -export const links: LinksFunction = () => { - return [{ rel: "stylesheet", href: tailwindStylesheetUrl }]; -}; - -export const meta: MetaFunction = () => ({ - charset: "utf-8", - title: "Remix Notes", - viewport: "width=device-width, initial-scale=1", -}); - -type LoaderData = { - user: Awaited>; -}; - -export const loader: LoaderFunction = async ({ request }) => { - return json({ - user: await getUser(request), - }); -}; - -export default function App() { - return ( - - - - - - - - - - - - - ); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/healthcheck.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/healthcheck.tsx deleted file mode 100644 index 8ca82e9b3ec..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/healthcheck.tsx +++ /dev/null @@ -1,23 +0,0 @@ -// learn more: https://fly.io/docs/reference/configuration/#services-http_checks -import type { LoaderFunction } from "remix"; -import { prisma } from "~/db.server"; - -export const loader: LoaderFunction = async ({ request }) => { - const host = - request.headers.get("X-Forwarded-Host") ?? request.headers.get("host"); - - try { - // if we can connect to the database and make a simple query - // and make a HEAD request to ourselves, then we're good. - await Promise.all([ - prisma.user.count(), - fetch(`http://${host}`, { method: "HEAD" }).then((r) => { - if (!r.ok) return Promise.reject(r); - }), - ]); - return new Response("OK"); - } catch (error: unknown) { - console.log("healthcheck ❌", { error }); - return new Response("ERROR", { status: 500 }); - } -}; diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/index.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/index.tsx deleted file mode 100644 index 2abf6853232..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/index.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { Link } from "remix"; -import { useOptionalUser } from "~/utils"; - -export default function Index() { - const user = useOptionalUser(); - return ( -
-
-
-
-
- Sonic Youth On Stage -
-
-
-

- - Indie Stack - -

-

- Check the README.md file for instructions on how to get this - project deployed. -

-
- {user ? ( - - View Notes for {user.email} - - ) : ( -
- - Sign up - - - Log In - -
- )} -
- - Remix - -
-
-
- -
- - Blog Posts - -
- -
-
- {[ - { - src: "https://user-images.githubusercontent.com/1500684/157764397-ccd8ea10-b8aa-4772-a99b-35de937319e1.svg", - alt: "Fly.io", - href: "https://fly.io", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157764395-137ec949-382c-43bd-a3c0-0cb8cb22e22d.svg", - alt: "SQLite", - href: "https://sqlite.org", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157764484-ad64a21a-d7fb-47e3-8669-ec046da20c1f.svg", - alt: "Prisma", - href: "https://prisma.io", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157764276-a516a239-e377-4a20-b44a-0ac7b65c8c14.svg", - alt: "Tailwind", - href: "https://tailwindcss.com", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157764454-48ac8c71-a2a9-4b5e-b19c-edef8b8953d6.svg", - alt: "Cypress", - href: "https://www.cypress.io", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157772386-75444196-0604-4340-af28-53b236faa182.svg", - alt: "MSW", - href: "https://mswjs.io", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157772447-00fccdce-9d12-46a3-8bb4-fac612cdc949.svg", - alt: "Vitest", - href: "https://vitest.dev", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157772662-92b0dd3a-453f-4d18-b8be-9fa6efde52cf.png", - alt: "Testing Library", - href: "https://testing-library.com", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157772934-ce0a943d-e9d0-40f8-97f3-f464c0811643.svg", - alt: "Prettier", - href: "https://prettier.io", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157772990-3968ff7c-b551-4c55-a25c-046a32709a8e.svg", - alt: "ESLint", - href: "https://eslint.org", - }, - { - src: "https://user-images.githubusercontent.com/1500684/157773063-20a0ed64-b9f8-4e0b-9d1e-0b65a3d4a6db.svg", - alt: "TypeScript", - href: "https://typescriptlang.org", - }, - ].map((img) => ( - - {img.alt} - - ))} -
-
-
-
- ); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/join.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/join.tsx deleted file mode 100644 index ad0ed8014b2..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/join.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import * as React from "react"; -import type { ActionFunction, LoaderFunction, MetaFunction } from "remix"; -import { - Form, - Link, - redirect, - useSearchParams, - json, - useActionData, -} from "remix"; - -import { getUserId, createUserSession } from "~/session.server"; - -import { createUser, getUserByEmail } from "~/models/user.server"; -import { validateEmail } from "~/utils"; - -export const loader: LoaderFunction = async ({ request }) => { - const userId = await getUserId(request); - if (userId) return redirect("/"); - return json({}); -}; - -interface ActionData { - errors: { - email?: string; - password?: string; - }; -} - -export const action: ActionFunction = async ({ request }) => { - const formData = await request.formData(); - const email = formData.get("email"); - const password = formData.get("password"); - const redirectTo = formData.get("redirectTo"); - - if (!validateEmail(email)) { - return json( - { errors: { email: "Email is invalid" } }, - { status: 400 } - ); - } - - if (typeof password !== "string") { - return json( - { errors: { password: "Password is required" } }, - { status: 400 } - ); - } - - if (password.length < 8) { - return json( - { errors: { password: "Password is too short" } }, - { status: 400 } - ); - } - - const existingUser = await getUserByEmail(email); - if (existingUser) { - return json( - { errors: { email: "A user already exists with this email" } }, - { status: 400 } - ); - } - - const user = await createUser(email, password); - - return createUserSession({ - request, - userId: user.id, - remember: false, - redirectTo: typeof redirectTo === "string" ? redirectTo : "/", - }); -}; - -export const meta: MetaFunction = () => { - return { - title: "Sign Up", - }; -}; - -export default function Join() { - const [searchParams] = useSearchParams(); - const redirectTo = searchParams.get("redirectTo") ?? undefined; - const actionData = useActionData() as ActionData; - const emailRef = React.useRef(null); - const passwordRef = React.useRef(null); - - React.useEffect(() => { - if (actionData?.errors?.email) { - emailRef.current?.focus(); - } else if (actionData?.errors?.password) { - passwordRef.current?.focus(); - } - }, [actionData]); - - return ( -
-
-
-
- -
- - {actionData?.errors?.email && ( -
- {actionData.errors.email} -
- )} -
-
- -
- -
- - {actionData?.errors?.password && ( -
- {actionData.errors.password} -
- )} -
-
- - - -
-
- Already have an account?{" "} - - Log in - -
-
-
-
-
- ); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/login.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/login.tsx deleted file mode 100644 index ffd352cf2ca..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/login.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import * as React from "react"; -import type { ActionFunction, LoaderFunction, MetaFunction } from "remix"; -import { - Form, - json, - Link, - useActionData, - redirect, - useSearchParams, -} from "remix"; - -import { createUserSession, getUserId } from "~/session.server"; -import { verifyLogin } from "~/models/user.server"; -import { validateEmail } from "~/utils"; - -export const loader: LoaderFunction = async ({ request }) => { - const userId = await getUserId(request); - if (userId) return redirect("/"); - return json({}); -}; - -interface ActionData { - errors?: { - email?: string; - password?: string; - }; -} - -export const action: ActionFunction = async ({ request }) => { - const formData = await request.formData(); - const email = formData.get("email"); - const password = formData.get("password"); - const redirectTo = formData.get("redirectTo"); - const remember = formData.get("remember"); - - if (!validateEmail(email)) { - return json( - { errors: { email: "Email is invalid" } }, - { status: 400 } - ); - } - - if (typeof password !== "string") { - return json( - { errors: { password: "Password is required" } }, - { status: 400 } - ); - } - - if (password.length < 8) { - return json( - { errors: { password: "Password is too short" } }, - { status: 400 } - ); - } - - const user = await verifyLogin(email, password); - - if (!user) { - return json( - { errors: { email: "Invalid email or password" } }, - { status: 400 } - ); - } - - return createUserSession({ - request, - userId: user.id, - remember: remember === "on" ? true : false, - redirectTo: typeof redirectTo === "string" ? redirectTo : "/notes", - }); -}; - -export const meta: MetaFunction = () => { - return { - title: "Login", - }; -}; - -export default function LoginPage() { - const [searchParams] = useSearchParams(); - const redirectTo = searchParams.get("redirectTo") || "/notes"; - const actionData = useActionData() as ActionData; - const emailRef = React.useRef(null); - const passwordRef = React.useRef(null); - - React.useEffect(() => { - if (actionData?.errors?.email) { - emailRef.current?.focus(); - } else if (actionData?.errors?.password) { - passwordRef.current?.focus(); - } - }, [actionData]); - - return ( -
-
-
-
- -
- - {actionData?.errors?.email && ( -
- {actionData.errors.email} -
- )} -
-
- -
- -
- - {actionData?.errors?.password && ( -
- {actionData.errors.password} -
- )} -
-
- - - -
-
- - -
-
- Don't have an account?{" "} - - Sign up - -
-
-
-
-
- ); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/logout.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/logout.tsx deleted file mode 100644 index 17be85ff7f2..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/logout.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import type { ActionFunction, LoaderFunction } from "remix"; -import { redirect } from "remix"; -import { logout } from "~/session.server"; - -export const action: ActionFunction = async ({ request }) => { - return logout(request); -}; - -export const loader: LoaderFunction = async () => { - return redirect("/"); -}; diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes.tsx deleted file mode 100644 index 8a496a94f60..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { Form, json, useLoaderData, Outlet, Link, NavLink } from "remix"; -import type { LoaderFunction } from "remix"; - -import { requireUserId } from "~/session.server"; -import { useUser } from "~/utils"; -import { getNoteListItems } from "~/models/note.server"; - -type LoaderData = { - noteListItems: Awaited>; -}; - -export const loader: LoaderFunction = async ({ request }) => { - const userId = await requireUserId(request); - const noteListItems = await getNoteListItems({ userId }); - return json({ noteListItems }); -}; - -export default function NotesPage() { - const data = useLoaderData() as LoaderData; - const user = useUser(); - - return ( -
-
-

- Notes -

-

{user.email}

-
- -
-
- -
-
- - + New Note - - -
- - {data.noteListItems.length === 0 ? ( -

No notes yet

- ) : ( -
    - {data.noteListItems.map((note) => ( -
  1. - - `block border-b p-4 text-xl ${isActive ? "bg-white" : ""}` - } - to={note.id} - > - 📝 {note.title} - -
  2. - ))} -
- )} -
- -
- -
-
-
- ); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes/$noteId.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes/$noteId.tsx deleted file mode 100644 index 10b4e65fdd4..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes/$noteId.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import type { LoaderFunction, ActionFunction } from "remix"; -import { redirect } from "remix"; -import { json, useLoaderData, useCatch, Form } from "remix"; -import invariant from "tiny-invariant"; -import type { Note } from "~/models/note.server"; -import { deleteNote } from "~/models/note.server"; -import { getNote } from "~/models/note.server"; -import { requireUserId } from "~/session.server"; - -type LoaderData = { - note: Note; -}; - -export const loader: LoaderFunction = async ({ request, params }) => { - const userId = await requireUserId(request); - invariant(params.noteId, "noteId not found"); - - const note = await getNote({ userId, id: params.noteId }); - if (!note) { - throw new Response("Not Found", { status: 404 }); - } - return json({ note }); -}; - -export const action: ActionFunction = async ({ request, params }) => { - const userId = await requireUserId(request); - invariant(params.noteId, "noteId not found"); - - await deleteNote({ userId, id: params.noteId }); - - return redirect("/notes"); -}; - -export default function NoteDetailsPage() { - const data = useLoaderData() as LoaderData; - - return ( -
-

{data.note.title}

-

{data.note.body}

-
-
- -
-
- ); -} - -export function ErrorBoundary({ error }: { error: Error }) { - console.error(error); - - return
An unexpected error occurred: {error.message}
; -} - -export function CatchBoundary() { - const caught = useCatch(); - - if (caught.status === 404) { - return
Note not found
; - } - - throw new Error(`Unexpected caught response with status: ${caught.status}`); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes/index.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes/index.tsx deleted file mode 100644 index 30df34be502..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Link } from "remix"; - -export default function NoteIndexPage() { - return ( -

- No note selected. Select a note on the left, or{" "} - - create a new note. - -

- ); -} diff --git a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes/new.tsx b/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes/new.tsx deleted file mode 100644 index 8b856d66a35..00000000000 --- a/packages/remix-dev/__tests__/fixtures/replace-remix-magic-imports/app/routes/notes/new.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import * as React from "react"; -import { Form, json, redirect, useActionData } from "remix"; -import type { ActionFunction } from "remix"; -import Alert from "@reach/alert"; - -import { createNote } from "~/models/note.server"; -import { requireUserId } from "~/session.server"; - -type ActionData = { - errors?: { - title?: string; - body?: string; - }; -}; - -export const action: ActionFunction = async ({ request }) => { - const userId = await requireUserId(request); - - const formData = await request.formData(); - const title = formData.get("title"); - const body = formData.get("body"); - - if (typeof title !== "string" || title.length === 0) { - return json( - { errors: { title: "Title is required" } }, - { status: 400 } - ); - } - - if (typeof body !== "string" || body.length === 0) { - return json( - { errors: { body: "Body is required" } }, - { status: 400 } - ); - } - - const note = await createNote({ title, body, userId }); - - return redirect(`/notes/${note.id}`); -}; - -export default function NewNotePage() { - const actionData = useActionData() as ActionData; - const titleRef = React.useRef(null); - const bodyRef = React.useRef(null); - - React.useEffect(() => { - if (actionData?.errors?.title) { - titleRef.current?.focus(); - } else if (actionData?.errors?.body) { - bodyRef.current?.focus(); - } - }, [actionData]); - - return ( -
-
- - {actionData?.errors?.title && ( - - {actionData.errors.title} - - )} -
- -
-