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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Back to Vite 🎉 #1728

Merged
merged 86 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
0123e59
Bump Remix to 2.6.0
frandiox Feb 2, 2024
0ae5420
Super experimental Vite
frandiox Feb 2, 2024
ff1119e
Add vite-skeleton template
frandiox Feb 2, 2024
f05b284
Add more missing features
frandiox Feb 2, 2024
5d41ecf
Refactor and support Subrequest Profiler
frandiox Feb 5, 2024
04f5688
Pass client info as bindings instead of replacing strings
frandiox Feb 5, 2024
7d97634
Basic support for inspector
frandiox Feb 5, 2024
6900b54
Simplify servers
frandiox Feb 5, 2024
dff3412
Fix server HMR
frandiox Feb 5, 2024
c28c105
Log request lines
frandiox Feb 5, 2024
f0a7d92
Add comments
frandiox Feb 5, 2024
afef76f
Refactor and simplify
frandiox Feb 5, 2024
2427725
Extract remix patch
frandiox Feb 5, 2024
a730ba6
Fix requests with body
frandiox Feb 5, 2024
6e0c2eb
Turn Vite template into diff example
frandiox Feb 5, 2024
b696fb0
Fix Vite diffs
frandiox Feb 5, 2024
dbbf9c5
Cleanup
frandiox Feb 5, 2024
65e38f2
Add --entry flag
frandiox Feb 6, 2024
83cca68
Add build-vite command
frandiox Feb 6, 2024
88816ed
Fix build
frandiox Feb 7, 2024
9627d5f
Merge branch 'main' into fd-h2-vite
frandiox Feb 8, 2024
69c4c96
Support for deploy command
frandiox Feb 8, 2024
e7fa64e
Support --diff in deploy command
frandiox Feb 8, 2024
9681fd0
Avoid consuming redirects in Vite<>Workerd
frandiox Feb 8, 2024
d285945
Simplify HMR logic
frandiox Feb 8, 2024
e951a07
Warmup Vite cache
frandiox Feb 8, 2024
fb418c8
Support local dev and reload config
frandiox Feb 8, 2024
47f2230
Cleanup
frandiox Feb 8, 2024
c7b2a69
Minor refactor and fix
frandiox Feb 8, 2024
eeb205b
Support subrequest profiler
frandiox Feb 8, 2024
baef8f3
Cleanup
frandiox Feb 9, 2024
8f641e2
Update to Vite 5.1 stable
frandiox Feb 9, 2024
05fb592
Adjust isbot version
frandiox Feb 9, 2024
5169eb3
Move files
frandiox Feb 9, 2024
451ee15
Generate types in CLI for entry points
frandiox Feb 9, 2024
09499ba
Refactor, remove early publicUrl dependency
frandiox Feb 9, 2024
bd01b6d
Extract as a Vite plugin
frandiox Feb 9, 2024
8c036fb
Fix minioxygen entry in main compiler
frandiox Feb 9, 2024
bfcd576
Fix sourcemaps
frandiox Feb 13, 2024
f7d501f
Extract shared utilities
frandiox Feb 13, 2024
9df94ad
Move Vite config responsibilities to plugin
frandiox Feb 13, 2024
658263c
Simplify workerd entry module, add comments
frandiox Feb 13, 2024
3c74831
Simplify server HMR
frandiox Feb 13, 2024
d4ec1e6
Avoid running in Remix child compiler
frandiox Feb 13, 2024
316a2a8
Fix HMR when using the same server for HTTP and WS
frandiox Feb 14, 2024
e48e7a6
Default to minify:true for worker build
frandiox Feb 14, 2024
2144d93
Refactor subrequest profiler sourcemap handling to make it more flexible
frandiox Feb 14, 2024
6a58903
Partially fix subrequest profiler locations for Vite
frandiox Feb 14, 2024
3d05c1c
Refactor use workers option in Miniflare
frandiox Feb 14, 2024
c6ab9c6
Rename worker
frandiox Feb 14, 2024
315f1f1
Split plugins
frandiox Feb 14, 2024
df60bbc
Transform SSR entry to accept import.meta.hot automatically
frandiox Feb 14, 2024
287c791
Fix CSS flash in development
frandiox Feb 14, 2024
63a72dc
Split middlewares in files, rename worker entry
frandiox Feb 14, 2024
493d703
Make setupFunctions more robust
frandiox Feb 15, 2024
5a52f50
Fix critical CSS crash for virtual routes
frandiox Feb 15, 2024
c1d5921
Decouple Oxygen plugin from Hydrogen's subrequest profiler
frandiox Feb 15, 2024
aee2de7
Rename internal option
frandiox Feb 15, 2024
f4ff939
Move virtual routes logic to Hydrogen plugin
frandiox Feb 15, 2024
10bd529
Improve logs
frandiox Feb 15, 2024
68a9c10
Improve --template flag errors
frandiox Feb 15, 2024
ec74e74
Fix env variables in Vite
frandiox Feb 15, 2024
840ff01
Support creating examples from local repo using LOCAL_DEV=true
frandiox Feb 15, 2024
f03390e
Allow skipping skeleton files in diff examples
frandiox Feb 15, 2024
e9b234d
Update Vite template
frandiox Feb 15, 2024
dbbf3d7
Fix paths in JS Vite projects
frandiox Feb 15, 2024
bda9590
Add readme to Vite example
frandiox Feb 15, 2024
a88bdaa
Remove old build required plugin
frandiox Feb 16, 2024
b59d5e9
Merge branch 'main' into fd-h2-vite
frandiox Feb 16, 2024
fe47031
Fix timing issue in test
frandiox Feb 16, 2024
280177e
Fix another timing issue
frandiox Feb 16, 2024
ea74b95
Show virtual routes in a banner
frandiox Feb 16, 2024
2fcd845
Merge branch 'main' into fd-h2-vite
frandiox Feb 19, 2024
e75e556
Update to Remix 2.7
frandiox Feb 21, 2024
869fbd9
Dedupe package-lock.json
frandiox Feb 21, 2024
b4cbea7
Remove unneeded parameter after upgrading Remix
frandiox Feb 21, 2024
b85203b
Remove unneeded dependencies after upgrading Remix
frandiox Feb 21, 2024
3887bf4
Fix set-cookie header
frandiox Feb 28, 2024
6c32e42
Merge main
frandiox Feb 28, 2024
b860947
Remove copied code
frandiox Feb 28, 2024
3cb5c75
Merge branch 'main' into fd-h2-vite
frandiox Feb 28, 2024
1267721
Remove old code
frandiox Feb 28, 2024
48541ec
Cleanup build output
frandiox Feb 28, 2024
d71a906
Merge branch 'main' into fd-h2-vite
frandiox Feb 29, 2024
d3fc97e
Make Oxygen plugin agnostic from Remix
frandiox Feb 29, 2024
7427390
Changesets
frandiox Feb 29, 2024
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
27 changes: 27 additions & 0 deletions .changeset/nasty-files-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
'@shopify/cli-hydrogen': minor
---

