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.
Files changed (118) hide show
  1. package/README.md +212 -0
  2. package/dist/commands/add.d.ts +7 -0
  3. package/dist/commands/add.d.ts.map +1 -0
  4. package/dist/commands/add.js +123 -0
  5. package/dist/commands/add.js.map +1 -0
  6. package/dist/commands/create.d.ts +8 -0
  7. package/dist/commands/create.d.ts.map +1 -0
  8. package/dist/commands/create.js +160 -0
  9. package/dist/commands/create.js.map +1 -0
  10. package/dist/commands/setup-mcp.d.ts +7 -0
  11. package/dist/commands/setup-mcp.d.ts.map +1 -0
  12. package/dist/commands/setup-mcp.js +179 -0
  13. package/dist/commands/setup-mcp.js.map +1 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +50 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/lib/generators.d.ts +6 -0
  19. package/dist/lib/generators.d.ts.map +1 -0
  20. package/dist/lib/generators.js +470 -0
  21. package/dist/lib/generators.js.map +1 -0
  22. package/dist/lib/utils.d.ts +17 -0
  23. package/dist/lib/utils.d.ts.map +1 -0
  24. package/dist/lib/utils.js +101 -0
  25. package/dist/lib/utils.js.map +1 -0
  26. package/package.json +54 -0
  27. package/templates/starter/.env.example +21 -0
  28. package/templates/starter/.graphqlrc.ts +27 -0
  29. package/templates/starter/README.md +117 -0
  30. package/templates/starter/app/assets/favicon.svg +28 -0
  31. package/templates/starter/app/components/AddToCartButton.tsx +102 -0
  32. package/templates/starter/app/components/Aside.tsx +136 -0
  33. package/templates/starter/app/components/CartLineItem.tsx +229 -0
  34. package/templates/starter/app/components/CartMain.tsx +131 -0
  35. package/templates/starter/app/components/CartSummary.tsx +315 -0
  36. package/templates/starter/app/components/CollectionFilters.tsx +330 -0
  37. package/templates/starter/app/components/CollectionGrid.tsx +141 -0
  38. package/templates/starter/app/components/Footer.tsx +218 -0
  39. package/templates/starter/app/components/Header.tsx +296 -0
  40. package/templates/starter/app/components/PageLayout.tsx +174 -0
  41. package/templates/starter/app/components/PaginatedResourceSection.tsx +41 -0
  42. package/templates/starter/app/components/ProductCard.tsx +151 -0
  43. package/templates/starter/app/components/ProductForm.tsx +156 -0
  44. package/templates/starter/app/components/ProductGallery.tsx +164 -0
  45. package/templates/starter/app/components/ProductGrid.tsx +64 -0
  46. package/templates/starter/app/components/ProductImage.tsx +23 -0
  47. package/templates/starter/app/components/ProductItem.tsx +44 -0
  48. package/templates/starter/app/components/ProductPrice.tsx +97 -0
  49. package/templates/starter/app/components/SearchDialog.tsx +599 -0
  50. package/templates/starter/app/components/SearchForm.tsx +68 -0
  51. package/templates/starter/app/components/SearchFormPredictive.tsx +76 -0
  52. package/templates/starter/app/components/SearchResults.tsx +161 -0
  53. package/templates/starter/app/components/SearchResultsPredictive.tsx +461 -0
  54. package/templates/starter/app/entry.client.tsx +21 -0
  55. package/templates/starter/app/entry.server.tsx +53 -0
  56. package/templates/starter/app/graphql/customer-account/CustomerAddressMutations.ts +64 -0
  57. package/templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +40 -0
  58. package/templates/starter/app/graphql/customer-account/CustomerOrderQuery.ts +90 -0
  59. package/templates/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +63 -0
  60. package/templates/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +25 -0
  61. package/templates/starter/app/lib/context.ts +60 -0
  62. package/templates/starter/app/lib/fragments.ts +234 -0
  63. package/templates/starter/app/lib/orderFilters.ts +90 -0
  64. package/templates/starter/app/lib/redirect.ts +23 -0
  65. package/templates/starter/app/lib/search.ts +79 -0
  66. package/templates/starter/app/lib/session.ts +72 -0
  67. package/templates/starter/app/lib/variants.ts +46 -0
  68. package/templates/starter/app/root.tsx +209 -0
  69. package/templates/starter/app/routes/$.tsx +11 -0
  70. package/templates/starter/app/routes/[robots.txt].tsx +117 -0
  71. package/templates/starter/app/routes/[sitemap.xml].tsx +16 -0
  72. package/templates/starter/app/routes/_index.tsx +167 -0
  73. package/templates/starter/app/routes/account.$.tsx +9 -0
  74. package/templates/starter/app/routes/account._index.tsx +5 -0
  75. package/templates/starter/app/routes/account.addresses.tsx +516 -0
  76. package/templates/starter/app/routes/account.orders.$id.tsx +222 -0
  77. package/templates/starter/app/routes/account.orders._index.tsx +222 -0
  78. package/templates/starter/app/routes/account.profile.tsx +133 -0
  79. package/templates/starter/app/routes/account.tsx +97 -0
  80. package/templates/starter/app/routes/account_.authorize.tsx +5 -0
  81. package/templates/starter/app/routes/account_.login.tsx +7 -0
  82. package/templates/starter/app/routes/account_.logout.tsx +11 -0
  83. package/templates/starter/app/routes/api.$version.[graphql.json].tsx +14 -0
  84. package/templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +129 -0
  85. package/templates/starter/app/routes/blogs.$blogHandle._index.tsx +175 -0
  86. package/templates/starter/app/routes/blogs._index.tsx +109 -0
  87. package/templates/starter/app/routes/cart.$lines.tsx +70 -0
  88. package/templates/starter/app/routes/cart.tsx +117 -0
  89. package/templates/starter/app/routes/collections.$handle.tsx +161 -0
  90. package/templates/starter/app/routes/collections._index.tsx +133 -0
  91. package/templates/starter/app/routes/collections.all.tsx +122 -0
  92. package/templates/starter/app/routes/discount.$code.tsx +48 -0
  93. package/templates/starter/app/routes/pages.$handle.tsx +88 -0
  94. package/templates/starter/app/routes/policies.$handle.tsx +93 -0
  95. package/templates/starter/app/routes/policies._index.tsx +69 -0
  96. package/templates/starter/app/routes/products.$handle.tsx +232 -0
  97. package/templates/starter/app/routes/search.tsx +426 -0
  98. package/templates/starter/app/routes/sitemap.$type.$page[.xml].tsx +23 -0
  99. package/templates/starter/app/routes.ts +9 -0
  100. package/templates/starter/app/styles/app.css +574 -0
  101. package/templates/starter/app/styles/reset.css +139 -0
  102. package/templates/starter/app/styles/tailwind.css +116 -0
  103. package/templates/starter/customer-accountapi.generated.d.ts +543 -0
  104. package/templates/starter/env.d.ts +7 -0
  105. package/templates/starter/eslint.config.js +247 -0
  106. package/templates/starter/guides/predictiveSearch/predictiveSearch.jpg +0 -0
  107. package/templates/starter/guides/predictiveSearch/predictiveSearch.md +394 -0
  108. package/templates/starter/guides/search/search.jpg +0 -0
  109. package/templates/starter/guides/search/search.md +335 -0
  110. package/templates/starter/package.json +71 -0
  111. package/templates/starter/postcss.config.js +6 -0
  112. package/templates/starter/public/.gitkeep +0 -0
  113. package/templates/starter/react-router.config.ts +13 -0
  114. package/templates/starter/server.ts +59 -0
  115. package/templates/starter/storefrontapi.generated.d.ts +1264 -0
  116. package/templates/starter/tailwind.config.js +83 -0
  117. package/templates/starter/tsconfig.json +67 -0
  118. 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=""