hydrogen-forge 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/README.md +212 -0
- package/dist/commands/add.d.ts +7 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +123 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/create.d.ts +8 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +160 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/setup-mcp.d.ts +7 -0
- package/dist/commands/setup-mcp.d.ts.map +1 -0
- package/dist/commands/setup-mcp.js +179 -0
- package/dist/commands/setup-mcp.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/generators.d.ts +6 -0
- package/dist/lib/generators.d.ts.map +1 -0
- package/dist/lib/generators.js +470 -0
- package/dist/lib/generators.js.map +1 -0
- package/dist/lib/utils.d.ts +17 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +101 -0
- package/dist/lib/utils.js.map +1 -0
- package/package.json +54 -0
- package/templates/starter/.env.example +21 -0
- package/templates/starter/.graphqlrc.ts +27 -0
- package/templates/starter/README.md +117 -0
- package/templates/starter/app/assets/favicon.svg +28 -0
- package/templates/starter/app/components/AddToCartButton.tsx +102 -0
- package/templates/starter/app/components/Aside.tsx +136 -0
- package/templates/starter/app/components/CartLineItem.tsx +229 -0
- package/templates/starter/app/components/CartMain.tsx +131 -0
- package/templates/starter/app/components/CartSummary.tsx +315 -0
- package/templates/starter/app/components/CollectionFilters.tsx +330 -0
- package/templates/starter/app/components/CollectionGrid.tsx +141 -0
- package/templates/starter/app/components/Footer.tsx +218 -0
- package/templates/starter/app/components/Header.tsx +296 -0
- package/templates/starter/app/components/PageLayout.tsx +174 -0
- package/templates/starter/app/components/PaginatedResourceSection.tsx +41 -0
- package/templates/starter/app/components/ProductCard.tsx +151 -0
- package/templates/starter/app/components/ProductForm.tsx +156 -0
- package/templates/starter/app/components/ProductGallery.tsx +164 -0
- package/templates/starter/app/components/ProductGrid.tsx +64 -0
- package/templates/starter/app/components/ProductImage.tsx +23 -0
- package/templates/starter/app/components/ProductItem.tsx +44 -0
- package/templates/starter/app/components/ProductPrice.tsx +97 -0
- package/templates/starter/app/components/SearchDialog.tsx +599 -0
- package/templates/starter/app/components/SearchForm.tsx +68 -0
- package/templates/starter/app/components/SearchFormPredictive.tsx +76 -0
- package/templates/starter/app/components/SearchResults.tsx +161 -0
- package/templates/starter/app/components/SearchResultsPredictive.tsx +461 -0
- package/templates/starter/app/entry.client.tsx +21 -0
- package/templates/starter/app/entry.server.tsx +53 -0
- package/templates/starter/app/graphql/customer-account/CustomerAddressMutations.ts +64 -0
- package/templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +40 -0
- package/templates/starter/app/graphql/customer-account/CustomerOrderQuery.ts +90 -0
- package/templates/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +63 -0
- package/templates/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +25 -0
- package/templates/starter/app/lib/context.ts +60 -0
- package/templates/starter/app/lib/fragments.ts +234 -0
- package/templates/starter/app/lib/orderFilters.ts +90 -0
- package/templates/starter/app/lib/redirect.ts +23 -0
- package/templates/starter/app/lib/search.ts +79 -0
- package/templates/starter/app/lib/session.ts +72 -0
- package/templates/starter/app/lib/variants.ts +46 -0
- package/templates/starter/app/root.tsx +209 -0
- package/templates/starter/app/routes/$.tsx +11 -0
- package/templates/starter/app/routes/[robots.txt].tsx +117 -0
- package/templates/starter/app/routes/[sitemap.xml].tsx +16 -0
- package/templates/starter/app/routes/_index.tsx +167 -0
- package/templates/starter/app/routes/account.$.tsx +9 -0
- package/templates/starter/app/routes/account._index.tsx +5 -0
- package/templates/starter/app/routes/account.addresses.tsx +516 -0
- package/templates/starter/app/routes/account.orders.$id.tsx +222 -0
- package/templates/starter/app/routes/account.orders._index.tsx +222 -0
- package/templates/starter/app/routes/account.profile.tsx +133 -0
- package/templates/starter/app/routes/account.tsx +97 -0
- package/templates/starter/app/routes/account_.authorize.tsx +5 -0
- package/templates/starter/app/routes/account_.login.tsx +7 -0
- package/templates/starter/app/routes/account_.logout.tsx +11 -0
- package/templates/starter/app/routes/api.$version.[graphql.json].tsx +14 -0
- package/templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +129 -0
- package/templates/starter/app/routes/blogs.$blogHandle._index.tsx +175 -0
- package/templates/starter/app/routes/blogs._index.tsx +109 -0
- package/templates/starter/app/routes/cart.$lines.tsx +70 -0
- package/templates/starter/app/routes/cart.tsx +117 -0
- package/templates/starter/app/routes/collections.$handle.tsx +161 -0
- package/templates/starter/app/routes/collections._index.tsx +133 -0
- package/templates/starter/app/routes/collections.all.tsx +122 -0
- package/templates/starter/app/routes/discount.$code.tsx +48 -0
- package/templates/starter/app/routes/pages.$handle.tsx +88 -0
- package/templates/starter/app/routes/policies.$handle.tsx +93 -0
- package/templates/starter/app/routes/policies._index.tsx +69 -0
- package/templates/starter/app/routes/products.$handle.tsx +232 -0
- package/templates/starter/app/routes/search.tsx +426 -0
- package/templates/starter/app/routes/sitemap.$type.$page[.xml].tsx +23 -0
- package/templates/starter/app/routes.ts +9 -0
- package/templates/starter/app/styles/app.css +574 -0
- package/templates/starter/app/styles/reset.css +139 -0
- package/templates/starter/app/styles/tailwind.css +116 -0
- package/templates/starter/customer-accountapi.generated.d.ts +543 -0
- package/templates/starter/env.d.ts +7 -0
- package/templates/starter/eslint.config.js +247 -0
- package/templates/starter/guides/predictiveSearch/predictiveSearch.jpg +0 -0
- package/templates/starter/guides/predictiveSearch/predictiveSearch.md +394 -0
- package/templates/starter/guides/search/search.jpg +0 -0
- package/templates/starter/guides/search/search.md +335 -0
- package/templates/starter/package.json +71 -0
- package/templates/starter/postcss.config.js +6 -0
- package/templates/starter/public/.gitkeep +0 -0
- package/templates/starter/react-router.config.ts +13 -0
- package/templates/starter/server.ts +59 -0
- package/templates/starter/storefrontapi.generated.d.ts +1264 -0
- package/templates/starter/tailwind.config.js +83 -0
- package/templates/starter/tsconfig.json +67 -0
- package/templates/starter/vite.config.ts +32 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
export function generateComponent(name, type, withStyles) {
|
|
2
|
+
switch (type) {
|
|
3
|
+
case 'product':
|
|
4
|
+
return generateProductComponent(name, withStyles);
|
|
5
|
+
case 'collection':
|
|
6
|
+
return generateCollectionComponent(name, withStyles);
|
|
7
|
+
case 'cart':
|
|
8
|
+
return generateCartComponent(name, withStyles);
|
|
9
|
+
case 'form':
|
|
10
|
+
return generateFormComponent(name, withStyles);
|
|
11
|
+
case 'layout':
|
|
12
|
+
return generateLayoutComponent(name, withStyles);
|
|
13
|
+
default:
|
|
14
|
+
return generateBasicComponent(name, withStyles);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function generateRoute(name, type) {
|
|
18
|
+
switch (type) {
|
|
19
|
+
case 'collection':
|
|
20
|
+
return generateCollectionRoute(name);
|
|
21
|
+
case 'product':
|
|
22
|
+
return generateProductRoute(name);
|
|
23
|
+
case 'account':
|
|
24
|
+
return generateAccountRoute(name);
|
|
25
|
+
case 'api':
|
|
26
|
+
return generateApiRoute(name);
|
|
27
|
+
case 'resource':
|
|
28
|
+
return generateResourceRoute(name);
|
|
29
|
+
default:
|
|
30
|
+
return generatePageRoute(name);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Component generators
|
|
34
|
+
function generateBasicComponent(name, withStyles) {
|
|
35
|
+
return `export interface ${name}Props {
|
|
36
|
+
// Add props here
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function ${name}({}: ${name}Props) {
|
|
40
|
+
return (
|
|
41
|
+
<div${withStyles ? ' className="p-4"' : ''}>
|
|
42
|
+
<h2>${name} Component</h2>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
function generateProductComponent(name, withStyles) {
|
|
49
|
+
return `import {Link} from 'react-router';
|
|
50
|
+
import {Image, Money} from '@shopify/hydrogen';
|
|
51
|
+
import type {MoneyV2} from '@shopify/hydrogen/storefront-api-types';
|
|
52
|
+
|
|
53
|
+
export interface ${name}Props {
|
|
54
|
+
product: {
|
|
55
|
+
id: string;
|
|
56
|
+
title: string;
|
|
57
|
+
handle: string;
|
|
58
|
+
featuredImage?: {
|
|
59
|
+
url: string;
|
|
60
|
+
altText?: string | null;
|
|
61
|
+
} | null;
|
|
62
|
+
priceRange: {
|
|
63
|
+
minVariantPrice: MoneyV2;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function ${name}({product}: ${name}Props) {
|
|
69
|
+
const image = product.featuredImage;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<Link
|
|
73
|
+
to={\`/products/\${product.handle}\`}
|
|
74
|
+
${withStyles ? 'className="group block"' : ''}
|
|
75
|
+
prefetch="intent"
|
|
76
|
+
>
|
|
77
|
+
<div${withStyles ? ' className="relative aspect-square overflow-hidden rounded-lg bg-secondary-100"' : ''}>
|
|
78
|
+
{image ? (
|
|
79
|
+
<Image
|
|
80
|
+
alt={image.altText || product.title}
|
|
81
|
+
src={image.url}
|
|
82
|
+
${withStyles ? 'className="h-full w-full object-cover transition-transform group-hover:scale-105"' : ''}
|
|
83
|
+
/>
|
|
84
|
+
) : (
|
|
85
|
+
<div${withStyles ? ' className="flex h-full w-full items-center justify-center"' : ''}>
|
|
86
|
+
<span${withStyles ? ' className="text-secondary-400"' : ''}>No image</span>
|
|
87
|
+
</div>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
<div${withStyles ? ' className="mt-3 space-y-1"' : ''}>
|
|
91
|
+
<h3${withStyles ? ' className="text-sm font-medium text-secondary-900"' : ''}>{product.title}</h3>
|
|
92
|
+
<Money
|
|
93
|
+
data={product.priceRange.minVariantPrice}
|
|
94
|
+
${withStyles ? 'className="text-sm text-secondary-500"' : ''}
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
</Link>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
function generateCollectionComponent(name, withStyles) {
|
|
103
|
+
return `import {Link} from 'react-router';
|
|
104
|
+
import {Image} from '@shopify/hydrogen';
|
|
105
|
+
|
|
106
|
+
export interface ${name}Props {
|
|
107
|
+
collection: {
|
|
108
|
+
id: string;
|
|
109
|
+
title: string;
|
|
110
|
+
handle: string;
|
|
111
|
+
image?: {
|
|
112
|
+
url: string;
|
|
113
|
+
altText?: string | null;
|
|
114
|
+
} | null;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function ${name}({collection}: ${name}Props) {
|
|
119
|
+
return (
|
|
120
|
+
<Link
|
|
121
|
+
to={\`/collections/\${collection.handle}\`}
|
|
122
|
+
${withStyles ? 'className="group relative block overflow-hidden rounded-lg"' : ''}
|
|
123
|
+
prefetch="intent"
|
|
124
|
+
>
|
|
125
|
+
<div${withStyles ? ' className="aspect-video bg-secondary-100"' : ''}>
|
|
126
|
+
{collection.image ? (
|
|
127
|
+
<Image
|
|
128
|
+
alt={collection.image.altText || collection.title}
|
|
129
|
+
src={collection.image.url}
|
|
130
|
+
${withStyles ? 'className="h-full w-full object-cover transition-transform group-hover:scale-105"' : ''}
|
|
131
|
+
/>
|
|
132
|
+
) : (
|
|
133
|
+
<div${withStyles ? ' className="flex h-full w-full items-center justify-center bg-secondary-200"' : ''}>
|
|
134
|
+
<span${withStyles ? ' className="text-secondary-500"' : ''}>{collection.title}</span>
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
<div${withStyles ? ' className="absolute inset-0 flex items-end bg-gradient-to-t from-black/60 to-transparent p-4"' : ''}>
|
|
139
|
+
<h3${withStyles ? ' className="text-lg font-semibold text-white"' : ''}>{collection.title}</h3>
|
|
140
|
+
</div>
|
|
141
|
+
</Link>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
145
|
+
}
|
|
146
|
+
function generateCartComponent(name, withStyles) {
|
|
147
|
+
return `import {CartForm} from '@shopify/hydrogen';
|
|
148
|
+
|
|
149
|
+
export interface ${name}Props {
|
|
150
|
+
line: {
|
|
151
|
+
id: string;
|
|
152
|
+
quantity: number;
|
|
153
|
+
merchandise: {
|
|
154
|
+
title: string;
|
|
155
|
+
product: {
|
|
156
|
+
title: string;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function ${name}({line}: ${name}Props) {
|
|
163
|
+
return (
|
|
164
|
+
<div${withStyles ? ' className="flex gap-4 py-4 border-b border-secondary-200"' : ''}>
|
|
165
|
+
<div${withStyles ? ' className="flex-1"' : ''}>
|
|
166
|
+
<h3${withStyles ? ' className="font-medium"' : ''}>{line.merchandise.product.title}</h3>
|
|
167
|
+
<p${withStyles ? ' className="text-sm text-secondary-500"' : ''}>{line.merchandise.title}</p>
|
|
168
|
+
<p${withStyles ? ' className="mt-1 text-sm"' : ''}>Qty: {line.quantity}</p>
|
|
169
|
+
</div>
|
|
170
|
+
<CartForm
|
|
171
|
+
route="/cart"
|
|
172
|
+
action={CartForm.ACTIONS.LinesRemove}
|
|
173
|
+
inputs={{lineIds: [line.id]}}
|
|
174
|
+
>
|
|
175
|
+
<button
|
|
176
|
+
type="submit"
|
|
177
|
+
${withStyles ? 'className="text-sm text-red-600 hover:text-red-700"' : ''}
|
|
178
|
+
>
|
|
179
|
+
Remove
|
|
180
|
+
</button>
|
|
181
|
+
</CartForm>
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
}
|
|
187
|
+
function generateFormComponent(name, withStyles) {
|
|
188
|
+
return `import {Form, useNavigation} from 'react-router';
|
|
189
|
+
|
|
190
|
+
export interface ${name}Props {
|
|
191
|
+
action?: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function ${name}({action}: ${name}Props) {
|
|
195
|
+
const navigation = useNavigation();
|
|
196
|
+
const isSubmitting = navigation.state === 'submitting';
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<Form action={action} method="post"${withStyles ? ' className="space-y-4"' : ''}>
|
|
200
|
+
<div>
|
|
201
|
+
<label htmlFor="email"${withStyles ? ' className="block text-sm font-medium"' : ''}>
|
|
202
|
+
Email
|
|
203
|
+
</label>
|
|
204
|
+
<input
|
|
205
|
+
type="email"
|
|
206
|
+
id="email"
|
|
207
|
+
name="email"
|
|
208
|
+
required
|
|
209
|
+
${withStyles ? 'className="mt-1 block w-full rounded-md border border-secondary-300 px-3 py-2"' : ''}
|
|
210
|
+
/>
|
|
211
|
+
</div>
|
|
212
|
+
<button
|
|
213
|
+
type="submit"
|
|
214
|
+
disabled={isSubmitting}
|
|
215
|
+
${withStyles ? 'className="w-full rounded-md bg-primary-600 px-4 py-2 text-white disabled:opacity-50"' : ''}
|
|
216
|
+
>
|
|
217
|
+
{isSubmitting ? 'Submitting...' : 'Submit'}
|
|
218
|
+
</button>
|
|
219
|
+
</Form>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
`;
|
|
223
|
+
}
|
|
224
|
+
function generateLayoutComponent(name, withStyles) {
|
|
225
|
+
return `import type {ReactNode} from 'react';
|
|
226
|
+
|
|
227
|
+
export interface ${name}Props {
|
|
228
|
+
children: ReactNode;
|
|
229
|
+
header?: ReactNode;
|
|
230
|
+
footer?: ReactNode;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function ${name}({children, header, footer}: ${name}Props) {
|
|
234
|
+
return (
|
|
235
|
+
<div${withStyles ? ' className="flex min-h-screen flex-col"' : ''}>
|
|
236
|
+
{header && <header${withStyles ? ' className="sticky top-0 z-40 border-b bg-white"' : ''}>{header}</header>}
|
|
237
|
+
<main${withStyles ? ' className="flex-1"' : ''}>{children}</main>
|
|
238
|
+
{footer && <footer${withStyles ? ' className="border-t bg-secondary-50"' : ''}>{footer}</footer>}
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
`;
|
|
243
|
+
}
|
|
244
|
+
// Route generators
|
|
245
|
+
function generatePageRoute(name) {
|
|
246
|
+
const routeTitle = name
|
|
247
|
+
.split('.')
|
|
248
|
+
.pop()
|
|
249
|
+
?.replace(/[$_]/g, '')
|
|
250
|
+
.replace(/^./, (c) => c.toUpperCase()) || 'Page';
|
|
251
|
+
return `import {useLoaderData} from 'react-router';
|
|
252
|
+
import type {Route} from './+types/${name}';
|
|
253
|
+
|
|
254
|
+
export const meta: Route.MetaFunction = () => {
|
|
255
|
+
return [{title: '${routeTitle}'}];
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export async function loader({context}: Route.LoaderArgs) {
|
|
259
|
+
// Add your loader logic here
|
|
260
|
+
return {};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export default function ${routeTitle}() {
|
|
264
|
+
const data = useLoaderData<typeof loader>();
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<div className="container mx-auto px-4 py-8">
|
|
268
|
+
<h1 className="text-2xl font-bold">${routeTitle}</h1>
|
|
269
|
+
</div>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
`;
|
|
273
|
+
}
|
|
274
|
+
function generateCollectionRoute(name) {
|
|
275
|
+
return `import {redirect, useLoaderData} from 'react-router';
|
|
276
|
+
import type {Route} from './+types/${name}';
|
|
277
|
+
import {getPaginationVariables} from '@shopify/hydrogen';
|
|
278
|
+
|
|
279
|
+
export const meta: Route.MetaFunction = ({data}) => {
|
|
280
|
+
return [{title: \`\${data?.collection?.title ?? ''} Collection\`}];
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
export async function loader({context, params, request}: Route.LoaderArgs) {
|
|
284
|
+
const {handle} = params;
|
|
285
|
+
const {storefront} = context;
|
|
286
|
+
const paginationVariables = getPaginationVariables(request, {pageBy: 8});
|
|
287
|
+
|
|
288
|
+
if (!handle) {
|
|
289
|
+
throw redirect('/collections');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const {collection} = await storefront.query(COLLECTION_QUERY, {
|
|
293
|
+
variables: {handle, ...paginationVariables},
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
if (!collection) {
|
|
297
|
+
throw new Response('Collection not found', {status: 404});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return {collection};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export default function Collection() {
|
|
304
|
+
const {collection} = useLoaderData<typeof loader>();
|
|
305
|
+
|
|
306
|
+
return (
|
|
307
|
+
<div className="container mx-auto px-4 py-8">
|
|
308
|
+
<h1 className="text-2xl font-bold">{collection.title}</h1>
|
|
309
|
+
{/* Add collection products grid here */}
|
|
310
|
+
</div>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const COLLECTION_QUERY = \`#graphql
|
|
315
|
+
query Collection($handle: String!, $first: Int, $last: Int, $after: String, $before: String) {
|
|
316
|
+
collection(handle: $handle) {
|
|
317
|
+
id
|
|
318
|
+
handle
|
|
319
|
+
title
|
|
320
|
+
description
|
|
321
|
+
products(first: $first, last: $last, after: $after, before: $before) {
|
|
322
|
+
nodes {
|
|
323
|
+
id
|
|
324
|
+
handle
|
|
325
|
+
title
|
|
326
|
+
}
|
|
327
|
+
pageInfo {
|
|
328
|
+
hasNextPage
|
|
329
|
+
hasPreviousPage
|
|
330
|
+
startCursor
|
|
331
|
+
endCursor
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
\` as const;
|
|
337
|
+
`;
|
|
338
|
+
}
|
|
339
|
+
function generateProductRoute(name) {
|
|
340
|
+
return `import {useLoaderData} from 'react-router';
|
|
341
|
+
import type {Route} from './+types/${name}';
|
|
342
|
+
import {getSelectedProductOptions} from '@shopify/hydrogen';
|
|
343
|
+
|
|
344
|
+
export const meta: Route.MetaFunction = ({data}) => {
|
|
345
|
+
return [{title: data?.product?.title ?? 'Product'}];
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
export async function loader({context, params, request}: Route.LoaderArgs) {
|
|
349
|
+
const {handle} = params;
|
|
350
|
+
const {storefront} = context;
|
|
351
|
+
const selectedOptions = getSelectedProductOptions(request);
|
|
352
|
+
|
|
353
|
+
const {product} = await storefront.query(PRODUCT_QUERY, {
|
|
354
|
+
variables: {handle, selectedOptions},
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
if (!product) {
|
|
358
|
+
throw new Response('Product not found', {status: 404});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return {product};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export default function Product() {
|
|
365
|
+
const {product} = useLoaderData<typeof loader>();
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<div className="container mx-auto px-4 py-8">
|
|
369
|
+
<h1 className="text-3xl font-bold">{product.title}</h1>
|
|
370
|
+
{/* Add product gallery and form here */}
|
|
371
|
+
</div>
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const PRODUCT_QUERY = \`#graphql
|
|
376
|
+
query Product($handle: String!, $selectedOptions: [SelectedOptionInput!]!) {
|
|
377
|
+
product(handle: $handle) {
|
|
378
|
+
id
|
|
379
|
+
title
|
|
380
|
+
handle
|
|
381
|
+
descriptionHtml
|
|
382
|
+
featuredImage {
|
|
383
|
+
url
|
|
384
|
+
altText
|
|
385
|
+
}
|
|
386
|
+
selectedOrFirstAvailableVariant(selectedOptions: $selectedOptions) {
|
|
387
|
+
id
|
|
388
|
+
title
|
|
389
|
+
price {
|
|
390
|
+
amount
|
|
391
|
+
currencyCode
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
\` as const;
|
|
397
|
+
`;
|
|
398
|
+
}
|
|
399
|
+
function generateAccountRoute(name) {
|
|
400
|
+
return `import {useLoaderData} from 'react-router';
|
|
401
|
+
import type {Route} from './+types/${name}';
|
|
402
|
+
|
|
403
|
+
export const meta: Route.MetaFunction = () => {
|
|
404
|
+
return [{title: 'Account'}];
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
export async function loader({context}: Route.LoaderArgs) {
|
|
408
|
+
const {customerAccount} = context;
|
|
409
|
+
await customerAccount.handleAuthStatus();
|
|
410
|
+
|
|
411
|
+
const {data} = await customerAccount.query(CUSTOMER_QUERY);
|
|
412
|
+
return {customer: data.customer};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export default function Account() {
|
|
416
|
+
const {customer} = useLoaderData<typeof loader>();
|
|
417
|
+
|
|
418
|
+
return (
|
|
419
|
+
<div className="container mx-auto px-4 py-8">
|
|
420
|
+
<h1 className="text-2xl font-bold">Account</h1>
|
|
421
|
+
<p className="mt-4">Welcome, {customer?.firstName || 'Customer'}</p>
|
|
422
|
+
</div>
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const CUSTOMER_QUERY = \`#graphql
|
|
427
|
+
query CustomerQuery {
|
|
428
|
+
customer {
|
|
429
|
+
id
|
|
430
|
+
firstName
|
|
431
|
+
lastName
|
|
432
|
+
email
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
\` as const;
|
|
436
|
+
`;
|
|
437
|
+
}
|
|
438
|
+
function generateApiRoute(name) {
|
|
439
|
+
return `import type {Route} from './+types/${name}';
|
|
440
|
+
|
|
441
|
+
export async function loader({request}: Route.LoaderArgs) {
|
|
442
|
+
return Response.json({
|
|
443
|
+
message: 'API endpoint',
|
|
444
|
+
timestamp: new Date().toISOString(),
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export async function action({request}: Route.ActionArgs) {
|
|
449
|
+
const method = request.method;
|
|
450
|
+
|
|
451
|
+
if (method === 'POST') {
|
|
452
|
+
const body = await request.json();
|
|
453
|
+
return Response.json({success: true, data: body});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return Response.json({error: 'Method not allowed'}, {status: 405});
|
|
457
|
+
}
|
|
458
|
+
`;
|
|
459
|
+
}
|
|
460
|
+
function generateResourceRoute(name) {
|
|
461
|
+
return `import type {Route} from './+types/${name}';
|
|
462
|
+
|
|
463
|
+
export async function loader({context}: Route.LoaderArgs) {
|
|
464
|
+
return Response.json({
|
|
465
|
+
// Return your resource data
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
`;
|
|
469
|
+
}
|
|
470
|
+
//# sourceMappingURL=generators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generators.js","sourceRoot":"","sources":["../../src/lib/generators.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface SpinnerOptions {
|
|
2
|
+
text: string;
|
|
3
|
+
successText?: string;
|
|
4
|
+
failText?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function withSpinner<T>(options: SpinnerOptions, fn: () => Promise<T>): Promise<T>;
|
|
7
|
+
export declare function runCommand(command: string, args: string[], cwd: string): Promise<void>;
|
|
8
|
+
export declare function copyTemplate(templateDir: string, targetDir: string, replacements?: Record<string, string>): Promise<void>;
|
|
9
|
+
export declare function validateProjectName(name: string): boolean;
|
|
10
|
+
export declare function isDirectoryEmpty(dir: string): Promise<boolean>;
|
|
11
|
+
export declare function detectPackageManager(): Promise<'npm' | 'pnpm' | 'yarn'>;
|
|
12
|
+
export declare function printSuccess(message: string): void;
|
|
13
|
+
export declare function printError(message: string): void;
|
|
14
|
+
export declare function printInfo(message: string): void;
|
|
15
|
+
export declare function printWarning(message: string): void;
|
|
16
|
+
export declare function printStep(step: number, total: number, message: string): void;
|
|
17
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,WAAW,CAAC,CAAC,EACjC,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAWZ;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC,CAMf;AA+BD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIzD;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOpE;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC,CAU7E;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE5E"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as fs from 'fs-extra';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
export async function withSpinner(options, fn) {
|
|
7
|
+
const spinner = ora(options.text).start();
|
|
8
|
+
try {
|
|
9
|
+
const result = await fn();
|
|
10
|
+
spinner.succeed(options.successText || options.text);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
spinner.fail(options.failText || options.text);
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function runCommand(command, args, cwd) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const child = spawn(command, args, {
|
|
21
|
+
cwd,
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
shell: true,
|
|
24
|
+
});
|
|
25
|
+
child.on('close', (code) => {
|
|
26
|
+
if (code === 0) {
|
|
27
|
+
resolve();
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
reject(new Error(`Command failed with code ${code}`));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
child.on('error', reject);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export async function copyTemplate(templateDir, targetDir, replacements) {
|
|
37
|
+
await fs.copy(templateDir, targetDir);
|
|
38
|
+
if (replacements) {
|
|
39
|
+
await replaceInFiles(targetDir, replacements);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function replaceInFiles(dir, replacements) {
|
|
43
|
+
const files = await fs.readdir(dir);
|
|
44
|
+
for (const file of files) {
|
|
45
|
+
const filePath = path.join(dir, file);
|
|
46
|
+
const stat = await fs.stat(filePath);
|
|
47
|
+
if (stat.isDirectory()) {
|
|
48
|
+
await replaceInFiles(filePath, replacements);
|
|
49
|
+
}
|
|
50
|
+
else if (file.endsWith('.ts') ||
|
|
51
|
+
file.endsWith('.tsx') ||
|
|
52
|
+
file.endsWith('.json') ||
|
|
53
|
+
file.endsWith('.md')) {
|
|
54
|
+
let content = await fs.readFile(filePath, 'utf-8');
|
|
55
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
56
|
+
content = content.replace(new RegExp(key, 'g'), value);
|
|
57
|
+
}
|
|
58
|
+
await fs.writeFile(filePath, content);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function validateProjectName(name) {
|
|
63
|
+
// Valid npm package name
|
|
64
|
+
const validName = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
65
|
+
return validName.test(name);
|
|
66
|
+
}
|
|
67
|
+
export async function isDirectoryEmpty(dir) {
|
|
68
|
+
try {
|
|
69
|
+
const files = await fs.readdir(dir);
|
|
70
|
+
return files.length === 0;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return true; // Directory doesn't exist
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export async function detectPackageManager() {
|
|
77
|
+
const cwd = process.cwd();
|
|
78
|
+
if (await fs.pathExists(path.join(cwd, 'pnpm-lock.yaml'))) {
|
|
79
|
+
return 'pnpm';
|
|
80
|
+
}
|
|
81
|
+
if (await fs.pathExists(path.join(cwd, 'yarn.lock'))) {
|
|
82
|
+
return 'yarn';
|
|
83
|
+
}
|
|
84
|
+
return 'npm';
|
|
85
|
+
}
|
|
86
|
+
export function printSuccess(message) {
|
|
87
|
+
console.log(chalk.green('✓'), message);
|
|
88
|
+
}
|
|
89
|
+
export function printError(message) {
|
|
90
|
+
console.error(chalk.red('✗'), message);
|
|
91
|
+
}
|
|
92
|
+
export function printInfo(message) {
|
|
93
|
+
console.log(chalk.blue('ℹ'), message);
|
|
94
|
+
}
|
|
95
|
+
export function printWarning(message) {
|
|
96
|
+
console.log(chalk.yellow('⚠'), message);
|
|
97
|
+
}
|
|
98
|
+
export function printStep(step, total, message) {
|
|
99
|
+
console.log(chalk.dim(`[${step}/${total}]`), message);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hydrogen-forge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for creating and managing Hydrogen Forge projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"hydrogen-forge": "./dist/index.js",
|
|
9
|
+
"hf": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"start": "node dist/index.js",
|
|
15
|
+
"lint": "eslint src/",
|
|
16
|
+
"typecheck": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"hydrogen",
|
|
20
|
+
"shopify",
|
|
21
|
+
"cli",
|
|
22
|
+
"scaffolding",
|
|
23
|
+
"react",
|
|
24
|
+
"ecommerce"
|
|
25
|
+
],
|
|
26
|
+
"author": "nathanmcmullendev",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"chalk": "^5.3.0",
|
|
30
|
+
"commander": "^12.0.0",
|
|
31
|
+
"fs-extra": "^11.2.0",
|
|
32
|
+
"inquirer": "^9.2.0",
|
|
33
|
+
"ora": "^8.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/fs-extra": "^11.0.4",
|
|
37
|
+
"@types/inquirer": "^9.0.9",
|
|
38
|
+
"@types/node": "^20.10.0",
|
|
39
|
+
"typescript": "^5.3.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20.0.0"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"dist",
|
|
46
|
+
"templates",
|
|
47
|
+
"README.md"
|
|
48
|
+
],
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/nathanmcmullendev/hydrogen-forge.git",
|
|
52
|
+
"directory": "packages/cli"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Hydrogen Forge Starter - Environment Variables
|
|
2
|
+
# Copy this file to .env and fill in your values
|
|
3
|
+
|
|
4
|
+
# Session secret (generate a random string)
|
|
5
|
+
SESSION_SECRET="your-session-secret-here"
|
|
6
|
+
|
|
7
|
+
# Shopify Store Domain (without https://)
|
|
8
|
+
PUBLIC_STORE_DOMAIN="your-store.myshopify.com"
|
|
9
|
+
|
|
10
|
+
# Storefront API Token (from Shopify Admin > Apps > Headless)
|
|
11
|
+
PUBLIC_STOREFRONT_API_TOKEN="your-storefront-api-token"
|
|
12
|
+
|
|
13
|
+
# Optional: Private Storefront API Token (for server-side operations)
|
|
14
|
+
# PRIVATE_STOREFRONT_API_TOKEN=""
|
|
15
|
+
|
|
16
|
+
# Optional: Customer Account API
|
|
17
|
+
# PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID=""
|
|
18
|
+
# PUBLIC_CUSTOMER_ACCOUNT_API_URL=""
|
|
19
|
+
|
|
20
|
+
# Optional: Checkout Domain
|
|
21
|
+
# PUBLIC_CHECKOUT_DOMAIN=""
|