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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add codegen to demo-store #937

Merged
merged 26 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
32a5454
Add codegen to demo-store
frandiox May 24, 2023
144c135
Remove hardcoded types for mutations
frandiox May 24, 2023
ee3c4c5
Use codegen in sitemap route
frandiox May 24, 2023
73fab41
Use codegen in journal-handle route
frandiox May 24, 2023
012ab89
Use codegen in policies routes
frandiox May 24, 2023
5baba42
Use codegen in page route
frandiox May 24, 2023
3d4e7ba
Use codegen in product routes
frandiox May 24, 2023
68851ba
Use codegen in order routes
frandiox May 24, 2023
ad57b01
Use codegen in product and account routes
frandiox May 24, 2023
23c2333
Use codegen in journal
frandiox May 24, 2023
7970ec0
Use codegen in product-handle
frandiox May 24, 2023
ac9438a
Use codegen in search and featured-products. Reuse fragment
frandiox May 24, 2023
73cc17a
Use codegen in collections route
frandiox May 24, 2023
69043d6
Fix query
frandiox May 24, 2023
acd206b
Remove codegen flag when transpiling project
frandiox May 24, 2023
7a80cc6
Implement generated root types in demo-store
juanpprieto May 25, 2023
f4e79ca
Implement generated homepage types in demo-store
juanpprieto May 25, 2023
763c128
track generated demo-store types
juanpprieto May 25, 2023
abe344e
Fix featured products result types
frandiox May 26, 2023
21e1c2c
Merge branch '2023-04' into fd-codegen-demo-store
frandiox May 26, 2023
09ea473
ESLint auto fix import order
frandiox May 26, 2023
8064754
Fix article type from flattenConnection
frandiox May 26, 2023
6e0d19c
Fix orders type from flattenConnection
frandiox May 26, 2023
6a557a0
Fix possible undefined
frandiox May 26, 2023
479f720
Changesets
frandiox May 26, 2023
444f1c0
Changesets -- add link to PR
frandiox May 26, 2023
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
5 changes: 5 additions & 0 deletions .changeset/breezy-dolphins-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/cli-hydrogen': patch
---

Remove `--codegen-unstable` flag from scripts when transpiling projects from TypeScript to JavaScript.
9 changes: 9 additions & 0 deletions .changeset/dull-stingrays-shop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'demo-store': patch
---

Start using GraphQL code generation. This allows us to have full-stack type-safety and better developer experience.

As a result of the above, we've fixed issues where the frontend was accessing data that was not correctly fetched from the Storefront API. For example, missing `product.vendor` or accessing `totalPrice` instead of `totalPriceV2`.

To enable the unstable codegen feature in your project, run your dev command as `shopify hydrogen dev --codegen-unstable`. See the [changes associated here](https://github.com/Shopify/hydrogen/pull/937/files) for examples.
7 changes: 7 additions & 0 deletions packages/cli/src/lib/transpile-ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ export async function transpileProject(projectDir: string) {
}
}

if (pkgJson.scripts?.dev) {
pkgJson.scripts.dev = pkgJson.scripts.dev.replace(
/\s*--codegen(-unstable)?/,
'',
);
}

