Thing Time 📦
This commit is contained in:
parent
65e14d6a48
commit
2ec0dc1e55
@ -1,4 +0,0 @@
|
|||||||
build
|
|
||||||
node_modules
|
|
||||||
bin
|
|
||||||
*.d.ts
|
|
@ -1,6 +0,0 @@
|
|||||||
/**
|
|
||||||
* @type {import("@types/eslint").Linter.BaseConfig}
|
|
||||||
*/
|
|
||||||
module.exports = {
|
|
||||||
extends: ['plugin:hydrogen/recommended', 'plugin:hydrogen/typescript'],
|
|
||||||
};
|
|
8
app/.gitignore
vendored
8
app/.gitignore
vendored
@ -1,8 +0,0 @@
|
|||||||
node_modules
|
|
||||||
|
|
||||||
/.cache
|
|
||||||
/build
|
|
||||||
/dist
|
|
||||||
/public/build
|
|
||||||
/.mf
|
|
||||||
.env
|
|
@ -1 +0,0 @@
|
|||||||
schema: node_modules/@shopify/hydrogen-react/storefront.schema.json
|
|
@ -1,2 +0,0 @@
|
|||||||
@shopify:registry=https://registry.npmjs.com
|
|
||||||
progress=false
|
|
@ -1,42 +0,0 @@
|
|||||||
# Hydrogen template: Hello World
|
|
||||||
|
|
||||||
Hydrogen is Shopify’s stack for headless commerce. Hydrogen is designed to dovetail with [Remix](https://remix.run/), Shopify’s full stack web framework. This template contains a **minimal setup** of components, queries and tooling to get started with Hydrogen.
|
|
||||||
|
|
||||||
[Check out Hydrogen docs](https://shopify.dev/custom-storefronts/hydrogen)
|
|
||||||
[Get familiar with Remix](https://remix.run/docs/en/v1)
|
|
||||||
|
|
||||||
## What's included
|
|
||||||
|
|
||||||
- Remix
|
|
||||||
- Hydrogen
|
|
||||||
- Oxygen
|
|
||||||
- Shopify CLI
|
|
||||||
- ESLint
|
|
||||||
- Prettier
|
|
||||||
- GraphQL generator
|
|
||||||
- TypeScript and JavaScript flavors
|
|
||||||
- Minimal setup of components and routes
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
**Requirements:**
|
|
||||||
|
|
||||||
- Node.js version 16.14.0 or higher
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm create @shopify/hydrogen@latest --template hello-world
|
|
||||||
```
|
|
||||||
|
|
||||||
Remember to update `.env` with your shop's domain and Storefront API token!
|
|
||||||
|
|
||||||
## Building for production
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Local development
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
|
26292
app/package-lock.json
generated
26292
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "hello-world",
|
|
||||||
"private": true,
|
|
||||||
"sideEffects": false,
|
|
||||||
"version": "0.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"build": "shopify hydrogen build",
|
|
||||||
"dev": "shopify hydrogen dev",
|
|
||||||
"preview": "npm run build && shopify hydrogen preview",
|
|
||||||
"lint": "eslint --no-error-on-unmatched-pattern --ext .js,.ts,.jsx,.tsx .",
|
|
||||||
"typecheck": "tsc --noEmit",
|
|
||||||
"g": "shopify hydrogen generate"
|
|
||||||
},
|
|
||||||
"prettier": "@shopify/prettier-config",
|
|
||||||
"dependencies": {
|
|
||||||
"@remix-run/react": "1.12.0",
|
|
||||||
"@shopify/cli": "3.29.0",
|
|
||||||
"@shopify/cli-hydrogen": "^4.0.8",
|
|
||||||
"@shopify/hydrogen": "^2023.1.5",
|
|
||||||
"@shopify/remix-oxygen": "^1.0.3",
|
|
||||||
"graphql": "^16.6.0",
|
|
||||||
"graphql-tag": "^2.12.6",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@remix-run/dev": "1.12.0",
|
|
||||||
"@shopify/oxygen-workers-types": "^3.17.2",
|
|
||||||
"@shopify/prettier-config": "^1.1.2",
|
|
||||||
"@types/eslint": "^8.4.10",
|
|
||||||
"@types/react": "^18.0.20",
|
|
||||||
"@types/react-dom": "^18.0.6",
|
|
||||||
"eslint": "^8.20.0",
|
|
||||||
"eslint-plugin-hydrogen": "0.12.2",
|
|
||||||
"prettier": "^2.8.4",
|
|
||||||
"typescript": "^4.9.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.13"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none">
|
|
||||||
<style>
|
|
||||||
.stroke {
|
|
||||||
stroke: #000;
|
|
||||||
}
|
|
||||||
.fill {
|
|
||||||
fill: #000;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.stroke {
|
|
||||||
stroke: #fff;
|
|
||||||
}
|
|
||||||
.fill {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<path
|
|
||||||
class="stroke"
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M16.1 16.04 1 8.02 6.16 5.3l5.82 3.09 4.88-2.57-5.82-3.1L16.21 0l15.1 8.02-5.17 2.72-5.5-2.91-4.88 2.57 5.5 2.92-5.16 2.72Z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
class="fill"
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M16.1 32 1 23.98l5.16-2.72 5.82 3.08 4.88-2.57-5.82-3.08 5.17-2.73 15.1 8.02-5.17 2.72-5.5-2.92-4.88 2.58 5.5 2.92L16.1 32Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 690 B |
@ -1,22 +0,0 @@
|
|||||||
/** @type {import('@remix-run/dev').AppConfig} */
|
|
||||||
|
|
||||||
require('dotenv')?.config();
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
appDirectory: 'src',
|
|
||||||
ignoredRouteFiles: ['**/.*'],
|
|
||||||
watchPaths: ['./public'],
|
|
||||||
server: './server.ts',
|
|
||||||
/**
|
|
||||||
* The following settings are required to deploy Hydrogen apps to Oxygen:
|
|
||||||
*/
|
|
||||||
publicPath: (process.env.HYDROGEN_ASSET_BASE_URL ?? '/') + 'build/',
|
|
||||||
assetsBuildDirectory: 'dist/client/build',
|
|
||||||
serverBuildPath: 'dist/worker/index.js',
|
|
||||||
serverMainFields: ['browser', 'module', 'main'],
|
|
||||||
serverConditions: ['worker', process.env.NODE_ENV],
|
|
||||||
serverDependenciesToBundle: 'all',
|
|
||||||
serverModuleFormat: 'esm',
|
|
||||||
serverPlatform: 'neutral',
|
|
||||||
serverMinify: process.env.NODE_ENV === 'production',
|
|
||||||
};
|
|
36
app/remix.env.d.ts
vendored
36
app/remix.env.d.ts
vendored
@ -1,36 +0,0 @@
|
|||||||
/// <reference types="@remix-run/dev" />
|
|
||||||
/// <reference types="@shopify/remix-oxygen" />
|
|
||||||
/// <reference types="@shopify/oxygen-workers-types" />
|
|
||||||
|
|
||||||
import type {Storefront} from '@shopify/hydrogen';
|
|
||||||
import type {HydrogenSession} from '../server';
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
/**
|
|
||||||
* A global `process` object is only available during build to access NODE_ENV.
|
|
||||||
*/
|
|
||||||
const process: {env: {NODE_ENV: 'production' | 'development'}};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Declare expected Env parameter in fetch handler.
|
|
||||||
*/
|
|
||||||
interface Env {
|
|
||||||
SESSION_SECRET: string;
|
|
||||||
PUBLIC_STOREFRONT_API_TOKEN: string;
|
|
||||||
PRIVATE_STOREFRONT_API_TOKEN: string;
|
|
||||||
PUBLIC_STOREFRONT_API_VERSION: string;
|
|
||||||
PUBLIC_STORE_DOMAIN: string;
|
|
||||||
PUBLIC_STOREFRONT_ID: string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Declare local additions to `AppLoadContext` to include the session utilities we injected in `server.ts`.
|
|
||||||
*/
|
|
||||||
declare module '@shopify/remix-oxygen' {
|
|
||||||
export interface AppLoadContext {
|
|
||||||
session: HydrogenSession;
|
|
||||||
storefront: Storefront;
|
|
||||||
env: Env;
|
|
||||||
}
|
|
||||||
}
|
|
131
app/server.ts
131
app/server.ts
@ -1,131 +0,0 @@
|
|||||||
// Virtual entry point for the app
|
|
||||||
import * as remixBuild from '@remix-run/dev/server-build';
|
|
||||||
import {createStorefrontClient, storefrontRedirect} from '@shopify/hydrogen';
|
|
||||||
import {
|
|
||||||
createRequestHandler,
|
|
||||||
getBuyerIp,
|
|
||||||
createCookieSessionStorage,
|
|
||||||
type SessionStorage,
|
|
||||||
type Session,
|
|
||||||
} from '@shopify/remix-oxygen';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export a fetch handler in module format.
|
|
||||||
*/
|
|
||||||
export default {
|
|
||||||
async fetch(
|
|
||||||
request: Request,
|
|
||||||
env: Env,
|
|
||||||
executionContext: ExecutionContext,
|
|
||||||
): Promise<Response> {
|
|
||||||
try {
|
|
||||||
/**
|
|
||||||
* Open a cache instance in the worker and a custom session instance.
|
|
||||||
*/
|
|
||||||
if (!env?.SESSION_SECRET) {
|
|
||||||
throw new Error('SESSION_SECRET environment variable is not set');
|
|
||||||
}
|
|
||||||
|
|
||||||
const waitUntil = (p: Promise<any>) => executionContext.waitUntil(p);
|
|
||||||
const [cache, session] = await Promise.all([
|
|
||||||
caches.open('hydrogen'),
|
|
||||||
HydrogenSession.init(request, [env.SESSION_SECRET]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create Hydrogen's Storefront client.
|
|
||||||
*/
|
|
||||||
const {storefront} = createStorefrontClient({
|
|
||||||
cache,
|
|
||||||
waitUntil,
|
|
||||||
buyerIp: getBuyerIp(request),
|
|
||||||
i18n: {language: 'EN', country: 'US'},
|
|
||||||
publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN,
|
|
||||||
privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN,
|
|
||||||
storeDomain: `https://${env.PUBLIC_STORE_DOMAIN}`,
|
|
||||||
storefrontApiVersion: env.PUBLIC_STOREFRONT_API_VERSION || '2023-01',
|
|
||||||
storefrontId: env.PUBLIC_STOREFRONT_ID,
|
|
||||||
requestGroupId: request.headers.get('request-id'),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Remix request handler and pass
|
|
||||||
* Hydrogen's Storefront client to the loader context.
|
|
||||||
*/
|
|
||||||
const handleRequest = createRequestHandler({
|
|
||||||
build: remixBuild,
|
|
||||||
mode: process.env.NODE_ENV,
|
|
||||||
getLoadContext: () => ({session, storefront, env}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await handleRequest(request);
|
|
||||||
|
|
||||||
if (response.status === 404) {
|
|
||||||
/**
|
|
||||||
* Check for redirects only when there's a 404 from the app.
|
|
||||||
* If the redirect doesn't exist, then `storefrontRedirect`
|
|
||||||
* will pass through the 404 response.
|
|
||||||
*/
|
|
||||||
return storefrontRedirect({request, response, storefront});
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(error);
|
|
||||||
return new Response('An unexpected error occurred', {status: 500});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a custom session implementation for your Hydrogen shop.
|
|
||||||
* Feel free to customize it to your needs, add helper methods, or
|
|
||||||
* swap out the cookie-based implementation with something else!
|
|
||||||
*/
|
|
||||||
class HydrogenSession {
|
|
||||||
constructor(
|
|
||||||
private sessionStorage: SessionStorage,
|
|
||||||
private session: Session,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
static async init(request: Request, secrets: string[]) {
|
|
||||||
const storage = createCookieSessionStorage({
|
|
||||||
cookie: {
|
|
||||||
name: 'session',
|
|
||||||
httpOnly: true,
|
|
||||||
path: '/',
|
|
||||||
sameSite: 'lax',
|
|
||||||
secrets,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const session = await storage.getSession(request.headers.get('Cookie'));
|
|
||||||
|
|
||||||
return new this(storage, session);
|
|
||||||
}
|
|
||||||
|
|
||||||
get(key: string) {
|
|
||||||
return this.session.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
return this.sessionStorage.destroySession(this.session);
|
|
||||||
}
|
|
||||||
|
|
||||||
flash(key: string, value: any) {
|
|
||||||
this.session.flash(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
unset(key: string) {
|
|
||||||
this.session.unset(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
set(key: string, value: any) {
|
|
||||||
this.session.set(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
commit() {
|
|
||||||
return this.sessionStorage.commitSession(this.session);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
import {RemixBrowser} from '@remix-run/react';
|
|
||||||
import {hydrateRoot} from 'react-dom/client';
|
|
||||||
|
|
||||||
hydrateRoot(document, <RemixBrowser />);
|
|
@ -1,21 +0,0 @@
|
|||||||
import type {EntryContext} from '@shopify/remix-oxygen';
|
|
||||||
import {RemixServer} from '@remix-run/react';
|
|
||||||
import {renderToReadableStream} from 'react-dom/server';
|
|
||||||
|
|
||||||
export default async function handleRequest(
|
|
||||||
request: Request,
|
|
||||||
responseStatusCode: number,
|
|
||||||
responseHeaders: Headers,
|
|
||||||
remixContext: EntryContext,
|
|
||||||
) {
|
|
||||||
const body = await renderToReadableStream(
|
|
||||||
<RemixServer context={remixContext} url={request.url} />,
|
|
||||||
);
|
|
||||||
|
|
||||||
responseHeaders.set('Content-Type', 'text/html');
|
|
||||||
|
|
||||||
return new Response(body, {
|
|
||||||
status: responseStatusCode,
|
|
||||||
headers: responseHeaders,
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
import {
|
|
||||||
type LinksFunction,
|
|
||||||
type MetaFunction,
|
|
||||||
type LoaderArgs,
|
|
||||||
} from '@shopify/remix-oxygen';
|
|
||||||
import {
|
|
||||||
Links,
|
|
||||||
Meta,
|
|
||||||
Outlet,
|
|
||||||
Scripts,
|
|
||||||
ScrollRestoration,
|
|
||||||
useLoaderData,
|
|
||||||
} from '@remix-run/react';
|
|
||||||
import type {Shop} from '@shopify/hydrogen/storefront-api-types';
|
|
||||||
import styles from './styles/app.css';
|
|
||||||
import favicon from '../public/favicon.svg';
|
|
||||||
|
|
||||||
export const links: LinksFunction = () => {
|
|
||||||
return [
|
|
||||||
{rel: 'stylesheet', href: styles},
|
|
||||||
{
|
|
||||||
rel: 'preconnect',
|
|
||||||
href: 'https://cdn.shopify.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rel: 'preconnect',
|
|
||||||
href: 'https://shop.app',
|
|
||||||
},
|
|
||||||
{rel: 'icon', type: 'image/svg+xml', href: favicon},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const meta: MetaFunction = () => ({
|
|
||||||
charset: 'utf-8',
|
|
||||||
viewport: 'width=device-width,initial-scale=1',
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function loader({context}: LoaderArgs) {
|
|
||||||
const layout = await context.storefront.query<{shop: Shop}>(LAYOUT_QUERY);
|
|
||||||
return {layout};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const data = useLoaderData<typeof loader>();
|
|
||||||
|
|
||||||
const {name} = data.layout.shop;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<Meta />
|
|
||||||
<Links />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Hello, {name}</h1>
|
|
||||||
<p>This is a custom storefront powered by Hydrogen</p>
|
|
||||||
<Outlet />
|
|
||||||
<ScrollRestoration />
|
|
||||||
<Scripts />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const LAYOUT_QUERY = `#graphql
|
|
||||||
query layout {
|
|
||||||
shop {
|
|
||||||
name
|
|
||||||
description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
@ -1,31 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
background: #FFFFFF;
|
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
|
||||||
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"],
|
|
||||||
"compilerOptions": {
|
|
||||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
|
||||||
"isolatedModules": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"target": "ES2022",
|
|
||||||
"strict": true,
|
|
||||||
"allowJs": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"types": ["@shopify/oxygen-workers-types"],
|
|
||||||
"paths": {
|
|
||||||
"~/*": ["src/*"]
|
|
||||||
},
|
|
||||||
|
|
||||||
// Remix takes care of building everything in `./app` with `remix build`.
|
|
||||||
// Wrangler takes care of building everything in `./worker` with `wrangler start` / `wrangler publish`.
|
|
||||||
"noEmit": true
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user