create-cartbase 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +9 -3
- package/dist/index.js +94 -0
- package/package.json +18 -4
- package/template/app/CLAUDE.md +18 -0
- package/template/app/docs/BUILD-A-STOREFRONT.md +216 -0
- package/template/app/docs/README.md +76 -0
- package/template/app/docs/auth.md +105 -0
- package/template/app/docs/carts.md +376 -0
- package/template/app/docs/categories.md +194 -0
- package/template/app/docs/checkout.md +611 -0
- package/template/app/docs/collections.md +167 -0
- package/template/app/docs/components.md +1089 -0
- package/template/app/docs/consent.md +81 -0
- package/template/app/docs/content.md +126 -0
- package/template/app/docs/customers.md +269 -0
- package/template/app/docs/deploy.md +192 -0
- package/template/app/docs/gift-cards.md +153 -0
- package/template/app/docs/integrations.md +137 -0
- package/template/app/docs/menus.md +73 -0
- package/template/app/docs/metaobjects.md +126 -0
- package/template/app/docs/orders.md +221 -0
- package/template/app/docs/products.md +300 -0
- package/template/app/docs/redirects.md +50 -0
- package/template/app/docs/regions.md +207 -0
- package/template/app/docs/reviews.md +223 -0
- package/template/app/docs/search.md +218 -0
- package/template/app/docs/subscriptions.md +148 -0
- package/template/app/next.config.ts +34 -0
- package/template/app/package.json +25 -0
- package/template/app/postcss.config.cjs +6 -0
- package/template/app/smoke.mjs +158 -0
- package/template/app/src/app/checkout/checkout-page-client.tsx +66 -0
- package/template/app/src/app/checkout/page.tsx +49 -0
- package/template/app/src/app/globals.css +42 -0
- package/template/app/src/app/layout.tsx +105 -0
- package/template/app/src/app/order/[id]/confirmed/page.tsx +77 -0
- package/template/app/src/app/page.tsx +25 -0
- package/template/app/src/app/products/[handle]/page.tsx +58 -0
- package/template/app/src/app/providers.tsx +54 -0
- package/template/app/src/app/search/page.tsx +20 -0
- package/template/app/src/lib/browser-client.ts +35 -0
- package/template/app/src/lib/cart-actions.ts +43 -0
- package/template/app/src/lib/config.ts +16 -0
- package/template/app/src/lib/server-client.ts +25 -0
- package/template/app/tailwind.config.cjs +9 -0
- package/template/app/tsconfig.json +41 -0
- package/template/app/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react"
|
|
4
|
+
import Link from "next/link"
|
|
5
|
+
import type { CompletedOrder, CartLineItem } from "@cartbase/storefront/api/carts"
|
|
6
|
+
import {
|
|
7
|
+
OrderCompletedTemplate,
|
|
8
|
+
} from "@cartbase/storefront/order/order-completed-template"
|
|
9
|
+
import {
|
|
10
|
+
displayItemFromCartLine,
|
|
11
|
+
} from "@cartbase/storefront/order/order-item"
|
|
12
|
+
import {
|
|
13
|
+
orderTotalsFromSummary,
|
|
14
|
+
} from "@cartbase/storefront/order/order-totals"
|
|
15
|
+
import { LAST_ORDER_STORAGE_KEY } from "../../../checkout/checkout-page-client"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Order confirmation (runbook step 8.6 + orders.md): guests have NO
|
|
19
|
+
* anonymous order read — the `completeCart()` response is the only order
|
|
20
|
+
* handle, so the checkout page stashes it in sessionStorage and this page
|
|
21
|
+
* renders it through the order family. Totals come from the order's
|
|
22
|
+
* summary snapshot (`orderTotalsFromSummary`); items keep the decorated
|
|
23
|
+
* cart lines' server-computed totals (`displayItemFromCartLine`).
|
|
24
|
+
*/
|
|
25
|
+
type Stash = { order: CompletedOrder; cartItems: CartLineItem[] }
|
|
26
|
+
|
|
27
|
+
export default function OrderConfirmedPage() {
|
|
28
|
+
const [stash, setStash] = useState<Stash | null | "missing">(null)
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
try {
|
|
32
|
+
const raw = sessionStorage.getItem(LAST_ORDER_STORAGE_KEY)
|
|
33
|
+
setStash(raw ? (JSON.parse(raw) as Stash) : "missing")
|
|
34
|
+
} catch {
|
|
35
|
+
setStash("missing")
|
|
36
|
+
}
|
|
37
|
+
}, [])
|
|
38
|
+
|
|
39
|
+
if (stash === null) return null // first paint before sessionStorage read
|
|
40
|
+
|
|
41
|
+
if (stash === "missing") {
|
|
42
|
+
return (
|
|
43
|
+
<div className="max-w-xl mx-auto px-4 py-16 text-center">
|
|
44
|
+
<h1 className="text-xl font-semibold mb-2">Order placed</h1>
|
|
45
|
+
<p className="text-muted-foreground mb-6">
|
|
46
|
+
Your order was placed successfully. A confirmation email is on its
|
|
47
|
+
way.
|
|
48
|
+
</p>
|
|
49
|
+
<Link href="/" className="underline">
|
|
50
|
+
Continue shopping
|
|
51
|
+
</Link>
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { order, cartItems } = stash
|
|
57
|
+
const totals = orderTotalsFromSummary(order.summary) ?? {
|
|
58
|
+
total: null,
|
|
59
|
+
}
|
|
60
|
+
// The completeCart() order carries FLATTENED items (checkout.md), not the
|
|
61
|
+
// order-detail pivot shape the template's default converter expects —
|
|
62
|
+
// pass the normalized cart-line items instead and drop `order.items`.
|
|
63
|
+
const orderForTemplate = { ...order, items: undefined }
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<OrderCompletedTemplate
|
|
67
|
+
order={orderForTemplate}
|
|
68
|
+
totals={totals}
|
|
69
|
+
items={cartItems.map(displayItemFromCartLine)}
|
|
70
|
+
// This reference store enables exactly one provider (pp_manual); a
|
|
71
|
+
// multi-provider store should stash the chosen provider id from its
|
|
72
|
+
// checkout state alongside the order.
|
|
73
|
+
paymentProviderId={"pp_manual"}
|
|
74
|
+
storeHref="/"
|
|
75
|
+
/>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { StoreTemplate } from "@cartbase/storefront/store/store-template"
|
|
2
|
+
import type { SortOptions } from "@cartbase/storefront/lib/sort-products"
|
|
3
|
+
import { PRICING_CONTEXT } from "@/lib/config"
|
|
4
|
+
import { getServerClient } from "@/lib/server-client"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Home = the all-products listing (runbook step 5: always pass a pricing
|
|
8
|
+
* context or prices come back undecorated).
|
|
9
|
+
*/
|
|
10
|
+
export default async function HomePage({
|
|
11
|
+
searchParams,
|
|
12
|
+
}: {
|
|
13
|
+
searchParams: Promise<{ sortBy?: string; page?: string }>
|
|
14
|
+
}) {
|
|
15
|
+
const { sortBy, page } = await searchParams
|
|
16
|
+
const client = await getServerClient()
|
|
17
|
+
return (
|
|
18
|
+
<StoreTemplate
|
|
19
|
+
client={client}
|
|
20
|
+
sortBy={sortBy as SortOptions | undefined}
|
|
21
|
+
page={page}
|
|
22
|
+
pricingContext={PRICING_CONTEXT}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { notFound } from "next/navigation"
|
|
2
|
+
import { retrieveProduct } from "@cartbase/storefront/api/products"
|
|
3
|
+
import { StoreApiError } from "@cartbase/storefront/api/types"
|
|
4
|
+
import { ProductTemplate } from "@cartbase/storefront/products/product-template"
|
|
5
|
+
import { addToCartAction } from "@/lib/cart-actions"
|
|
6
|
+
import { PRICING_CONTEXT } from "@/lib/config"
|
|
7
|
+
import { getServerClient } from "@/lib/server-client"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* PDP (runbook step 5): fetch by handle WITH the pricing context, render
|
|
11
|
+
* the full product template. `addToCart` is the app-owned seam — a server
|
|
12
|
+
* action that owns the cart cookie (products family contract).
|
|
13
|
+
*/
|
|
14
|
+
export default async function ProductPage({
|
|
15
|
+
params,
|
|
16
|
+
}: {
|
|
17
|
+
params: Promise<{ handle: string }>
|
|
18
|
+
}) {
|
|
19
|
+
const { handle } = await params
|
|
20
|
+
const client = await getServerClient()
|
|
21
|
+
|
|
22
|
+
let product
|
|
23
|
+
try {
|
|
24
|
+
const res = await retrieveProduct(client, handle, PRICING_CONTEXT)
|
|
25
|
+
product = res.product
|
|
26
|
+
} catch (e) {
|
|
27
|
+
if (e instanceof StoreApiError && e.status === 404) notFound()
|
|
28
|
+
throw e
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<ProductTemplate
|
|
33
|
+
client={client}
|
|
34
|
+
product={product}
|
|
35
|
+
pricingContext={PRICING_CONTEXT}
|
|
36
|
+
addToCart={addToCartAction}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function generateMetadata({
|
|
42
|
+
params,
|
|
43
|
+
}: {
|
|
44
|
+
params: Promise<{ handle: string }>
|
|
45
|
+
}) {
|
|
46
|
+
const { handle } = await params
|
|
47
|
+
const client = await getServerClient()
|
|
48
|
+
try {
|
|
49
|
+
const { product } = await retrieveProduct(client, handle)
|
|
50
|
+
// SEO fields with title/description fallbacks (runbook step 5).
|
|
51
|
+
return {
|
|
52
|
+
title: product.seo_title ?? product.title,
|
|
53
|
+
description: product.seo_description ?? product.description ?? undefined,
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
return {}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { Cart } from "@cartbase/storefront/api/carts"
|
|
4
|
+
import { CartDrawerProvider } from "@cartbase/storefront/cart-drawer/context"
|
|
5
|
+
import { CartDrawerTemplate } from "@cartbase/storefront/cart-drawer/template"
|
|
6
|
+
import { ConsentBanner } from "@cartbase/storefront/tracking/consent-banner"
|
|
7
|
+
import {
|
|
8
|
+
pickConsentCopy,
|
|
9
|
+
shouldRenderBanner,
|
|
10
|
+
type ConsentSettings,
|
|
11
|
+
} from "@cartbase/storefront/tracking/consent"
|
|
12
|
+
import { CART_COOKIE, CART_COOKIE_MAX_AGE } from "@/lib/config"
|
|
13
|
+
import { browserClient } from "@/lib/browser-client"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Client-side shell: ONE CartDrawerProvider per app (cart-drawer family
|
|
17
|
+
* mount rule) + the consent banner (consent.md: render the builtin banner
|
|
18
|
+
* only when `enabled && mode === "builtin"`; ConsentInit stays in the
|
|
19
|
+
* server layout as the first child of <body>).
|
|
20
|
+
*/
|
|
21
|
+
export function Providers({
|
|
22
|
+
cart,
|
|
23
|
+
consent,
|
|
24
|
+
children,
|
|
25
|
+
}: {
|
|
26
|
+
cart: Cart | null
|
|
27
|
+
consent: ConsentSettings
|
|
28
|
+
children: React.ReactNode
|
|
29
|
+
}) {
|
|
30
|
+
const copy = pickConsentCopy(consent.copy, "en")
|
|
31
|
+
return (
|
|
32
|
+
<CartDrawerProvider
|
|
33
|
+
cart={cart}
|
|
34
|
+
client={browserClient}
|
|
35
|
+
onCartChange={(next) => {
|
|
36
|
+
// Persist the cart id (carts.md: the app owns the cart cookie).
|
|
37
|
+
document.cookie = `${CART_COOKIE}=${encodeURIComponent(
|
|
38
|
+
next.id
|
|
39
|
+
)};path=/;max-age=${CART_COOKIE_MAX_AGE};samesite=lax`
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
{children}
|
|
43
|
+
<CartDrawerTemplate />
|
|
44
|
+
{shouldRenderBanner(consent) && copy && (
|
|
45
|
+
<ConsentBanner
|
|
46
|
+
copy={copy}
|
|
47
|
+
layout={consent.layout}
|
|
48
|
+
privacyHref={consent.privacy_href}
|
|
49
|
+
rejectOnFirstLayer={consent.reject_on_first_layer}
|
|
50
|
+
/>
|
|
51
|
+
)}
|
|
52
|
+
</CartDrawerProvider>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SearchTemplate } from "@cartbase/storefront/store/search-template"
|
|
2
|
+
import { PRICING_CONTEXT } from "@/lib/config"
|
|
3
|
+
import { getServerClient } from "@/lib/server-client"
|
|
4
|
+
|
|
5
|
+
/** Search page (runbook step 5) — fully URL-state driven search-template. */
|
|
6
|
+
export default async function SearchPage({
|
|
7
|
+
searchParams,
|
|
8
|
+
}: {
|
|
9
|
+
searchParams: Promise<Record<string, string | string[] | undefined>>
|
|
10
|
+
}) {
|
|
11
|
+
const params = await searchParams
|
|
12
|
+
const client = await getServerClient()
|
|
13
|
+
return (
|
|
14
|
+
<SearchTemplate
|
|
15
|
+
client={client}
|
|
16
|
+
searchParams={params}
|
|
17
|
+
pricingContext={PRICING_CONTEXT}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { StorefrontClient } from "@cartbase/storefront/api/http"
|
|
4
|
+
import {
|
|
5
|
+
BARTER_CLIENT_ID,
|
|
6
|
+
BARTER_PUBLISHABLE_KEY,
|
|
7
|
+
BARTER_URL,
|
|
8
|
+
LOCALE_COOKIE,
|
|
9
|
+
} from "./config"
|
|
10
|
+
|
|
11
|
+
function readCookie(name: string): string | null {
|
|
12
|
+
if (typeof document === "undefined") return null
|
|
13
|
+
const match = document.cookie
|
|
14
|
+
.split("; ")
|
|
15
|
+
.find((row) => row.startsWith(`${name}=`))
|
|
16
|
+
return match ? decodeURIComponent(match.slice(name.length + 1)) : null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Browser-scope StorefrontClient (runbook step 2): constructed once per
|
|
21
|
+
* app. Guest-only reference app — no auth token store.
|
|
22
|
+
*
|
|
23
|
+
* baseUrl is the APP origin, not the API origin: the store API ships no
|
|
24
|
+
* CORS headers, so browser calls ride the same-origin `/api/store/*`
|
|
25
|
+
* rewrite (next.config.ts). During SSR of client components no fetches
|
|
26
|
+
* run — the API origin stands in only to satisfy the constructor.
|
|
27
|
+
*/
|
|
28
|
+
export const browserClient = new StorefrontClient({
|
|
29
|
+
baseUrl:
|
|
30
|
+
typeof window !== "undefined" ? window.location.origin : BARTER_URL,
|
|
31
|
+
clientId: BARTER_CLIENT_ID,
|
|
32
|
+
publishableKey: BARTER_PUBLISHABLE_KEY,
|
|
33
|
+
getAuthToken: () => null,
|
|
34
|
+
getLocale: () => readCookie(LOCALE_COOKIE),
|
|
35
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use server"
|
|
2
|
+
|
|
3
|
+
import { revalidatePath } from "next/cache"
|
|
4
|
+
import { cookies } from "next/headers"
|
|
5
|
+
import { addLineItem, createCart } from "@cartbase/storefront/api/carts"
|
|
6
|
+
import { CART_COOKIE, CART_COOKIE_MAX_AGE } from "./config"
|
|
7
|
+
import { getServerClient } from "./server-client"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* PDP add-to-cart server action (carts.md: create the cart lazily on the
|
|
11
|
+
* first add — region falls back to the store default; persist `cart.id`
|
|
12
|
+
* in a cookie). Passed into `ProductTemplate`'s `addToCart` seam.
|
|
13
|
+
*/
|
|
14
|
+
export async function addToCartAction(input: {
|
|
15
|
+
variantId: string
|
|
16
|
+
quantity: number
|
|
17
|
+
}): Promise<void> {
|
|
18
|
+
const client = await getServerClient()
|
|
19
|
+
const jar = await cookies()
|
|
20
|
+
const cartId = jar.get(CART_COOKIE)?.value
|
|
21
|
+
|
|
22
|
+
if (cartId) {
|
|
23
|
+
await addLineItem(client, cartId, {
|
|
24
|
+
variant_id: input.variantId,
|
|
25
|
+
quantity: input.quantity,
|
|
26
|
+
})
|
|
27
|
+
} else {
|
|
28
|
+
const { cart } = await createCart(client, {
|
|
29
|
+
currency_code: "eur",
|
|
30
|
+
items: [{ variant_id: input.variantId, quantity: input.quantity }],
|
|
31
|
+
})
|
|
32
|
+
jar.set(CART_COOKIE, cart.id, {
|
|
33
|
+
path: "/",
|
|
34
|
+
sameSite: "lax",
|
|
35
|
+
httpOnly: false, // the browser-side drawer persists the same cookie
|
|
36
|
+
maxAge: CART_COOKIE_MAX_AGE,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Layout refetches the cart snapshot → CartDrawerProvider sees the new
|
|
41
|
+
// product-line count and auto-opens the drawer.
|
|
42
|
+
revalidatePath("/", "layout")
|
|
43
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Shared storefront configuration (BUILD-A-STOREFRONT.md — inputs table). */
|
|
2
|
+
|
|
3
|
+
export const BARTER_URL = process.env.NEXT_PUBLIC_BARTER_URL ?? ""
|
|
4
|
+
export const BARTER_CLIENT_ID = process.env.NEXT_PUBLIC_BARTER_CLIENT_ID ?? ""
|
|
5
|
+
export const BARTER_PUBLISHABLE_KEY =
|
|
6
|
+
process.env.NEXT_PUBLIC_BARTER_PUBLISHABLE_KEY || undefined
|
|
7
|
+
|
|
8
|
+
/** Cart-id cookie — owned by the app (the SDK never persists the cart). */
|
|
9
|
+
export const CART_COOKIE = "_barter_cart_id"
|
|
10
|
+
export const CART_COOKIE_MAX_AGE = 60 * 60 * 24 * 30 // 30 days
|
|
11
|
+
|
|
12
|
+
/** Locale cookie read by the clients' `getLocale`. */
|
|
13
|
+
export const LOCALE_COOKIE = "_barter_locale"
|
|
14
|
+
|
|
15
|
+
/** Pricing context for every catalog surface (runbook step 5). EUR only. */
|
|
16
|
+
export const PRICING_CONTEXT = { currency_code: "eur" } as const
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { cookies } from "next/headers"
|
|
2
|
+
import { StorefrontClient } from "@cartbase/storefront/api/http"
|
|
3
|
+
import {
|
|
4
|
+
BARTER_CLIENT_ID,
|
|
5
|
+
BARTER_PUBLISHABLE_KEY,
|
|
6
|
+
BARTER_URL,
|
|
7
|
+
LOCALE_COOKIE,
|
|
8
|
+
} from "./config"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Server-scope StorefrontClient (runbook step 2): construct per request;
|
|
12
|
+
* `getLocale` reads the locale cookie. No customer accounts in this
|
|
13
|
+
* reference app, so `getAuthToken` always answers null (guest).
|
|
14
|
+
*/
|
|
15
|
+
export async function getServerClient(): Promise<StorefrontClient> {
|
|
16
|
+
const jar = await cookies()
|
|
17
|
+
const locale = jar.get(LOCALE_COOKIE)?.value ?? null
|
|
18
|
+
return new StorefrontClient({
|
|
19
|
+
baseUrl: BARTER_URL,
|
|
20
|
+
clientId: BARTER_CLIENT_ID,
|
|
21
|
+
publishableKey: BARTER_PUBLISHABLE_KEY,
|
|
22
|
+
getAuthToken: () => null,
|
|
23
|
+
getLocale: () => locale,
|
|
24
|
+
})
|
|
25
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
presets: [require("@cartbase/storefront/tailwind-preset")],
|
|
4
|
+
content: [
|
|
5
|
+
"./src/**/*.{ts,tsx}",
|
|
6
|
+
// The component library ships source — scan it so its classes exist.
|
|
7
|
+
"../../packages/storefront/src/**/*.{ts,tsx}",
|
|
8
|
+
],
|
|
9
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": [
|
|
5
|
+
"dom",
|
|
6
|
+
"dom.iterable",
|
|
7
|
+
"esnext"
|
|
8
|
+
],
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"module": "esnext",
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"jsx": "react-jsx",
|
|
19
|
+
"incremental": true,
|
|
20
|
+
"plugins": [
|
|
21
|
+
{
|
|
22
|
+
"name": "next"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"paths": {
|
|
26
|
+
"@/*": [
|
|
27
|
+
"./src/*"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"include": [
|
|
32
|
+
"next-env.d.ts",
|
|
33
|
+
"**/*.ts",
|
|
34
|
+
"**/*.tsx",
|
|
35
|
+
".next/types/**/*.ts",
|
|
36
|
+
".next/dev/types/**/*.ts"
|
|
37
|
+
],
|
|
38
|
+
"exclude": [
|
|
39
|
+
"node_modules"
|
|
40
|
+
]
|
|
41
|
+
}
|