await fs.writeFile(
path.join(projectDir, 'package.json'),
JSON.stringify(pkgJson, null, 2),
Expand Down
8 changes: 3 additions & 5 deletions templates/demo-store/app/components/AccountAddressBook.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import {Form} from '@remix-run/react';
import type {
Customer,
MailingAddress,
} from '@shopify/hydrogen/storefront-api-types';
import type {MailingAddress} from '@shopify/hydrogen/storefront-api-types';

import type {CustomerDetailsFragment} from 'storefrontapi.generated';
import {Button, Link, Text} from '~/components';

export function AccountAddressBook({
customer,
addresses,
}: {
customer: Customer;
customer: CustomerDetailsFragment;
addresses: MailingAddress[];
}) {
return (
Expand Down
9 changes: 6 additions & 3 deletions templates/demo-store/app/components/AccountDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type {Customer} from '@shopify/hydrogen/storefront-api-types';

import type {CustomerDetailsFragment} from 'storefrontapi.generated';
import {Link} from '~/components';

export function AccountDetails({customer}: {customer: Customer}) {
export function AccountDetails({
customer,
}: {
customer: CustomerDetailsFragment;
}) {
const {firstName, lastName, email, phone} = customer;

return (
Expand Down
24 changes: 11 additions & 13 deletions templates/demo-store/app/components/FeaturedCollections.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
import {Image} from '@shopify/hydrogen';
import type {Collection} from '@shopify/hydrogen/storefront-api-types';

import type {HomepageFeaturedCollectionsQuery} from 'storefrontapi.generated';
import {Heading, Section, Grid, Link} from '~/components';

type FeaturedCollectionsProps = HomepageFeaturedCollectionsQuery & {
title?: string;
[key: string]: any;
};

export function FeaturedCollections({
collections,
title = 'Collections',
...props
}: {
collections: Collection[];
title?: string;
[key: string]: any;
}) {
const haveCollections = collections && collections.length > 0;
}: FeaturedCollectionsProps) {
const haveCollections = collections?.nodes?.length > 0;
if (!haveCollections) return null;

const items = collections.filter((item) => item.image).length;
const collectionsWithImage = collections.nodes.filter((item) => item.image);

return (
<Section {...props} heading={title}>
<Grid items={items}>
{collections.map((collection) => {
if (!collection?.image) {
return null;
}
<Grid items={collectionsWithImage.length}>
{collectionsWithImage.map((collection) => {
return (
<Link key={collection.id} to={`/collections/${collection.handle}`}>
<div className="grid gap-4">
Expand Down
13 changes: 4 additions & 9 deletions templates/demo-store/app/components/FeaturedSection.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import {useEffect} from 'react';
import {useFetcher} from '@remix-run/react';
import type {Collection, Product} from '@shopify/hydrogen/storefront-api-types';

import {usePrefixPathWithLocale} from '~/lib/utils';
import type {FeaturedData} from '~/routes/($locale).featured-products';

import {FeaturedCollections} from './FeaturedCollections';
import {ProductSwimlane} from './ProductSwimlane';

export interface FeaturedData {
featuredCollections: Collection[];
featuredProducts: Product[];
}

export function FeaturedSection() {
const {load, data} = useFetcher();
const {load, data} = useFetcher<FeaturedData>();
const path = usePrefixPathWithLocale('/featured-products');

useEffect(() => {
Expand All @@ -22,11 +17,11 @@ export function FeaturedSection() {

if (!data) return null;

const {featuredCollections, featuredProducts} = data as FeaturedData;
const {featuredCollections, featuredProducts} = data;

return (
<>
{featuredCollections.length < 2 && (
{featuredCollections.nodes.length < 2 && (
<FeaturedCollections
title="Popular Collections"
collections={featuredCollections}
Expand Down
21 changes: 7 additions & 14 deletions templates/demo-store/app/components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import clsx from 'clsx';
import type {SerializeFrom} from '@shopify/remix-oxygen';
import {MediaFile} from '@shopify/hydrogen';
import type {
MediaImage,
Media,
Metafield,
Video as MediaVideo,
} from '@shopify/hydrogen/storefront-api-types';

import type {CollectionContentFragment} from 'storefrontapi.generated';
import {Heading, Text, Link} from '~/components';

export interface CollectionHero {
byline: Metafield;
cta: Metafield;
handle: string;
heading: Metafield;
type HeroProps = CollectionContentFragment & {
height?: 'full';
loading?: 'eager' | 'lazy';
spread: Metafield;
spreadSecondary: Metafield;
top?: boolean;
}
loading?: HTMLImageElement['loading'];
};

/**
* Hero component that renders metafields attached to collection resources
Expand All @@ -35,7 +28,7 @@ export function Hero({
spread,
spreadSecondary,
top,
}: SerializeFrom<CollectionHero>) {
}: HeroProps) {
return (
<Link to={`/collections/${handle}`}>
<section
Expand Down Expand Up @@ -89,11 +82,11 @@ export function Hero({
);
}

interface SpreadMediaProps {
type SpreadMediaProps = {
data: Media | MediaImage | MediaVideo;
loading?: HTMLImageElement['loading'];
sizes: string;
}
};

function SpreadMedia({data, loading, sizes}: SpreadMediaProps) {
return (
Expand Down
40 changes: 18 additions & 22 deletions templates/demo-store/app/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {useWindowScroll} from 'react-use';
import {Disclosure} from '@headlessui/react';
import {Suspense, useEffect, useMemo} from 'react';

import type {LayoutQuery} from 'storefrontapi.generated';
import {
Drawer,
useDrawer,
Expand All @@ -21,23 +22,21 @@ import {
CartLoading,
Link,
} from '~/components';
import {
type EnhancedMenu,
type EnhancedMenuItem,
useIsHomePath,
} from '~/lib/utils';
import type {ChildEnhancedMenuItem} from '~/lib/utils';
import {type EnhancedMenu, useIsHomePath} from '~/lib/utils';
import {useIsHydrated} from '~/hooks/useIsHydrated';
import {useCartFetchers} from '~/hooks/useCartFetchers';

import type {LayoutData} from '../root';

export function Layout({
children,
layout,
}: {
type LayoutProps = {
children: React.ReactNode;
layout: LayoutData;
}) {
layout: LayoutQuery & {
headerMenu?: EnhancedMenu | null;
footerMenu?: EnhancedMenu | null;
};
};

export function Layout({children, layout}: LayoutProps) {
const {headerMenu, footerMenu} = layout;
return (
<>
<div className="flex flex-col min-h-screen">
Expand All @@ -46,15 +45,12 @@ export function Layout({
Skip to content
</a>
</div>
<Header
title={layout?.shop.name ?? 'Hydrogen'}
menu={layout?.headerMenu}
/>
{headerMenu && <Header title={layout.shop.name} menu={headerMenu} />}
<main role="main" id="mainContent" className="flex-grow">
{children}
</main>
</div>
<Footer menu={layout?.footerMenu} />
{footerMenu && <Footer menu={footerMenu} />}
</>
);
}
Expand Down Expand Up @@ -432,7 +428,7 @@ function Footer({menu}: {menu?: EnhancedMenu}) {
);
}

const FooterLink = ({item}: {item: EnhancedMenuItem}) => {
function FooterLink({item}: {item: ChildEnhancedMenuItem}) {
if (item.to.startsWith('http')) {
return (
<a href={item.to} target={item.target} rel="noopener noreferrer">
Expand All @@ -446,7 +442,7 @@ const FooterLink = ({item}: {item: EnhancedMenuItem}) => {
{item.title}
</Link>
);
};
}

function FooterMenu({menu}: {menu?: EnhancedMenu}) {
const styles = {
Expand All @@ -456,7 +452,7 @@ function FooterMenu({menu}: {menu?: EnhancedMenu}) {

return (
<>
{(menu?.items || []).map((item: EnhancedMenuItem) => (
{(menu?.items || []).map((item) => (
<section key={item.id} className={styles.section}>
<Disclosure>
{({open}) => (
Expand All @@ -480,7 +476,7 @@ function FooterMenu({menu}: {menu?: EnhancedMenu}) {
<Suspense data-comment="This suspense fixes a hydration bug in Disclosure.Panel with static prop">
<Disclosure.Panel static>
<nav className={styles.nav}>
{item.items.map((subItem) => (
{item.items.map((subItem: ChildEnhancedMenuItem) => (
<FooterLink key={subItem.id} item={subItem} />
))}
</nav>
Expand Down
33 changes: 31 additions & 2 deletions templates/demo-store/app/components/OrderCard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {flattenConnection, Image} from '@shopify/hydrogen';
import type {Order} from '@shopify/hydrogen/storefront-api-types';

import type {OrderCardFragment} from 'storefrontapi.generated';
import {Heading, Text, Link} from '~/components';
import {statusMessage} from '~/lib/utils';

export function OrderCard({order}: {order: Order}) {
export function OrderCard({order}: {order: OrderCardFragment}) {
if (!order?.id) return null;
const [legacyOrderId, key] = order!.id!.split('/').pop()!.split('?');
const lineItems = flattenConnection(order?.lineItems);
Expand Down Expand Up @@ -81,3 +81,32 @@ export function OrderCard({order}: {order: Order}) {
</li>
);
}

export const ORDER_CARD_FRAGMENT = `#graphql
fragment OrderCard on Order {
id
orderNumber
processedAt
financialStatus
fulfillmentStatus
currentTotalPrice {
amount
currencyCode
}
lineItems(first: 2) {
edges {
node {
variant {
image {
url
altText
height
width
}
}
title
}
}
}
}
`;
3 changes: 2 additions & 1 deletion templates/demo-store/app/components/ProductCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {ShopifyAnalyticsProduct} from '@shopify/hydrogen';
import {flattenConnection, Image, Money, useMoney} from '@shopify/hydrogen';
import type {MoneyV2, Product} from '@shopify/hydrogen/storefront-api-types';

import type {ProductCardFragment} from 'storefrontapi.generated';
import {Text, Link, AddToCartButton} from '~/components';
import {isDiscounted, isNewArrival} from '~/lib/utils';
import {getProductPlaceholder} from '~/lib/placeholders';
Expand All @@ -15,7 +16,7 @@ export function ProductCard({
onClick,
quickAdd,
}: {
product: Product;
product: ProductCardFragment;
label?: string;
className?: string;
loading?: HTMLImageElement['loading'];
Expand Down
Loading
Loading