Add experimental support for Vite projects.

In the Vite config of you Vite<>Remix project, import and use the new experimental Hydrogen and Oxygen plugins, and include them before Remix:

```ts
import {defineConfig} from 'vite';
import {hydrogen, oxygen} from '@shopify/cli-hydrogen/experimental-vite';
import {vitePlugin as remix} from '@remix-run/dev';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
plugins: [
hydrogen(), // Adds utilities like GraphiQL and Subrequest Profiler
oxygen(), // Runs your app using the MiniOxygen runtime (closer to production)
remix({buildDirectory: 'dist'}), // Use `dist` to be compatible with `h2 deploy`
tsconfigPaths(),
],
});
```

Then, run `h2 dev-vite` and `h2 build-vite` commands to start and build your app.

Please report any issue with this new feature, and let us know if you have any feedback or suggestions.
64 changes: 64 additions & 0 deletions examples/vite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Hydrogen template: Experimental Vite

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 and Vite.

[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
- Vite
- Shopify CLI
- ESLint
- Prettier
- GraphQL generator
- TypeScript and JavaScript flavors
- Minimal setup of components and routes

## Getting started

**Requirements:**

- Node.js version 18.0.0 or higher

```bash
npm create @shopify/hydrogen@latest -- --template vite
```

## Building for production

```bash
npm run build
```

## Local development

```bash
npm run dev
```

## Setup for using Customer Account API (`/account` section)

### Setup public domain using ngrok

1. Setup a [ngrok](https://ngrok.com/) account and add a permanent domain (ie. `https://<your-ngrok-domain>.app`).
1. Install the [ngrok CLI](https://ngrok.com/download) to use in terminal
1. Start ngrok using `ngrok http --domain=<your-ngrok-domain>.app 3000`

### Include public domain in Customer Account API settings

1. Go to your Shopify admin => `Hydrogen` or `Headless` app/channel => Customer Account API => Application setup
1. Edit `Callback URI(s)` to include `https://<your-ngrok-domain>.app/account/authorize`
1. Edit `Javascript origin(s)` to include your public domain `https://<your-ngrok-domain>.app` or keep it blank
1. Edit `Logout URI` to include your public domain `https://<your-ngrok-domain>.app` or keep it blank

### Prepare Environment variables

Run [`npx shopify hydrogen link`](https://shopify.dev/docs/custom-storefronts/hydrogen/cli#link) or [`npx shopify hydrogen env pull`](https://shopify.dev/docs/custom-storefronts/hydrogen/cli#env-pull) to link this app to your own test shop.

Alternatly, the values of the required environment varaibles "PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID" and "PUBLIC_CUSTOMER_ACCOUNT_API_URL" can be found in customer account api settings in the Hydrogen admin channel.

🗒️ Note that mock.shop doesn't supply these variables automatically.
240 changes: 240 additions & 0 deletions examples/vite/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import {useNonce} from '@shopify/hydrogen';
import {
defer,
type SerializeFrom,
type LoaderFunctionArgs,
} from '@shopify/remix-oxygen';
import {
Links,
Meta,
Outlet,
Scripts,
useMatches,
useRouteError,
useLoaderData,
ScrollRestoration,
isRouteErrorResponse,
type ShouldRevalidateFunction,
} from '@remix-run/react';
import {Layout} from '~/components/Layout';

import './styles/reset.css';
import './styles/app.css';

/**
* This is important to avoid re-fetching root queries on sub-navigations
*/
export const shouldRevalidate: ShouldRevalidateFunction = ({
formMethod,
currentUrl,
nextUrl,
}) => {
// revalidate when a mutation is performed e.g add to cart, login...
if (formMethod && formMethod !== 'GET') {
return true;
}

// revalidate when manually revalidating via useRevalidator
if (currentUrl.toString() === nextUrl.toString()) {
return true;
}

return false;
};

export function links() {
return [
{
rel: 'preconnect',
href: 'https://cdn.shopify.com',
},
{
rel: 'preconnect',
href: 'https://shop.app',
},
{rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg'},
];
}

/**
* Access the result of the root loader from a React component.
*/
export const useRootLoaderData = () => {
const [root] = useMatches();
return root?.data as SerializeFrom<typeof loader>;
};

export async function loader({context}: LoaderFunctionArgs) {
const {storefront, customerAccount, cart} = context;
const publicStoreDomain = context.env.PUBLIC_STORE_DOMAIN;

const isLoggedInPromise = customerAccount.isLoggedIn();

// defer the cart query by not awaiting it
const cartPromise = cart.get();

// defer the footer query (below the fold)
const footerPromise = storefront.query(FOOTER_QUERY, {
cache: storefront.CacheLong(),
variables: {
footerMenuHandle: 'footer', // Adjust to your footer menu handle
},
});

// await the header query (above the fold)
const headerPromise = storefront.query(HEADER_QUERY, {
cache: storefront.CacheLong(),
variables: {
headerMenuHandle: 'main-menu', // Adjust to your header menu handle
},
});

return defer(
{
cart: cartPromise,
footer: footerPromise,
header: await headerPromise,
isLoggedIn: isLoggedInPromise,
publicStoreDomain,
},
{
headers: {
'Set-Cookie': await context.session.commit(),
},
},
);
}

export default function App() {
const nonce = useNonce();
const data = useLoaderData<typeof loader>();

return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Layout {...data}>
<Outlet />
</Layout>
<ScrollRestoration nonce={nonce} />
<Scripts nonce={nonce} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear from the docs if moving forward we can remove the LiveReload component from all the templates (instead of just the vite). But we can test and do that in a different PR.

</body>
</html>
);
}

export function ErrorBoundary() {
const error = useRouteError();
const rootData = useRootLoaderData();
const nonce = useNonce();
let errorMessage = 'Unknown error';
let errorStatus = 500;

if (isRouteErrorResponse(error)) {
errorMessage = error?.data?.message ?? error.data;
errorStatus = error.status;
} else if (error instanceof Error) {
errorMessage = error.message;
}

return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Layout {...rootData}>
<div className="route-error">
<h1>Oops</h1>
<h2>{errorStatus}</h2>
{errorMessage && (
<fieldset>
<pre>{errorMessage}</pre>
</fieldset>
)}
</div>
</Layout>
<ScrollRestoration nonce={nonce} />
<Scripts nonce={nonce} />
</body>
</html>
);
}

const MENU_FRAGMENT = `#graphql
fragment MenuItem on MenuItem {
id
resourceId
tags
title
type
url
}
fragment ChildMenuItem on MenuItem {
...MenuItem
}
fragment ParentMenuItem on MenuItem {
...MenuItem
items {
...ChildMenuItem
}
}
fragment Menu on Menu {
id
items {
...ParentMenuItem
}
}
` as const;

const HEADER_QUERY = `#graphql
fragment Shop on Shop {
id
name
description
primaryDomain {
url
}
brand {
logo {
image {
url
}
}
}
}
query Header(
$country: CountryCode
$headerMenuHandle: String!
$language: LanguageCode
) @inContext(language: $language, country: $country) {
shop {
...Shop
}
menu(handle: $headerMenuHandle) {
...Menu
}
}
${MENU_FRAGMENT}
` as const;

const FOOTER_QUERY = `#graphql
query Footer(
$country: CountryCode
$footerMenuHandle: String!
$language: LanguageCode
) @inContext(language: $language, country: $country) {
menu(handle: $footerMenuHandle) {
...Menu
}
}
${MENU_FRAGMENT}
` as const;
47 changes: 47 additions & 0 deletions examples/vite/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/// <reference types="vite/client" />
/// <reference types="@shopify/remix-oxygen" />
/// <reference types="@shopify/oxygen-workers-types" />

// Enhance TypeScript's built-in typings.
import '@total-typescript/ts-reset';

import type {
Storefront,
CustomerAccount,
HydrogenCart,
} from '@shopify/hydrogen';
import type {AppSession} from '~/lib/session';

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_STORE_DOMAIN: string;
PUBLIC_STOREFRONT_ID: string;
PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID: string;
PUBLIC_CUSTOMER_ACCOUNT_API_URL: string;
}
}

declare module '@shopify/remix-oxygen' {
/**
* Declare local additions to the Remix loader context.
*/
export interface AppLoadContext {
env: Env;
cart: HydrogenCart;
storefront: Storefront;
customerAccount: CustomerAccount;
session: AppSession;
waitUntil: ExecutionContext['waitUntil'];
}
}
Loading
Loading