Skip to content

Commit

Permalink
Convert template to typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
paulomarg committed Oct 16, 2023
1 parent 39fc2bd commit e3f0d0d
Show file tree
Hide file tree
Showing 17 changed files with 67 additions and 68 deletions.
File renamed without changes.
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,6 @@ This template uses [Remix](https://remix.run). The following Shopify tools are a
- [Webhooks](https://github.com/Shopify/shopify-app-js/tree/main/packages/shopify-app-remix#authenticating-webhook-requests): Callbacks sent by Shopify when certain events occur
- [Polaris](https://polaris.shopify.com/): Design system that enables apps to create Shopify-like experiences

> **Note**: This template runs on JavaScript, but it's fully set up for [TypeScript](https://www.typescriptlang.org/).
> If you want to create your routes using TypeScript, we recommend removing the `noImplicitAny` config from [`tsconfig.json`](/tsconfig.json)
## Resources

- [Remix Docs](https://remix.run/docs/en/v1)
Expand Down
6 changes: 5 additions & 1 deletion app/db.server.js → app/db.server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { PrismaClient } from "@prisma/client";

const prisma = global.prisma || new PrismaClient();
declare global {
var prisma: PrismaClient;
}

const prisma: PrismaClient = global.prisma || new PrismaClient();

if (process.env.NODE_ENV !== "production") {
if (!global.prisma) {
Expand Down
22 changes: 11 additions & 11 deletions app/entry.server.jsx → app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { PassThrough } from "stream";
import { renderToPipeableStream } from "react-dom/server";
import { RemixServer } from "@remix-run/react";
import {
createReadableStreamFromReadable,
type EntryContext,
} from "@remix-run/node";
import isbot from "isbot";

import { addDocumentResponseHeaders } from "./shopify.server";

const ABORT_DELAY = 5_000;
const ABORT_DELAY = 5000;

export default async function handleRequest(
request,
responseStatusCode,
responseHeaders,
remixContext,
_loadContext
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
addDocumentResponseHeaders(request, responseHeaders);

const callbackName = isbot(request.headers.get("user-agent"))
? "onAllReady"
: "onShellReady";
Expand All @@ -30,16 +31,15 @@ export default async function handleRequest(
{
[callbackName]: () => {
const body = new PassThrough();
const stream = createReadableStreamFromReadable(body);

responseHeaders.set("Content-Type", "text/html");

resolve(
new Response(body, {
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
})
);

pipe(body);
},
onShellError(error) {
Expand Down
File renamed without changes.
9 changes: 4 additions & 5 deletions app/routes/_index/route.jsx → app/routes/_index/route.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import type { LoaderArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { Form, useLoaderData } from "@remix-run/react";

import { login } from "../../shopify.server";

import indexStyles from "./style.css";

export const links = () => [{ rel: "stylesheet", href: indexStyles }];

export async function loader({ request }) {
export const loader = async ({ request }: LoaderArgs) => {
const url = new URL(request.url);

if (url.searchParams.get("shop")) {
throw redirect(`/app?${url.searchParams.toString()}`);
}

return json({ showForm: Boolean(login) });
}
};

export default function App() {
const { showForm } = useLoaderData();
const { showForm } = useLoaderData<typeof loader>();

return (
<div className="index">
Expand Down
15 changes: 5 additions & 10 deletions app/routes/app._index.jsx → app/routes/app._index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect } from "react";
import type { ActionArgs, LoaderArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useActionData, useNavigation, useSubmit } from "@remix-run/react";
import {
Expand All @@ -14,18 +15,16 @@ import {
List,
Link,
} from "@shopify/polaris";

import { authenticate } from "../shopify.server";

export const loader = async ({ request }) => {
export const loader = async ({ request }: LoaderArgs) => {
await authenticate.admin(request);

return null;
};

export async function action({ request }) {
export const action = async ({ request }: ActionArgs) => {
const { admin } = await authenticate.admin(request);

const color = ["Red", "Orange", "Yellow", "Green"][
Math.floor(Math.random() * 4)
];
Expand Down Expand Up @@ -60,22 +59,19 @@ export async function action({ request }) {
},
}
);

const responseJson = await response.json();

return json({
product: responseJson.data.productCreate.product,
});
}
};

export default function Index() {
const nav = useNavigation();
const actionData = useActionData();
const actionData = useActionData<typeof action>();
const submit = useSubmit();

const isLoading =
["loading", "submitting"].includes(nav.state) && nav.formMethod === "POST";

const productId = actionData?.product?.id.replace(
"gid://shopify/Product/",
""
Expand All @@ -86,7 +82,6 @@ export default function Index() {
shopify.toast.show("Product created");
}
}, [productId]);

const generateProduct = () => submit({}, { replace: true, method: "POST" });

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function AdditionalPage() {
);
}

function Code({ children }) {
function Code({ children }: { children: React.ReactNode }) {
return (
<Box
as="span"
Expand Down
8 changes: 4 additions & 4 deletions app/routes/app.jsx → app/routes/app.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import type { HeadersFunction, LoaderArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Link, Outlet, useLoaderData, useRouteError } from "@remix-run/react";
import polarisStyles from "@shopify/polaris/build/esm/styles.css";
import { boundary } from "@shopify/shopify-app-remix/server";
import { AppProvider } from "@shopify/shopify-app-remix/react";

import { authenticate } from "../shopify.server";

export const links = () => [{ rel: "stylesheet", href: polarisStyles }];

export async function loader({ request }) {
export const loader = async ({ request }: LoaderArgs) => {
await authenticate.admin(request);

return json({ apiKey: process.env.SHOPIFY_API_KEY });
}
};

export default function App() {
const { apiKey } = useLoaderData();
Expand All @@ -35,6 +35,6 @@ export function ErrorBoundary() {
return boundary.error(useRouteError());
}

export const headers = (headersArgs) => {
export const headers: HeadersFunction = (headersArgs) => {
return boundary.headers(headersArgs);
};
7 changes: 0 additions & 7 deletions app/routes/auth.$.jsx

This file was deleted.

8 changes: 8 additions & 0 deletions app/routes/auth.$.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { LoaderArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";

export const loader = async ({ request }: LoaderArgs) => {
await authenticate.admin(request);

return null;
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { LoginError } from "@shopify/shopify-app-remix/server";
import { LoginErrorType } from "@shopify/shopify-app-remix/server";

export function loginErrorMessage(loginErrors) {
interface LoginErrorMessage {
shop?: string;
}

export function loginErrorMessage(loginErrors: LoginError): LoginErrorMessage {
if (loginErrors?.shop === LoginErrorType.MissingShop) {
return { shop: "Please enter your shop domain to log in" };
} else if (loginErrors?.shop === LoginErrorType.InvalidShop) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState } from "react";
import type { ActionArgs, LoaderArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import {
AppProvider as PolarisAppProvider,
Expand All @@ -9,41 +10,38 @@ import {
Text,
TextField,
} from "@shopify/polaris";

import { Form, useActionData, useLoaderData } from "@remix-run/react";
import polarisStyles from "@shopify/polaris/build/esm/styles.css";

import { login } from "../../shopify.server";
import { loginErrorMessage } from "./error.server";

export const links = () => [{ rel: "stylesheet", href: polarisStyles }];

export async function loader({ request }) {
export const loader = async ({ request }: LoaderArgs) => {
const errors = loginErrorMessage(await login(request));

return json({
errors,
polarisTranslations: require(`@shopify/polaris/locales/en.json`),
});
}
};

export async function action({ request }) {
export const action = async ({ request }: ActionArgs) => {
const errors = loginErrorMessage(await login(request));

return json({
errors,
});
}
};

export default function Auth() {
const { polarisTranslations } = useLoaderData();
const loaderData = useLoaderData();
const actionData = useActionData();
const loaderData = useLoaderData<typeof loader>();
const actionData = useActionData<typeof action>();
const [shop, setShop] = useState("");
const { errors } = actionData || loaderData;

return (
<PolarisAppProvider i18n={polarisTranslations}>
<PolarisAppProvider i18n={loaderData.polarisTranslations}>
<Page>
<Card>
<Form method="post">
Expand Down
4 changes: 3 additions & 1 deletion app/routes/webhooks.jsx → app/routes/webhooks.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { ActionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import db from "../db.server";

export const action = async ({ request }) => {
export const action = async ({ request }: ActionArgs) => {
const { topic, shop, session, admin, payload } = await authenticate.webhook(
request
);
Expand All @@ -15,6 +16,7 @@ export const action = async ({ request }) => {
if (session) {
await db.session.deleteMany({ where: { shop } });
}

break;
case "CUSTOMERS_DATA_REQUEST":
case "CUSTOMERS_REDACT":
Expand Down
1 change: 0 additions & 1 deletion app/shopify.server.js → app/shopify.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
} from "@shopify/shopify-app-remix/server";
import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
import { restResources } from "@shopify/shopify-api/rest/admin/2023-07";

import prisma from "./db.server";

const shopify = shopifyApp({
Expand Down
1 change: 1 addition & 0 deletions globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module "*.css";
24 changes: 11 additions & 13 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
{
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"],
"include": ["**/*.ts", "**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"strict": true,
"skipLibCheck": true,
"isolatedModules": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"removeComments": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"jsx": "react-jsx",
"module": "node16",
"moduleResolution": "node16",
"resolveJsonModule": true,
"module": "Node16",
"moduleResolution": "Node16",
"target": "ES2019",
"strict": true,
"allowJs": true,
"checkJs": true,
"noImplicitAny": false,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},
"types": [
"@shopify/app-bridge-types"
],
"noEmit": true
"node", "@shopify/app-bridge-types"
]
}
}

0 comments on commit e3f0d0d

Please sign in to comment.