@shopify/hydrogen 0.6.1 → 0.7.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/dist/esnext/components/AddToCartButton/AddToCartButton.client.d.ts +1 -1
- package/dist/esnext/components/AddToCartButton/AddToCartButton.client.js +3 -3
- package/dist/esnext/components/BuyNowButton/BuyNowButton.client.js +1 -5
- package/dist/esnext/components/CartProvider/hooks.js +4 -4
- package/dist/esnext/components/Link/Link.client.d.ts +1 -0
- package/dist/esnext/components/Link/Link.client.js +1 -0
- package/dist/esnext/components/Link/index.d.ts +1 -0
- package/dist/esnext/components/Link/index.js +1 -0
- package/dist/esnext/components/LocalizationProvider/LocalizationContext.client.d.ts +1 -8
- package/dist/esnext/components/LocalizationProvider/LocalizationContext.client.js +1 -3
- package/dist/esnext/components/Metafield/Metafield.client.js +8 -0
- package/dist/esnext/components/Metafield/MetafieldFragment.d.ts +16 -1
- package/dist/esnext/components/Model3D/Model3D.client.d.ts +2 -0
- package/dist/esnext/components/Model3D/Model3D.client.js +1 -1
- package/dist/esnext/components/ProductProvider/context.d.ts +2 -2
- package/dist/esnext/components/ProductProvider/types.d.ts +2 -2
- package/dist/esnext/components/SelectedVariantAddToCartButton/SelectedVariantAddToCartButton.client.d.ts +1 -1
- package/dist/esnext/components/SelectedVariantAddToCartButton/SelectedVariantAddToCartButton.client.js +1 -1
- package/dist/esnext/components/index.d.ts +1 -1
- package/dist/esnext/components/index.js +1 -1
- package/dist/esnext/entry-server.js +50 -20
- package/dist/esnext/foundation/Router/ServerStateRouter.client.js +5 -3
- package/dist/esnext/foundation/ServerStateProvider/ServerStateProvider.client.d.ts +19 -18
- package/dist/esnext/foundation/ServerStateProvider/ServerStateProvider.client.js +22 -22
- package/dist/esnext/foundation/ServerStateProvider/index.d.ts +1 -1
- package/dist/esnext/foundation/ShopifyProvider/ShopifyProvider.d.ts +1 -1
- package/dist/esnext/foundation/ShopifyProvider/ShopifyProvider.js +2 -1
- package/dist/esnext/foundation/ShopifyProvider/ShopifyServerProvider.server.js +1 -1
- package/dist/esnext/foundation/ShopifyProvider/consts.d.ts +1 -0
- package/dist/esnext/foundation/ShopifyProvider/consts.js +1 -0
- package/dist/esnext/{hooks → foundation}/useQuery/QueryProvider.d.ts +0 -0
- package/dist/esnext/{hooks → foundation}/useQuery/QueryProvider.js +0 -0
- package/dist/esnext/{hooks → foundation}/useQuery/hooks.d.ts +0 -0
- package/dist/esnext/{hooks → foundation}/useQuery/hooks.js +13 -8
- package/dist/esnext/{hooks → foundation}/useQuery/index.d.ts +0 -0
- package/dist/esnext/{hooks → foundation}/useQuery/index.js +0 -0
- package/dist/esnext/foundation/useServerState/use-server-state.d.ts +1 -1
- package/dist/esnext/framework/ClientMarker/ClientMarker.d.ts +2 -3
- package/dist/esnext/framework/ClientMarker/ClientMarker.js +24 -1
- package/dist/esnext/framework/Hydration/Cache.client.js +5 -2
- package/dist/esnext/framework/Hydration/ServerComponentResponse.server.d.ts +10 -0
- package/dist/esnext/framework/Hydration/ServerComponentResponse.server.js +13 -0
- package/dist/esnext/framework/Hydration/client-imports.d.ts +1 -0
- package/dist/esnext/framework/Hydration/client-imports.js +25 -0
- package/dist/esnext/framework/Hydration/react-utils.js +1 -0
- package/dist/esnext/framework/Hydration/wire.server.js +1 -1
- package/dist/esnext/framework/graphiql.js +1 -1
- package/dist/esnext/framework/middleware.d.ts +5 -1
- package/dist/esnext/framework/middleware.js +12 -8
- package/dist/esnext/framework/plugins/resolver.d.ts +1 -0
- package/dist/esnext/framework/plugins/resolver.js +3 -0
- package/dist/esnext/framework/plugins/vite-plugin-hydrogen-config.js +4 -1
- package/dist/esnext/framework/plugins/vite-plugin-hydrogen-middleware.js +8 -2
- package/dist/esnext/framework/plugins/vite-plugin-react-server-components-shim.js +41 -75
- package/dist/esnext/framework/server-components.d.ts +2 -10
- package/dist/esnext/framework/server-components.js +64 -68
- package/dist/esnext/graphql/graphql-constants.d.ts +51 -14
- package/dist/esnext/graphql/graphql-constants.js +89 -15
- package/dist/esnext/handle-event.js +8 -4
- package/dist/esnext/hooks/index.d.ts +1 -1
- package/dist/esnext/hooks/index.js +1 -1
- package/dist/esnext/hooks/useAvailableCountries/useAvailableCountries.js +1 -1
- package/dist/esnext/hooks/useCountry/useCountry.js +1 -1
- package/dist/esnext/hooks/useMoney/hooks.js +2 -16
- package/dist/esnext/hooks/useParsedMetafields/useParsedMetafields.d.ts +2 -3
- package/dist/esnext/hooks/useProductOptions/types.d.ts +2 -3
- package/dist/esnext/hooks/useShopQuery/hooks.js +1 -1
- package/dist/esnext/types.d.ts +19 -3
- package/dist/esnext/utilities/flattenConnection/flattenConnection.d.ts +0 -7
- package/dist/esnext/utilities/flattenConnection/flattenConnection.js +0 -7
- package/dist/esnext/utilities/isClient/isClient.d.ts +0 -4
- package/dist/esnext/utilities/isClient/isClient.js +0 -4
- package/dist/esnext/utilities/isServer/isServer.d.ts +0 -4
- package/dist/esnext/utilities/isServer/isServer.js +0 -4
- package/dist/esnext/utilities/parseMetafieldValue/parseMetafieldValue.d.ts +2 -33
- package/dist/esnext/utilities/parseMetafieldValue/parseMetafieldValue.js +0 -31
- package/dist/esnext/version.d.ts +1 -1
- package/dist/esnext/version.js +1 -1
- package/dist/node/foundation/ShopifyProvider/consts.d.ts +1 -0
- package/dist/node/foundation/ShopifyProvider/consts.js +4 -0
- package/dist/node/framework/ClientMarker/ClientMarker.d.ts +2 -3
- package/dist/node/framework/ClientMarker/ClientMarker.js +26 -3
- package/dist/node/framework/Hydration/Cache.client.js +7 -20
- package/dist/node/framework/Hydration/ServerComponentResponse.server.d.ts +10 -0
- package/dist/node/framework/Hydration/ServerComponentResponse.server.js +13 -0
- package/dist/node/framework/Hydration/client-imports.d.ts +1 -0
- package/dist/node/framework/Hydration/client-imports.js +28 -0
- package/dist/node/framework/Hydration/react-utils.js +1 -0
- package/dist/node/framework/Hydration/wire.server.js +1 -1
- package/dist/node/framework/graphiql.js +2 -2
- package/dist/node/framework/middleware.d.ts +5 -1
- package/dist/node/framework/middleware.js +15 -9
- package/dist/node/framework/plugins/resolver.d.ts +1 -0
- package/dist/node/framework/plugins/resolver.js +7 -0
- package/dist/node/framework/plugins/vite-plugin-hydrogen-config.js +4 -1
- package/dist/node/framework/plugins/vite-plugin-hydrogen-middleware.js +8 -2
- package/dist/node/framework/plugins/vite-plugin-react-server-components-shim.js +41 -75
- package/dist/node/framework/server-components.d.ts +2 -10
- package/dist/node/framework/server-components.js +69 -71
- package/dist/node/handle-event.js +8 -4
- package/dist/node/types.d.ts +19 -3
- package/dist/node/utilities/flattenConnection/flattenConnection.d.ts +0 -7
- package/dist/node/utilities/flattenConnection/flattenConnection.js +0 -7
- package/dist/node/utilities/isClient/isClient.d.ts +0 -4
- package/dist/node/utilities/isClient/isClient.js +0 -4
- package/dist/node/utilities/isServer/isServer.d.ts +0 -4
- package/dist/node/utilities/isServer/isServer.js +0 -4
- package/dist/node/utilities/parseMetafieldValue/parseMetafieldValue.d.ts +2 -33
- package/dist/node/utilities/parseMetafieldValue/parseMetafieldValue.js +0 -31
- package/dist/node/version.d.ts +1 -1
- package/dist/node/version.js +1 -1
- package/dist/worker/framework/Hydration/ServerComponentResponse.server.d.ts +10 -0
- package/dist/worker/framework/Hydration/ServerComponentResponse.server.js +13 -0
- package/dist/worker/handle-event.js +8 -4
- package/dist/worker/types.d.ts +19 -3
- package/marker.js +1 -1
- package/package.json +11 -7
- package/dist/node/foundation/Router/DefaultRoutes.d.ts +0 -20
- package/dist/node/foundation/Router/DefaultRoutes.js +0 -78
- package/dist/node/foundation/Router/index.d.ts +0 -1
- package/dist/node/foundation/Router/index.js +0 -5
- package/dist/node/foundation/ServerStateProvider/ServerStateProvider.client.d.ts +0 -24
- package/dist/node/foundation/ServerStateProvider/ServerStateProvider.client.js +0 -66
- package/dist/node/foundation/ServerStateProvider/index.d.ts +0 -1
- package/dist/node/foundation/ServerStateProvider/index.js +0 -6
- package/dist/node/foundation/ShopifyProvider/ShopifyContext.d.ts +0 -1
- package/dist/node/foundation/ShopifyProvider/ShopifyContext.js +0 -5
- package/dist/node/foundation/ShopifyProvider/ShopifyProvider.d.ts +0 -7
- package/dist/node/foundation/ShopifyProvider/ShopifyProvider.js +0 -42
- package/dist/node/foundation/ShopifyProvider/ShopifyServerProvider.server.d.ts +0 -8
- package/dist/node/foundation/ShopifyProvider/ShopifyServerProvider.server.js +0 -14
- package/dist/node/foundation/ShopifyProvider/index.d.ts +0 -1
- package/dist/node/foundation/ShopifyProvider/index.js +0 -6
- package/dist/node/foundation/ShopifyProvider/types.d.ts +0 -14
- package/dist/node/foundation/ShopifyProvider/types.js +0 -2
- package/dist/node/foundation/index.d.ts +0 -5
- package/dist/node/foundation/index.js +0 -23
- package/dist/node/foundation/useShop/index.d.ts +0 -1
- package/dist/node/foundation/useShop/index.js +0 -5
- package/dist/node/foundation/useShop/use-shop.d.ts +0 -5
- package/dist/node/foundation/useShop/use-shop.js +0 -16
- package/dist/node/hooks/useQuery/QueryProvider.d.ts +0 -6
- package/dist/node/hooks/useQuery/QueryProvider.js +0 -20
- package/dist/node/hooks/useQuery/hooks.d.ts +0 -15
- package/dist/node/hooks/useQuery/hooks.js +0 -67
- package/dist/node/hooks/useQuery/index.d.ts +0 -2
- package/dist/node/hooks/useQuery/index.js +0 -7
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
2
|
import { HydrationContext } from '../Hydration/HydrationContext.server';
|
|
3
3
|
import { renderReactProps } from '../Hydration/react-utils';
|
|
4
|
-
export function
|
|
4
|
+
export function wrapInClientMarker(meta) {
|
|
5
|
+
const { component, name } = meta;
|
|
6
|
+
if (!component ||
|
|
7
|
+
(typeof component !== 'function' &&
|
|
8
|
+
!Object.prototype.hasOwnProperty.call(component, 'render'))) {
|
|
9
|
+
// This is not a React component, return it as is.
|
|
10
|
+
return component;
|
|
11
|
+
}
|
|
12
|
+
// Use object syntax here to make sure the function name
|
|
13
|
+
// comes from the meta params for better error stacks.
|
|
14
|
+
const wrappedComponent = {
|
|
15
|
+
// eslint-disable-next-line react/display-name
|
|
16
|
+
[name]: (props) => React.createElement(ClientMarker, { ...{ props, meta } }),
|
|
17
|
+
}[name];
|
|
18
|
+
// Relay component properties such as `Image.Fragment`
|
|
19
|
+
for (const [key, value] of Object.entries(component)) {
|
|
20
|
+
Object.defineProperty(wrappedComponent, key, {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
value,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return wrappedComponent;
|
|
26
|
+
}
|
|
27
|
+
function ClientMarker({ props: allProps, meta: { name, id, component: Component, named }, }) {
|
|
5
28
|
const isHydrating = useContext(HydrationContext);
|
|
6
29
|
if (!isHydrating)
|
|
7
30
|
return React.createElement(Component, { ...allProps });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createElement, Fragment } from 'react';
|
|
2
2
|
import { wrapPromise } from '../../utilities';
|
|
3
|
+
import importClientComponent from './client-imports';
|
|
3
4
|
const cache = new Map();
|
|
4
5
|
const moduleCache = new Map();
|
|
5
6
|
/**
|
|
@@ -110,7 +111,9 @@ export async function convertHydrationResponseToReactComponents(response) {
|
|
|
110
111
|
function createManifestFromWirePayload(payload) {
|
|
111
112
|
return payload.split('\n').reduce((memo, row) => {
|
|
112
113
|
const [key, ...values] = row.split(':');
|
|
113
|
-
|
|
114
|
+
if (key) {
|
|
115
|
+
memo[key] = JSON.parse(values.join(':'));
|
|
116
|
+
}
|
|
114
117
|
return memo;
|
|
115
118
|
}, {});
|
|
116
119
|
}
|
|
@@ -122,7 +125,7 @@ async function eagerLoadModules(manifest) {
|
|
|
122
125
|
if (moduleCache.has(module.id)) {
|
|
123
126
|
return moduleCache.get(module.id);
|
|
124
127
|
}
|
|
125
|
-
const mod = await
|
|
128
|
+
const mod = await importClientComponent(module.id);
|
|
126
129
|
moduleCache.set(module.id, mod);
|
|
127
130
|
return mod;
|
|
128
131
|
})
|
|
@@ -2,6 +2,10 @@ import { CacheOptions } from '../../types';
|
|
|
2
2
|
export declare class ServerComponentResponse extends Response {
|
|
3
3
|
private wait;
|
|
4
4
|
private cacheOptions?;
|
|
5
|
+
customStatus?: {
|
|
6
|
+
code?: number;
|
|
7
|
+
text?: string;
|
|
8
|
+
};
|
|
5
9
|
/**
|
|
6
10
|
* Allow custom body to be a string or a Promise.
|
|
7
11
|
*/
|
|
@@ -14,6 +18,12 @@ export declare class ServerComponentResponse extends Response {
|
|
|
14
18
|
canStream(): boolean;
|
|
15
19
|
cache(options: CacheOptions): void;
|
|
16
20
|
get cacheControlHeader(): string;
|
|
21
|
+
writeHead({ status, statusText, headers, }?: {
|
|
22
|
+
status?: number;
|
|
23
|
+
statusText?: string;
|
|
24
|
+
headers?: Record<string, any>;
|
|
25
|
+
}): void;
|
|
26
|
+
redirect(location: string, status?: number): void;
|
|
17
27
|
/**
|
|
18
28
|
* Send the response from a Server Component. Renders React components to string,
|
|
19
29
|
* and returns `null` to make React happy.
|
|
@@ -30,6 +30,19 @@ export class ServerComponentResponse extends Response {
|
|
|
30
30
|
};
|
|
31
31
|
return generateCacheControlHeader(options);
|
|
32
32
|
}
|
|
33
|
+
writeHead({ status, statusText, headers, } = {}) {
|
|
34
|
+
if (status || statusText) {
|
|
35
|
+
this.customStatus = { code: status, text: statusText };
|
|
36
|
+
}
|
|
37
|
+
if (headers) {
|
|
38
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
39
|
+
this.headers.set(key, value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
redirect(location, status = 307) {
|
|
44
|
+
this.writeHead({ status, headers: { location } });
|
|
45
|
+
}
|
|
33
46
|
/**
|
|
34
47
|
* Send the response from a Server Component. Renders React components to string,
|
|
35
48
|
* and returns `null` to make React happy.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function importClientComponent(moduleId: string): any;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Transform relative paths to absolute in order
|
|
2
|
+
// to match component IDs from ClientMarker.
|
|
3
|
+
function normalizeComponentPaths(componentObject, prefix) {
|
|
4
|
+
return Object.entries(componentObject).reduce((acc, [key, value]) => {
|
|
5
|
+
acc[prefix + key.replace(/\.\.\//gm, '')] = value;
|
|
6
|
+
return acc;
|
|
7
|
+
}, {});
|
|
8
|
+
}
|
|
9
|
+
// These strings are replaced in a plugin with real globs
|
|
10
|
+
// and paths that depend on the user project structure.
|
|
11
|
+
const allClientComponents = {
|
|
12
|
+
...normalizeComponentPaths(
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
import.meta.glob('__LIB_COMPONENTS_GLOB__'), `__LIB_COMPONENTS_PREFIX__`),
|
|
15
|
+
...normalizeComponentPaths(
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
import.meta.glob('__USER_COMPONENTS_GLOB__'), `__USER_COMPONENTS_PREFIX__`),
|
|
18
|
+
};
|
|
19
|
+
export default function importClientComponent(moduleId) {
|
|
20
|
+
const modImport = allClientComponents[moduleId];
|
|
21
|
+
if (!modImport) {
|
|
22
|
+
throw new Error(`Could not find client component ${moduleId}`);
|
|
23
|
+
}
|
|
24
|
+
return modImport();
|
|
25
|
+
}
|
|
@@ -68,7 +68,7 @@ export function generateWireSyntaxFromRenderedHtml(html) {
|
|
|
68
68
|
.map((component, idx) => {
|
|
69
69
|
return `M${idx + 1}:${JSON.stringify(component)}`;
|
|
70
70
|
})
|
|
71
|
-
.join('\n') + `\nJ0:${JSON.stringify(wireModel)}`);
|
|
71
|
+
.join('\n') + `\nJ0:${JSON.stringify(wireModel)}`).trim();
|
|
72
72
|
}
|
|
73
73
|
function isDomNode(item) {
|
|
74
74
|
return item !== null && typeof item === 'object' && '_owner' in item;
|
|
@@ -10,9 +10,13 @@ declare type HydrogenMiddlewareArgs = {
|
|
|
10
10
|
devServer?: ViteDevServer;
|
|
11
11
|
cache?: Cache;
|
|
12
12
|
};
|
|
13
|
+
export declare function graphiqlMiddleware({ shopifyConfig, dev, }: {
|
|
14
|
+
shopifyConfig: ShopifyConfig;
|
|
15
|
+
dev: boolean;
|
|
16
|
+
}): (request: IncomingMessage, response: http.ServerResponse, next: NextFunction) => Promise<void>;
|
|
13
17
|
/**
|
|
14
18
|
* Provides middleware to Node.js Express-like servers. Used by the Hydrogen
|
|
15
19
|
* Vite dev server plugin as well as production Node.js implementation.
|
|
16
20
|
*/
|
|
17
|
-
export
|
|
21
|
+
export declare function hydrogenMiddleware({ dev, cache, indexTemplate, getServerEntrypoint, devServer, }: HydrogenMiddlewareArgs): (request: IncomingMessage, response: http.ServerResponse, next: NextFunction) => Promise<void>;
|
|
18
22
|
export {};
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { graphiqlHtml } from './graphiql';
|
|
2
2
|
import handleEvent from '../handle-event';
|
|
3
|
-
|
|
4
|
-
* Provides middleware to Node.js Express-like servers. Used by the Hydrogen
|
|
5
|
-
* Vite dev server plugin as well as production Node.js implementation.
|
|
6
|
-
*/
|
|
7
|
-
export default function hydrogenMiddleware({ dev, shopifyConfig, cache, indexTemplate, getServerEntrypoint, devServer, }) {
|
|
3
|
+
export function graphiqlMiddleware({ shopifyConfig, dev, }) {
|
|
8
4
|
return async function (request, response, next) {
|
|
9
5
|
const graphiqlRequest = dev && isGraphiqlRequest(request);
|
|
10
6
|
if (graphiqlRequest) {
|
|
11
7
|
return respondWithGraphiql(response, shopifyConfig);
|
|
12
8
|
}
|
|
9
|
+
next();
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Provides middleware to Node.js Express-like servers. Used by the Hydrogen
|
|
14
|
+
* Vite dev server plugin as well as production Node.js implementation.
|
|
15
|
+
*/
|
|
16
|
+
export function hydrogenMiddleware({ dev, cache, indexTemplate, getServerEntrypoint, devServer, }) {
|
|
17
|
+
return async function (request, response, next) {
|
|
13
18
|
const url = new URL('http://' + request.headers.host + request.originalUrl);
|
|
14
19
|
const isReactHydrationRequest = url.pathname === '/react';
|
|
15
20
|
/**
|
|
@@ -96,9 +101,8 @@ export default function hydrogenMiddleware({ dev, shopifyConfig, cache, indexTem
|
|
|
96
101
|
}
|
|
97
102
|
function shouldInterceptRequest(request, isReactHydrationRequest) {
|
|
98
103
|
var _a;
|
|
99
|
-
return (
|
|
100
|
-
isReactHydrationRequest)
|
|
101
|
-
request.url !== '/favicon.ico');
|
|
104
|
+
return (/text\/html|application\/hydrogen/.test((_a = request.headers['accept']) !== null && _a !== void 0 ? _a : '') ||
|
|
105
|
+
isReactHydrationRequest);
|
|
102
106
|
}
|
|
103
107
|
/**
|
|
104
108
|
* /graphiql and /___graphql are supported
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolve(path: string): string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default () => {
|
|
2
2
|
return {
|
|
3
3
|
name: 'vite-plugin-hydrogen-config',
|
|
4
|
-
config: () => ({
|
|
4
|
+
config: (_, env) => ({
|
|
5
5
|
resolve: {
|
|
6
6
|
alias: {
|
|
7
7
|
/**
|
|
@@ -52,6 +52,9 @@ export default () => {
|
|
|
52
52
|
'react-router-dom',
|
|
53
53
|
],
|
|
54
54
|
},
|
|
55
|
+
define: {
|
|
56
|
+
__DEV__: env.mode !== 'production',
|
|
57
|
+
},
|
|
55
58
|
}),
|
|
56
59
|
};
|
|
57
60
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { promises as fs } from 'fs';
|
|
3
|
-
import hydrogenMiddleware from '../middleware';
|
|
3
|
+
import { hydrogenMiddleware, graphiqlMiddleware } from '../middleware';
|
|
4
4
|
import { InMemoryCache } from '../cache/in-memory';
|
|
5
5
|
export default (shopifyConfig, pluginOptions) => {
|
|
6
6
|
return {
|
|
@@ -17,7 +17,13 @@ export default (shopifyConfig, pluginOptions) => {
|
|
|
17
17
|
const indexHtml = await fs.readFile(resolve('index.html'), 'utf-8');
|
|
18
18
|
return await server.transformIndexHtml(url, indexHtml);
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
// The default vite middleware rewrites the URL `/graphqil` to `/index.html`
|
|
21
|
+
// By running this middleware first, we avoid that.
|
|
22
|
+
server.middlewares.use(graphiqlMiddleware({
|
|
23
|
+
shopifyConfig,
|
|
24
|
+
dev: true,
|
|
25
|
+
}));
|
|
26
|
+
return () => server.middlewares.use(hydrogenMiddleware({
|
|
21
27
|
dev: true,
|
|
22
28
|
shopifyConfig,
|
|
23
29
|
indexTemplate: getIndexTemplate,
|
|
@@ -1,37 +1,16 @@
|
|
|
1
|
+
import { normalizePath } from 'vite';
|
|
1
2
|
import path from 'path';
|
|
3
|
+
import { proxyClientComponent } from '../server-components';
|
|
4
|
+
import { resolve } from './resolver';
|
|
2
5
|
import { promises as fs } from 'fs';
|
|
3
|
-
import glob from 'fast-glob';
|
|
4
|
-
import { tagClientComponents, wrapClientComponents } from '../server-components';
|
|
5
6
|
export default () => {
|
|
6
7
|
let config;
|
|
7
|
-
let clientManifest;
|
|
8
8
|
return {
|
|
9
9
|
name: 'vite-plugin-react-server-components-shim',
|
|
10
10
|
enforce: 'pre',
|
|
11
11
|
configResolved(_config) {
|
|
12
12
|
config = _config;
|
|
13
13
|
},
|
|
14
|
-
buildStart() {
|
|
15
|
-
if (config.build.ssr || config.command !== 'build')
|
|
16
|
-
return;
|
|
17
|
-
const hydrogenComponentPath = path.dirname(
|
|
18
|
-
// eslint-disable-next-line node/no-missing-require
|
|
19
|
-
require.resolve('@shopify/hydrogen'));
|
|
20
|
-
/**
|
|
21
|
-
* Grab each of the client components in this project and emit them as chunks.
|
|
22
|
-
* This allows us to dynamically import them later during partial hydration in production.
|
|
23
|
-
*/
|
|
24
|
-
const clientComponents = glob
|
|
25
|
-
.sync(path.resolve(config.root, './src/**/*.client.(j|t)sx'))
|
|
26
|
-
.concat(glob.sync(path.join(hydrogenComponentPath, '**/*.client.js')));
|
|
27
|
-
clientComponents.forEach((id) => {
|
|
28
|
-
this.emitFile({
|
|
29
|
-
type: 'chunk',
|
|
30
|
-
id,
|
|
31
|
-
preserveSignature: 'strict',
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
},
|
|
35
14
|
async resolveId(source, importer) {
|
|
36
15
|
if (!importer)
|
|
37
16
|
return null;
|
|
@@ -56,39 +35,51 @@ export default () => {
|
|
|
56
35
|
'When using Hydrogen components within Client Components, use the `@shopify/hydrogen/client` entrypoint instead.');
|
|
57
36
|
}
|
|
58
37
|
},
|
|
59
|
-
|
|
38
|
+
async load(id, options) {
|
|
60
39
|
if (!isSSR(options))
|
|
61
40
|
return null;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
41
|
+
// Wrapped components won't match this becase they end in ?no-proxy
|
|
42
|
+
if (/\.client\.[jt]sx?$/.test(id)) {
|
|
43
|
+
return proxyClientComponent({ id });
|
|
44
|
+
}
|
|
45
|
+
// Temporary fix for sourcemap warnings in client components. This can be fixed in @vitejs/react-plugin.
|
|
46
|
+
// `react-ssr-prepass` sourcemap seems to be broken and crashes in workers/Jest.
|
|
47
|
+
if (id.endsWith('?no-proxy') ||
|
|
48
|
+
id.includes('dist/react-ssr-prepass.es.js')) {
|
|
49
|
+
return {
|
|
50
|
+
code: await fs.readFile(id.split('?')[0], 'utf-8'),
|
|
51
|
+
map: { mappings: '' },
|
|
52
|
+
};
|
|
73
53
|
}
|
|
54
|
+
return null;
|
|
74
55
|
},
|
|
75
|
-
|
|
76
|
-
if (!isSSR(options))
|
|
77
|
-
return null;
|
|
56
|
+
transform(code, id) {
|
|
78
57
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
58
|
+
* In order to allow dynamic component imports from RSC, we use Vite's import.meta.glob.
|
|
59
|
+
* This replaces the glob import path placeholders in importer-dev.ts with resolved paths
|
|
60
|
+
* to all client components (both user and Hydrogen components).
|
|
61
|
+
*
|
|
62
|
+
* NOTE: Glob import paths MUST be relative to the importer file (client-imports.ts) in
|
|
63
|
+
* order to get the `?v=xxx` querystring from Vite added to the import URL.
|
|
64
|
+
* If the paths are relative to the root instead, Vite won't add the querystring
|
|
65
|
+
* and we will have duplicated files in the browser (with duplicated contexts, etc).
|
|
82
66
|
*/
|
|
83
|
-
if (id.includes('
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
67
|
+
if (id.includes('/Hydration/client-imports')) {
|
|
68
|
+
// eslint-disable-next-line node/no-missing-require
|
|
69
|
+
const hydrogenPath = path.dirname(resolve('@shopify/hydrogen'));
|
|
70
|
+
const importerPath = path.join(hydrogenPath, 'framework', 'Hydration');
|
|
71
|
+
const importerToRootPath = normalizePath(path.relative(importerPath, config.root));
|
|
72
|
+
const [importerToRootNested] = importerToRootPath.match(/(\.\.\/)+(\.\.)?/) || [];
|
|
73
|
+
const userPrefix = path.normalize(path.join(importerPath, importerToRootNested.replace(/\/?$/, path.sep)));
|
|
74
|
+
const userGlob = path.join(importerToRootPath, 'src', '**/*.client.[jt]sx');
|
|
75
|
+
const libPrefix = hydrogenPath + path.sep;
|
|
76
|
+
const libGlob = path.join(path.relative(importerPath, hydrogenPath), 'components', '**/*.client.js');
|
|
77
|
+
return code
|
|
78
|
+
.replace('__USER_COMPONENTS_PREFIX__', normalizePath(userPrefix))
|
|
79
|
+
.replace('__USER_COMPONENTS_GLOB__', normalizePath(userGlob))
|
|
80
|
+
.replace('__LIB_COMPONENTS_PREFIX__', normalizePath(libPrefix))
|
|
81
|
+
.replace('__LIB_COMPONENTS_GLOB__', normalizePath(libGlob));
|
|
90
82
|
}
|
|
91
|
-
return null;
|
|
92
83
|
},
|
|
93
84
|
};
|
|
94
85
|
// Mitigation for upcoming minor Vite update
|
|
@@ -106,29 +97,4 @@ export default () => {
|
|
|
106
97
|
}
|
|
107
98
|
return false;
|
|
108
99
|
}
|
|
109
|
-
async function getFileFromClientManifest(manifestId) {
|
|
110
|
-
const manifest = await getClientManifest();
|
|
111
|
-
const fileName = '/' + manifestId.split('/').pop();
|
|
112
|
-
const matchingKey = Object.keys(manifest).find((key) => key.endsWith(fileName));
|
|
113
|
-
if (!matchingKey) {
|
|
114
|
-
throw new Error(`Could not find a matching entry in the manifest for: ${manifestId}`);
|
|
115
|
-
}
|
|
116
|
-
return manifest[matchingKey].file;
|
|
117
|
-
}
|
|
118
|
-
async function getClientManifest() {
|
|
119
|
-
if (config.command !== 'build') {
|
|
120
|
-
return {};
|
|
121
|
-
}
|
|
122
|
-
if (clientManifest)
|
|
123
|
-
return clientManifest;
|
|
124
|
-
try {
|
|
125
|
-
const manifest = JSON.parse(await fs.readFile(path.resolve(config.root, './dist/client/manifest.json'), 'utf-8'));
|
|
126
|
-
clientManifest = manifest;
|
|
127
|
-
return manifest;
|
|
128
|
-
}
|
|
129
|
-
catch (e) {
|
|
130
|
-
console.error(`Failed to load client manifest:`);
|
|
131
|
-
console.error(e);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
100
|
};
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
export declare function
|
|
1
|
+
export declare function proxyClientComponent({ id, src, }: {
|
|
2
2
|
id: string;
|
|
3
|
-
|
|
4
|
-
root: string;
|
|
5
|
-
isBuild: boolean;
|
|
3
|
+
src?: string;
|
|
6
4
|
}): Promise<string>;
|
|
7
|
-
export declare function tagClientComponents(src: string, additionalReferences?: Array<string | RegExp>): {
|
|
8
|
-
code: string;
|
|
9
|
-
map: {
|
|
10
|
-
mappings: string;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
@@ -1,72 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
? id
|
|
14
|
-
.slice(id.indexOf('?fromServer=') + '?fromServer='.length)
|
|
15
|
-
.split(',')
|
|
16
|
-
.map((name) => name.split(':').shift())
|
|
17
|
-
: [];
|
|
18
|
-
/**
|
|
19
|
-
* Determine the id of the chunk to be imported. If we're building
|
|
20
|
-
* the production bundle, we need to reference the chunk generated
|
|
21
|
-
* during the client manifest. Otherwise, we can pass the normalizedId
|
|
22
|
-
* and Vite's dev server will load it as expected.
|
|
23
|
-
*/
|
|
24
|
-
const importId = isBuild
|
|
25
|
-
? '/' + (await getManifestFile(manifestId))
|
|
26
|
-
: normalizedId;
|
|
27
|
-
const isNamedExport = names.length > 0;
|
|
28
|
-
let code = `import React from 'react';
|
|
29
|
-
import {ClientMarker} from '@shopify/hydrogen/marker';`;
|
|
30
|
-
if (!isNamedExport) {
|
|
31
|
-
code += `
|
|
32
|
-
import _Component from '${normalizedId}';
|
|
33
|
-
|
|
34
|
-
export default function _ClientComponent(props) {
|
|
35
|
-
return React.createElement(ClientMarker, { name: '${name}', id: '${importId}', props, component: _Component, named: false });
|
|
36
|
-
}
|
|
37
|
-
export * from '${normalizedId}';`;
|
|
1
|
+
import { init, parse } from 'es-module-lexer';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import { transformWithEsbuild } from 'vite';
|
|
4
|
+
import MagicString from 'magic-string';
|
|
5
|
+
export async function proxyClientComponent({ id, src, }) {
|
|
6
|
+
var _a;
|
|
7
|
+
const defaultComponentName = (_a = id.split('/').pop()) === null || _a === void 0 ? void 0 : _a.split('.').shift();
|
|
8
|
+
// Modify the import ID to avoid infinite wraps
|
|
9
|
+
const importFrom = `${id}?no-proxy`;
|
|
10
|
+
await init;
|
|
11
|
+
if (!src) {
|
|
12
|
+
src = await fs.readFile(id, 'utf-8');
|
|
38
13
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
14
|
+
const { code } = await transformWithEsbuild(src, id);
|
|
15
|
+
const [, exportStatements] = parse(code);
|
|
16
|
+
const hasDefaultExport = exportStatements.includes('default');
|
|
17
|
+
// Split namedImports in components to wrap and everything else (e.g. GQL Fragments)
|
|
18
|
+
const namedImports = exportStatements.reduce((acc, i) => {
|
|
19
|
+
if (i !== 'default') {
|
|
20
|
+
// Add here any other naming pattern for a non-component export
|
|
21
|
+
if (/^use[A-Z]|Fragment$|Context$|^[A-Z_]+$/.test(i)) {
|
|
22
|
+
acc.other.push(i);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
acc.components.push(i);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return acc;
|
|
29
|
+
}, { components: [], other: [] });
|
|
30
|
+
if (!hasDefaultExport && namedImports.components.length === 0) {
|
|
31
|
+
return `export * from '${importFrom}';\n`;
|
|
50
32
|
}
|
|
51
|
-
|
|
33
|
+
const s = new MagicString(`import {wrapInClientMarker} from '@shopify/hydrogen/marker';`);
|
|
34
|
+
s.append('\nimport ');
|
|
35
|
+
if (hasDefaultExport) {
|
|
36
|
+
s.append(defaultComponentName);
|
|
37
|
+
if (namedImports.components.length > 0) {
|
|
38
|
+
s.append(', ');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (namedImports.components.length) {
|
|
42
|
+
s.append('* as namedImports');
|
|
43
|
+
}
|
|
44
|
+
s.append(` from '${importFrom}';\n\n`);
|
|
45
|
+
// Re-export other stuff directly without wrapping
|
|
46
|
+
if (namedImports.other.length > 0) {
|
|
47
|
+
s.append(`export {${namedImports.other.join(', ')}} from '${importFrom}';\n`);
|
|
48
|
+
}
|
|
49
|
+
if (hasDefaultExport) {
|
|
50
|
+
s.append(generateComponentExport({
|
|
51
|
+
id,
|
|
52
|
+
componentName: defaultComponentName,
|
|
53
|
+
isDefault: true,
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
namedImports.components.forEach((name) => s.append(generateComponentExport({
|
|
57
|
+
id,
|
|
58
|
+
componentName: name,
|
|
59
|
+
isDefault: false,
|
|
60
|
+
})));
|
|
61
|
+
return s.toString();
|
|
52
62
|
}
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const fromModulePattern = modulePatterns.join('|');
|
|
59
|
-
/**
|
|
60
|
-
* Default exports
|
|
61
|
-
* @see https://rubular.com/r/XZjsrolet5twvB
|
|
62
|
-
*/
|
|
63
|
-
let regex = new RegExp(`import\\s*\\w*\\s*from\\s*(?:'|")(?:(${fromModulePattern}))`, 'g');
|
|
64
|
-
let code = src.replace(regex, (mod) => mod + '?fromServer');
|
|
65
|
-
/**
|
|
66
|
-
* Named exports
|
|
67
|
-
* @see https://rubular.com/r/6qdREcs4T9Nw1e
|
|
68
|
-
*/
|
|
69
|
-
regex = new RegExp(`import\\s*{([\\w\\s,]+)}\\s*from\\s*(?:'|")(?:(${fromModulePattern}))`, 'g');
|
|
70
|
-
code = code.replace(regex, (mod, imports) => `${mod}?fromServer=${imports.replace(/ as /g, ':').replace(/ /g, '')}`);
|
|
71
|
-
return { code, map: { mappings: '' } };
|
|
63
|
+
function generateComponentExport({ id, isDefault, componentName, }) {
|
|
64
|
+
const component = isDefault
|
|
65
|
+
? componentName
|
|
66
|
+
: `namedImports['${componentName}']`;
|
|
67
|
+
return `export ${isDefault ? 'default' : `const ${componentName} =`} wrapInClientMarker({ name: '${componentName}', id: '${id}', component: ${component}, named: ${!isDefault} });\n`;
|
|
72
68
|
}
|