@shopify/hydrogen 0.14.0 → 0.16.1
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/CHANGELOG.md +124 -0
- package/dist/esnext/client.d.ts +4 -0
- package/dist/esnext/client.js +4 -0
- package/dist/esnext/components/CartProvider/CartProvider.client.js +23 -0
- package/dist/esnext/components/DevTools.d.ts +1 -0
- package/dist/esnext/components/DevTools.js +128 -0
- package/dist/esnext/components/Link/Link.client.js +1 -1
- package/dist/esnext/constants.d.ts +6 -0
- package/dist/esnext/constants.js +6 -0
- package/dist/esnext/entry-client.js +7 -4
- package/dist/esnext/entry-server.d.ts +1 -1
- package/dist/esnext/entry-server.js +29 -15
- package/dist/esnext/foundation/Analytics/Analytics.client.d.ts +3 -0
- package/dist/esnext/foundation/Analytics/Analytics.client.js +28 -0
- package/dist/esnext/foundation/Analytics/Analytics.server.d.ts +1 -0
- package/dist/esnext/foundation/Analytics/Analytics.server.js +38 -0
- package/dist/esnext/foundation/Analytics/ClientAnalytics.d.ts +24 -0
- package/dist/esnext/foundation/Analytics/ClientAnalytics.js +91 -0
- package/dist/esnext/foundation/Analytics/ServerAnalyticsRoute.server.d.ts +2 -0
- package/dist/esnext/foundation/Analytics/ServerAnalyticsRoute.server.js +33 -0
- package/dist/esnext/foundation/Analytics/const.d.ts +8 -0
- package/dist/esnext/foundation/Analytics/const.js +8 -0
- package/dist/esnext/foundation/Analytics/hook.d.ts +1 -0
- package/dist/esnext/foundation/Analytics/hook.js +7 -0
- package/dist/esnext/foundation/Analytics/index.d.ts +2 -0
- package/dist/esnext/foundation/Analytics/index.js +2 -0
- package/dist/esnext/foundation/Analytics/types.d.ts +5 -0
- package/dist/esnext/foundation/Analytics/types.js +1 -0
- package/dist/esnext/foundation/Analytics/utils.d.ts +1 -0
- package/dist/esnext/foundation/Analytics/utils.js +8 -0
- package/dist/esnext/foundation/Boomerang/Boomerang.client.js +3 -1
- package/dist/esnext/foundation/Route/Route.server.js +4 -0
- package/dist/esnext/foundation/Router/BrowserRouter.client.js +68 -15
- package/dist/esnext/foundation/ServerRequestProvider/ServerRequestProvider.js +1 -1
- package/dist/esnext/foundation/ShopifyProvider/types.d.ts +2 -5
- package/dist/esnext/foundation/fetchSync/client/fetchSync.d.ts +10 -0
- package/dist/esnext/foundation/fetchSync/client/fetchSync.js +27 -0
- package/dist/esnext/foundation/fetchSync/server/fetchSync.d.ts +8 -0
- package/dist/esnext/foundation/fetchSync/server/fetchSync.js +27 -0
- package/dist/esnext/foundation/fetchSync/types.d.ts +5 -0
- package/dist/esnext/foundation/fetchSync/types.js +1 -0
- package/dist/esnext/foundation/useQuery/hooks.d.ts +4 -2
- package/dist/esnext/foundation/useQuery/hooks.js +10 -6
- package/dist/esnext/foundation/useUrl/useUrl.js +8 -1
- package/dist/esnext/framework/Hydration/ServerComponentRequest.server.d.ts +1 -0
- package/dist/esnext/framework/Hydration/ServerComponentRequest.server.js +11 -5
- package/dist/esnext/framework/cache/in-memory.js +5 -5
- package/dist/esnext/framework/cache.d.ts +1 -2
- package/dist/esnext/framework/cache.js +67 -22
- package/dist/esnext/framework/plugin.js +10 -0
- package/dist/esnext/framework/plugins/vite-plugin-css-modules-rsc.js +1 -1
- package/dist/esnext/framework/plugins/vite-plugin-hydrogen-config.js +11 -2
- package/dist/esnext/hooks/useShopQuery/hooks.js +32 -25
- package/dist/esnext/index.d.ts +2 -0
- package/dist/esnext/index.js +2 -0
- package/dist/esnext/types.d.ts +6 -1
- package/dist/esnext/utilities/apiRoutes.d.ts +2 -3
- package/dist/esnext/utilities/apiRoutes.js +14 -9
- package/dist/esnext/utilities/hash.d.ts +2 -0
- package/dist/esnext/utilities/hash.js +7 -0
- package/dist/esnext/utilities/log/log-cache-api-status.js +1 -1
- package/dist/esnext/utilities/log/log-cache-header.js +1 -1
- package/dist/esnext/utilities/log/log-query-timeline.js +1 -1
- package/dist/esnext/utilities/storefrontApi.d.ts +4 -0
- package/dist/esnext/utilities/storefrontApi.js +21 -0
- package/dist/esnext/utilities/suspense.d.ts +5 -0
- package/dist/esnext/utilities/suspense.js +32 -0
- package/dist/esnext/utilities/template.js +1 -1
- package/dist/esnext/version.d.ts +1 -1
- package/dist/esnext/version.js +1 -1
- package/dist/node/constants.d.ts +6 -0
- package/dist/node/constants.js +7 -1
- package/dist/node/entry-server.d.ts +1 -1
- package/dist/node/entry-server.js +28 -17
- package/dist/node/foundation/Analytics/Analytics.client.d.ts +3 -0
- package/dist/node/foundation/Analytics/Analytics.client.js +32 -0
- package/dist/node/foundation/Analytics/Analytics.server.d.ts +1 -0
- package/dist/node/foundation/Analytics/Analytics.server.js +45 -0
- package/dist/node/foundation/Analytics/ClientAnalytics.d.ts +24 -0
- package/dist/node/foundation/Analytics/ClientAnalytics.js +94 -0
- package/dist/node/foundation/Analytics/ServerAnalyticsRoute.server.d.ts +2 -0
- package/dist/node/foundation/Analytics/ServerAnalyticsRoute.server.js +37 -0
- package/dist/node/foundation/Analytics/const.d.ts +8 -0
- package/dist/node/foundation/Analytics/const.js +11 -0
- package/dist/node/foundation/Analytics/hook.d.ts +1 -0
- package/dist/node/foundation/Analytics/hook.js +11 -0
- package/dist/node/foundation/Analytics/index.d.ts +2 -0
- package/dist/node/foundation/Analytics/index.js +7 -0
- package/dist/node/foundation/Analytics/types.d.ts +5 -0
- package/dist/node/foundation/Analytics/types.js +2 -0
- package/dist/node/foundation/Analytics/utils.d.ts +1 -0
- package/dist/node/foundation/Analytics/utils.js +12 -0
- package/dist/node/foundation/Router/BrowserRouter.client.js +67 -14
- package/dist/node/foundation/ServerRequestProvider/ServerRequestProvider.js +2 -2
- package/dist/node/foundation/ShopifyProvider/types.d.ts +2 -5
- package/dist/node/framework/Hydration/ServerComponentRequest.server.d.ts +1 -0
- package/dist/node/framework/Hydration/ServerComponentRequest.server.js +13 -7
- package/dist/node/framework/cache/in-memory.js +5 -5
- package/dist/node/framework/cache.d.ts +1 -2
- package/dist/node/framework/cache.js +71 -27
- package/dist/node/framework/plugin.js +10 -0
- package/dist/node/framework/plugins/vite-plugin-css-modules-rsc.js +1 -1
- package/dist/node/framework/plugins/vite-plugin-hydrogen-config.js +11 -2
- package/dist/node/types.d.ts +6 -1
- package/dist/node/utilities/apiRoutes.d.ts +2 -3
- package/dist/node/utilities/apiRoutes.js +14 -9
- package/dist/node/utilities/flattenConnection/flattenConnection.d.ts +6 -0
- package/dist/node/utilities/flattenConnection/flattenConnection.js +15 -0
- package/dist/node/utilities/flattenConnection/index.d.ts +1 -0
- package/dist/node/utilities/flattenConnection/index.js +5 -0
- package/dist/node/utilities/hash.d.ts +2 -0
- package/dist/node/utilities/hash.js +11 -0
- package/dist/node/utilities/image_size.d.ts +30 -0
- package/dist/node/utilities/image_size.js +110 -0
- package/dist/node/utilities/index.d.ts +11 -0
- package/dist/node/utilities/index.js +32 -0
- package/dist/node/utilities/isClient/index.d.ts +1 -0
- package/dist/node/utilities/isClient/index.js +5 -0
- package/dist/node/utilities/isClient/isClient.d.ts +4 -0
- package/dist/node/utilities/isClient/isClient.js +10 -0
- package/dist/node/utilities/isServer/index.d.ts +1 -0
- package/dist/node/utilities/isServer/index.js +5 -0
- package/dist/node/utilities/isServer/isServer.d.ts +4 -0
- package/dist/node/utilities/isServer/isServer.js +11 -0
- package/dist/node/utilities/load_script.d.ts +3 -0
- package/dist/node/utilities/load_script.js +27 -0
- package/dist/node/utilities/log/log-cache-api-status.js +1 -1
- package/dist/node/utilities/log/log-cache-header.js +2 -2
- package/dist/node/utilities/log/log-query-timeline.js +2 -2
- package/dist/node/utilities/measurement.d.ts +3 -0
- package/dist/node/utilities/measurement.js +103 -0
- package/dist/node/utilities/parseMetafieldValue/index.d.ts +1 -0
- package/dist/node/utilities/parseMetafieldValue/index.js +5 -0
- package/dist/node/utilities/parseMetafieldValue/parseMetafieldValue.d.ts +6 -0
- package/dist/node/utilities/parseMetafieldValue/parseMetafieldValue.js +39 -0
- package/dist/node/utilities/storefrontApi.d.ts +4 -0
- package/dist/node/utilities/storefrontApi.js +25 -0
- package/dist/node/utilities/suspense.d.ts +12 -0
- package/dist/node/utilities/suspense.js +64 -0
- package/dist/node/utilities/template.js +1 -1
- package/dist/node/utilities/video_parameters.d.ts +47 -0
- package/dist/node/utilities/video_parameters.js +27 -0
- package/dist/node/version.d.ts +1 -1
- package/dist/node/version.js +1 -1
- package/package.json +1 -1
- package/vendor/react-server-dom-vite/cjs/react-server-dom-vite-plugin.js +9 -21
- package/vendor/react-server-dom-vite/cjs/react-server-dom-vite-writer.browser.development.server.js +51 -47
- package/vendor/react-server-dom-vite/cjs/react-server-dom-vite-writer.browser.production.min.server.js +30 -29
- package/vendor/react-server-dom-vite/cjs/react-server-dom-vite-writer.node.development.server.js +51 -47
- package/vendor/react-server-dom-vite/cjs/react-server-dom-vite-writer.node.production.min.server.js +17 -17
- package/vendor/react-server-dom-vite/esm/react-server-dom-vite-client-proxy.js +55 -45
- package/vendor/react-server-dom-vite/esm/react-server-dom-vite-plugin.js +9 -21
- package/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js +51 -47
- package/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.node.server.js +51 -47
- package/vendor/react-server-dom-vite/package.json +3 -3
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { getCache } from './runtime';
|
|
2
2
|
import { CacheSeconds, generateCacheControlHeader, } from '../framework/CachingStrategy';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { hashKey } from '../utilities/hash';
|
|
4
|
+
import { logCacheApiStatus } from '../utilities/log';
|
|
5
|
+
function getCacheControlSetting(userCacheOptions, options) {
|
|
6
|
+
if (userCacheOptions && options) {
|
|
7
|
+
return {
|
|
8
|
+
...userCacheOptions,
|
|
9
|
+
...options,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
return userCacheOptions || CacheSeconds();
|
|
14
|
+
}
|
|
5
15
|
}
|
|
6
|
-
export function
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* TODO: Smarter hash
|
|
10
|
-
*/
|
|
11
|
-
return rawKey.map((k) => JSON.stringify(k)).join('');
|
|
16
|
+
export function generateSubRequestCacheControlHeader(userCacheOptions) {
|
|
17
|
+
return generateCacheControlHeader(getCacheControlSetting(userCacheOptions));
|
|
12
18
|
}
|
|
13
19
|
/**
|
|
14
20
|
* Cache API is weird. We just need a full URL, so we make one up.
|
|
@@ -29,8 +35,11 @@ export async function getItemFromCache(key) {
|
|
|
29
35
|
const url = getKeyUrl(hashKey(key));
|
|
30
36
|
const request = new Request(url);
|
|
31
37
|
const response = await cache.match(request);
|
|
32
|
-
if (!response)
|
|
38
|
+
if (!response) {
|
|
39
|
+
logCacheApiStatus('MISS', url);
|
|
33
40
|
return;
|
|
41
|
+
}
|
|
42
|
+
logCacheApiStatus('HIT', url);
|
|
34
43
|
return [await response.json(), response];
|
|
35
44
|
}
|
|
36
45
|
/**
|
|
@@ -43,14 +52,53 @@ export async function setItemInCache(key, value, userCacheOptions) {
|
|
|
43
52
|
}
|
|
44
53
|
const url = getKeyUrl(hashKey(key));
|
|
45
54
|
const request = new Request(url);
|
|
55
|
+
/**
|
|
56
|
+
* We are manually managing staled request by adding this workaround.
|
|
57
|
+
* Why? cache control header support is dependent on hosting platform
|
|
58
|
+
*
|
|
59
|
+
* For example:
|
|
60
|
+
*
|
|
61
|
+
* Cloudflare's Cache API does not support `stale-while-revalidate`.
|
|
62
|
+
* Cloudflare cache control header has a very odd behaviour.
|
|
63
|
+
* Say we have the following cache control header on a request:
|
|
64
|
+
*
|
|
65
|
+
* public, max-age=15, stale-while-revalidate=30
|
|
66
|
+
*
|
|
67
|
+
* When there is a cache.match HIT, the cache control header would become
|
|
68
|
+
*
|
|
69
|
+
* public, max-age=14400, stale-while-revalidate=30
|
|
70
|
+
*
|
|
71
|
+
* == `stale-while-revalidate` workaround ==
|
|
72
|
+
* Update response max-age so that:
|
|
73
|
+
*
|
|
74
|
+
* max-age = max-age + stale-while-revalidate
|
|
75
|
+
*
|
|
76
|
+
* For example:
|
|
77
|
+
*
|
|
78
|
+
* public, max-age=1, stale-while-revalidate=9
|
|
79
|
+
* |
|
|
80
|
+
* V
|
|
81
|
+
* public, max-age=10, stale-while-revalidate=9
|
|
82
|
+
*
|
|
83
|
+
* Store the following information in the response header:
|
|
84
|
+
*
|
|
85
|
+
* cache-put-date - UTC time string of when this request is PUT into cache
|
|
86
|
+
*
|
|
87
|
+
* Note on `cache-put-date`: The `response.headers.get('date')` isn't static. I am
|
|
88
|
+
* not positive what date this is returning but it is never over 500 ms
|
|
89
|
+
* after subtracting from the current timestamp.
|
|
90
|
+
*
|
|
91
|
+
* `isStale` function will use the above information to test for stale-ness of a cached response
|
|
92
|
+
*/
|
|
93
|
+
const cacheControl = getCacheControlSetting(userCacheOptions);
|
|
46
94
|
const headers = new Headers({
|
|
47
|
-
'cache-control': generateSubRequestCacheControlHeader(
|
|
95
|
+
'cache-control': generateSubRequestCacheControlHeader(getCacheControlSetting(cacheControl, {
|
|
96
|
+
maxAge: (cacheControl.maxAge || 0) + (cacheControl.staleWhileRevalidate || 0),
|
|
97
|
+
})),
|
|
98
|
+
'cache-put-date': new Date().toUTCString(),
|
|
48
99
|
});
|
|
49
100
|
const response = new Response(JSON.stringify(value), { headers });
|
|
50
|
-
|
|
51
|
-
* WARNING: Cloudflare's Cache API does not support `stale-while-revalidate`
|
|
52
|
-
* so this implementation will not work as expected on that platform.
|
|
53
|
-
*/
|
|
101
|
+
logCacheApiStatus('PUT', url);
|
|
54
102
|
await cache.put(request, response);
|
|
55
103
|
}
|
|
56
104
|
export async function deleteItemFromCache(key) {
|
|
@@ -59,20 +107,17 @@ export async function deleteItemFromCache(key) {
|
|
|
59
107
|
return;
|
|
60
108
|
const url = getKeyUrl(hashKey(key));
|
|
61
109
|
const request = new Request(url);
|
|
110
|
+
logCacheApiStatus('DELETE', url);
|
|
62
111
|
await cache.delete(request);
|
|
63
112
|
}
|
|
64
113
|
/**
|
|
65
114
|
* Manually check the response to see if it's stale.
|
|
66
115
|
*/
|
|
67
|
-
export function isStale(response) {
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
if (!responseDate
|
|
71
|
-
return false;
|
|
72
|
-
const responseMaxAgeMatch = responseCacheControl.match(/max-age=(\d+)/);
|
|
73
|
-
if (!responseMaxAgeMatch)
|
|
116
|
+
export function isStale(response, userCacheOptions) {
|
|
117
|
+
const responseMaxAge = getCacheControlSetting(userCacheOptions).maxAge || 0;
|
|
118
|
+
const responseDate = response.headers.get('cache-put-date');
|
|
119
|
+
if (!responseDate)
|
|
74
120
|
return false;
|
|
75
|
-
const responseMaxAge = parseInt(responseMaxAgeMatch[1]);
|
|
76
121
|
const ageInMs = new Date().valueOf() - new Date(responseDate).valueOf();
|
|
77
122
|
const age = ageInMs / 1000;
|
|
78
123
|
return age > responseMaxAge;
|
|
@@ -12,6 +12,15 @@ import react from '@vitejs/plugin-react';
|
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import cssModulesRsc from './plugins/vite-plugin-css-modules-rsc';
|
|
14
14
|
export default (shopifyConfig, pluginOptions = {}) => {
|
|
15
|
+
let hydrogenUiPath;
|
|
16
|
+
try {
|
|
17
|
+
hydrogenUiPath = path.join(
|
|
18
|
+
// eslint-disable-next-line node/no-missing-require
|
|
19
|
+
path.dirname(require.resolve('@shopify/hydrogen-ui/client')));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// hydrogen-ui isn't installed, so don't worry about it
|
|
23
|
+
}
|
|
15
24
|
return [
|
|
16
25
|
process.env.VITE_INSPECT && inspect(),
|
|
17
26
|
hydrogenConfig(),
|
|
@@ -24,6 +33,7 @@ export default (shopifyConfig, pluginOptions = {}) => {
|
|
|
24
33
|
rsc({
|
|
25
34
|
clientComponentPaths: [
|
|
26
35
|
path.join(path.dirname(require.resolve('@shopify/hydrogen/package.json'))),
|
|
36
|
+
...[hydrogenUiPath].filter(Boolean),
|
|
27
37
|
],
|
|
28
38
|
isServerComponentImporterAllowed(importer, source) {
|
|
29
39
|
// Always allow the entry server (e.g. App.server.jsx) to be imported
|
|
@@ -20,7 +20,7 @@ export default function cssModulesRsc() {
|
|
|
20
20
|
enforce: 'post',
|
|
21
21
|
transform(code, id) {
|
|
22
22
|
if (id.includes('.module.') && cssMap.has(id)) {
|
|
23
|
-
return code.replace(/export default .*$/gms, `import React from 'react'; export const StyleTag = () => React.createElement('style', {
|
|
23
|
+
return code.replace(/export default .*$/gms, `import React from 'react'; export const StyleTag = () => React.createElement('style', {dangerouslySetInnerHTML: {__html: ${JSON.stringify(cssMap.get(id))}}});`);
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
},
|
|
@@ -36,11 +36,18 @@ export default () => {
|
|
|
36
36
|
// Reload when updating local Hydrogen lib
|
|
37
37
|
server: process.env.LOCAL_DEV && {
|
|
38
38
|
watch: {
|
|
39
|
-
ignored: [
|
|
39
|
+
ignored: [
|
|
40
|
+
'!**/node_modules/@shopify/hydrogen/**',
|
|
41
|
+
'!**/node_modules/@shopify/hydrogen-ui/**',
|
|
42
|
+
],
|
|
40
43
|
},
|
|
41
44
|
},
|
|
42
45
|
optimizeDeps: {
|
|
43
|
-
exclude: [
|
|
46
|
+
exclude: [
|
|
47
|
+
'@shopify/hydrogen/client',
|
|
48
|
+
'@shopify/hydrogen/entry-client',
|
|
49
|
+
'@shopify/hydrogen-ui',
|
|
50
|
+
],
|
|
44
51
|
include: [
|
|
45
52
|
/**
|
|
46
53
|
* Additionally, the following dependencies have trouble loading the
|
|
@@ -57,6 +64,8 @@ export default () => {
|
|
|
57
64
|
'react',
|
|
58
65
|
'react-dom/client',
|
|
59
66
|
'react-server-dom-vite/client-proxy',
|
|
67
|
+
// https://github.com/vitejs/vite/issues/6215
|
|
68
|
+
'react/jsx-runtime',
|
|
60
69
|
],
|
|
61
70
|
},
|
|
62
71
|
define: {
|
|
@@ -1,31 +1,51 @@
|
|
|
1
1
|
import { useShop } from '../../foundation/useShop';
|
|
2
2
|
import { getLoggerWithContext } from '../../utilities/log';
|
|
3
|
-
import {
|
|
4
|
-
import { fetchBuilder, graphqlRequestBody } from '../../utilities';
|
|
3
|
+
import { graphqlRequestBody } from '../../utilities';
|
|
5
4
|
import { getConfig } from '../../framework/config';
|
|
6
5
|
import { useServerRequest } from '../../foundation/ServerRequestProvider';
|
|
7
6
|
import { injectGraphQLTracker } from '../../utilities/graphql-tracker';
|
|
8
7
|
import { sendMessageToClient } from '../../utilities/devtools';
|
|
8
|
+
import { fetchSync } from '../../foundation/fetchSync/server/fetchSync';
|
|
9
9
|
import { META_ENV_SSR } from '../../foundation/ssr-interop';
|
|
10
|
+
import { getStorefrontApiRequestHeaders } from '../../utilities/storefrontApi';
|
|
10
11
|
// Check if the response body has GraphQL errors
|
|
11
12
|
// https://spec.graphql.org/June2018/#sec-Response-Format
|
|
12
|
-
const shouldCacheResponse = (body) => !(body === null ||
|
|
13
|
+
const shouldCacheResponse = ([body]) => { var _a; return !((_a = JSON.parse(body)) === null || _a === void 0 ? void 0 : _a.errors); };
|
|
13
14
|
/**
|
|
14
15
|
* The `useShopQuery` hook allows you to make server-only GraphQL queries to the Storefront API. It must be a descendent of a `ShopifyProvider` component.
|
|
15
16
|
*/
|
|
16
17
|
export function useShopQuery({ query, variables = {}, cache, preload = false, }) {
|
|
17
18
|
var _a;
|
|
19
|
+
/**
|
|
20
|
+
* If no query is passed, we no-op here to allow developers to obey the Rules of Hooks.
|
|
21
|
+
*/
|
|
22
|
+
if (!query) {
|
|
23
|
+
return { data: undefined, errors: undefined };
|
|
24
|
+
}
|
|
18
25
|
if (!META_ENV_SSR) {
|
|
19
26
|
throw new Error('Shopify Storefront API requests should only be made from the server.');
|
|
20
27
|
}
|
|
21
28
|
const serverRequest = useServerRequest();
|
|
22
29
|
const log = getLoggerWithContext(serverRequest);
|
|
23
30
|
const body = query ? graphqlRequestBody(query, variables) : '';
|
|
24
|
-
const {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
const { url, requestInit } = useCreateShopRequest(body);
|
|
32
|
+
let data;
|
|
33
|
+
let useQueryError;
|
|
34
|
+
try {
|
|
35
|
+
data = fetchSync(url, {
|
|
36
|
+
...requestInit,
|
|
37
|
+
cache,
|
|
38
|
+
preload,
|
|
39
|
+
shouldCacheResponse,
|
|
40
|
+
}).json();
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
// Pass-through thrown promise for Suspense functionality
|
|
44
|
+
if (error === null || error === void 0 ? void 0 : error.then) {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
useQueryError = error;
|
|
48
|
+
}
|
|
29
49
|
/**
|
|
30
50
|
* The fetch request itself failed, so we handle that differently than a GraphQL error
|
|
31
51
|
*/
|
|
@@ -95,26 +115,13 @@ export function useShopQuery({ query, variables = {}, cache, preload = false, })
|
|
|
95
115
|
return data;
|
|
96
116
|
}
|
|
97
117
|
function useCreateShopRequest(body) {
|
|
98
|
-
var _a;
|
|
99
118
|
const { storeDomain, storefrontToken, storefrontApiVersion } = useShop();
|
|
100
119
|
const request = useServerRequest();
|
|
101
|
-
const secretToken = typeof Oxygen !== 'undefined'
|
|
102
|
-
? (_a = Oxygen === null || Oxygen === void 0 ? void 0 : Oxygen.env) === null || _a === void 0 ? void 0 : _a.SHOPIFY_STOREFRONT_API_SECRET_TOKEN
|
|
103
|
-
: null;
|
|
104
120
|
const buyerIp = request.getBuyerIp();
|
|
105
|
-
const extraHeaders = {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (secretToken) {
|
|
110
|
-
extraHeaders['Shopify-Storefront-Private-Token'] = secretToken;
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
extraHeaders['X-Shopify-Storefront-Access-Token'] = storefrontToken;
|
|
114
|
-
}
|
|
115
|
-
if (buyerIp) {
|
|
116
|
-
extraHeaders['Shopify-Storefront-Buyer-IP'] = buyerIp;
|
|
117
|
-
}
|
|
121
|
+
const extraHeaders = getStorefrontApiRequestHeaders({
|
|
122
|
+
buyerIp,
|
|
123
|
+
storefrontToken,
|
|
124
|
+
});
|
|
118
125
|
return {
|
|
119
126
|
key: [storeDomain, storefrontApiVersion, body],
|
|
120
127
|
url: `https://${storeDomain}/api/${storefrontApiVersion}/graphql.json`,
|
package/dist/esnext/index.d.ts
CHANGED
|
@@ -13,3 +13,5 @@ export * from './hooks/useShopQuery/hooks';
|
|
|
13
13
|
export * from './foundation/useQuery/hooks';
|
|
14
14
|
export { CartQuery } from './components/CartProvider/cart-queries';
|
|
15
15
|
export { generateCacheControlHeader, NoStore, CacheSeconds, CacheMinutes, CacheHours, CacheDays, CacheWeeks, CacheMonths, CacheCustom, } from './framework/CachingStrategy';
|
|
16
|
+
export { fetchSync } from './foundation/fetchSync/server/fetchSync';
|
|
17
|
+
export { useServerAnalytics } from './foundation/Analytics';
|
package/dist/esnext/index.js
CHANGED
|
@@ -17,3 +17,5 @@ export * from './foundation/useQuery/hooks';
|
|
|
17
17
|
// it from being bundled with other client components
|
|
18
18
|
export { CartQuery } from './components/CartProvider/cart-queries';
|
|
19
19
|
export { generateCacheControlHeader, NoStore, CacheSeconds, CacheMinutes, CacheHours, CacheDays, CacheWeeks, CacheMonths, CacheCustom, } from './framework/CachingStrategy';
|
|
20
|
+
export { fetchSync } from './foundation/fetchSync/server/fetchSync';
|
|
21
|
+
export { useServerAnalytics } from './foundation/Analytics';
|
package/dist/esnext/types.d.ts
CHANGED
|
@@ -35,14 +35,19 @@ export declare type Hook = (params: {
|
|
|
35
35
|
url: URL;
|
|
36
36
|
} & Record<string, any>) => any | Promise<any>;
|
|
37
37
|
export declare type ImportGlobEagerOutput = Record<string, Record<'default' | 'api', any>>;
|
|
38
|
+
export declare type ServerAnalyticsConnector = {
|
|
39
|
+
request: (request: Request, data?: any, contentType?: 'json' | 'text') => void;
|
|
40
|
+
};
|
|
38
41
|
export declare type ServerHandlerConfig = {
|
|
39
42
|
routes?: ImportGlobEagerOutput;
|
|
40
43
|
shopifyConfig: ShopifyConfig;
|
|
44
|
+
serverAnalyticsConnectors?: Array<ServerAnalyticsConnector>;
|
|
41
45
|
};
|
|
42
46
|
export declare type ClientHandlerConfig = {
|
|
43
47
|
shopifyConfig: ShopifyConfig;
|
|
44
48
|
/** React's StrictMode is on by default for your client side app; if you want to turn it off (not recommended), you can pass `false` */
|
|
45
49
|
strictMode?: boolean;
|
|
50
|
+
showDevTools?: boolean;
|
|
46
51
|
};
|
|
47
52
|
export declare type ClientHandler = (App: React.ElementType, config: ClientHandlerConfig) => Promise<void>;
|
|
48
53
|
export interface GraphQLConnection<T> {
|
|
@@ -74,7 +79,7 @@ export interface AllCacheOptions {
|
|
|
74
79
|
sMaxAge?: number;
|
|
75
80
|
staleIfError?: number;
|
|
76
81
|
}
|
|
77
|
-
export declare type CachingStrategy =
|
|
82
|
+
export declare type CachingStrategy = AllCacheOptions;
|
|
78
83
|
export interface HydrogenVitePluginOptions {
|
|
79
84
|
devCache?: boolean;
|
|
80
85
|
purgeQueryCacheOnBuild?: boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ImportGlobEagerOutput, ShopifyConfig } from '../types';
|
|
2
|
+
import type { ServerComponentRequest } from '../framework/Hydration/ServerComponentRequest.server';
|
|
2
3
|
import type { ASTNode } from 'graphql';
|
|
3
4
|
declare type RouteParams = Record<string, string>;
|
|
4
5
|
declare type RequestOptions = {
|
|
@@ -29,8 +30,6 @@ interface QueryShopArgs {
|
|
|
29
30
|
query: ASTNode | string;
|
|
30
31
|
/** An object of the variables for the GraphQL query. */
|
|
31
32
|
variables?: Record<string, any>;
|
|
32
|
-
/** A string corresponding to a valid locale identifier like `en-us` used to make the request. */
|
|
33
|
-
locale?: string;
|
|
34
33
|
}
|
|
35
|
-
export declare function renderApiRoute(request:
|
|
34
|
+
export declare function renderApiRoute(request: ServerComponentRequest, route: ApiRouteMatch, shopifyConfig: ShopifyConfig): Promise<Response | Request>;
|
|
36
35
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { matchPath } from './matchPath';
|
|
2
2
|
import { getLoggerWithContext, logServerResponse } from '../utilities/log/';
|
|
3
3
|
import { fetchBuilder, graphqlRequestBody } from './fetch';
|
|
4
|
+
import { getStorefrontApiRequestHeaders } from './storefrontApi';
|
|
4
5
|
let memoizedRoutes = [];
|
|
5
6
|
let memoizedPages = {};
|
|
6
7
|
export function getApiRoutes(pages, topLevelPath = '*') {
|
|
@@ -64,31 +65,35 @@ export function getApiRouteFromURL(url, routes) {
|
|
|
64
65
|
hasServerComponent: foundRoute.hasServerComponent,
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
|
-
function queryShopBuilder(shopifyConfig) {
|
|
68
|
-
return async function queryShop({ query, variables,
|
|
69
|
-
|
|
70
|
-
const
|
|
68
|
+
function queryShopBuilder(shopifyConfig, request) {
|
|
69
|
+
return async function queryShop({ query, variables, }) {
|
|
70
|
+
const { storeDomain, storefrontApiVersion, storefrontToken } = shopifyConfig;
|
|
71
|
+
const buyerIp = request.getBuyerIp();
|
|
72
|
+
const extraHeaders = getStorefrontApiRequestHeaders({
|
|
73
|
+
buyerIp,
|
|
74
|
+
storefrontToken,
|
|
75
|
+
});
|
|
71
76
|
const fetcher = fetchBuilder(`https://${storeDomain}/api/${storefrontApiVersion}/graphql.json`, {
|
|
72
77
|
method: 'POST',
|
|
73
78
|
body: graphqlRequestBody(query, variables),
|
|
74
79
|
headers: {
|
|
75
|
-
'X-Shopify-Storefront-Access-Token': storefrontToken,
|
|
76
|
-
'Accept-Language': (_a = locale) !== null && _a !== void 0 ? _a : defaultLocale,
|
|
77
80
|
'Content-Type': 'application/json',
|
|
81
|
+
...extraHeaders,
|
|
78
82
|
},
|
|
79
83
|
});
|
|
80
84
|
return await fetcher();
|
|
81
85
|
};
|
|
82
86
|
}
|
|
83
87
|
export async function renderApiRoute(request, route, shopifyConfig) {
|
|
88
|
+
var _a;
|
|
84
89
|
let response;
|
|
85
90
|
const log = getLoggerWithContext(request);
|
|
86
91
|
try {
|
|
87
92
|
response = await route.resource(request, {
|
|
88
93
|
params: route.params,
|
|
89
|
-
queryShop: queryShopBuilder(shopifyConfig),
|
|
94
|
+
queryShop: queryShopBuilder(shopifyConfig, request),
|
|
90
95
|
});
|
|
91
|
-
if (!(response instanceof Response)) {
|
|
96
|
+
if (!(response instanceof Response || response instanceof Request)) {
|
|
92
97
|
if (typeof response === 'string' || response instanceof String) {
|
|
93
98
|
response = new Response(response);
|
|
94
99
|
}
|
|
@@ -105,6 +110,6 @@ export async function renderApiRoute(request, route, shopifyConfig) {
|
|
|
105
110
|
log.error(e);
|
|
106
111
|
response = new Response('Error processing: ' + request.url, { status: 500 });
|
|
107
112
|
}
|
|
108
|
-
logServerResponse('api', request, response.status);
|
|
113
|
+
logServerResponse('api', request, (_a = response.status) !== null && _a !== void 0 ? _a : 200);
|
|
109
114
|
return response;
|
|
110
115
|
}
|
|
@@ -5,5 +5,5 @@ export function logCacheApiStatus(status, url) {
|
|
|
5
5
|
if (!log.options().showCacheApiStatus) {
|
|
6
6
|
return;
|
|
7
7
|
}
|
|
8
|
-
log.debug(gray(`[Cache] ${status === null || status === void 0 ? void 0 : status.padEnd(
|
|
8
|
+
log.debug(gray(`[Cache] ${status === null || status === void 0 ? void 0 : status.padEnd(8)} query ${findQueryName(url)}`));
|
|
9
9
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE, STOREFRONT_API_SECRET_TOKEN_HEADER, STOREFRONT_API_PUBLIC_TOKEN_HEADER, STOREFRONT_API_BUYER_IP_HEADER, } from '../constants';
|
|
2
|
+
export function getStorefrontApiRequestHeaders({ buyerIp, storefrontToken, }) {
|
|
3
|
+
var _a;
|
|
4
|
+
const headers = {};
|
|
5
|
+
const secretToken = typeof Oxygen !== 'undefined'
|
|
6
|
+
? (_a = Oxygen === null || Oxygen === void 0 ? void 0 : Oxygen.env) === null || _a === void 0 ? void 0 : _a[OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE]
|
|
7
|
+
: null;
|
|
8
|
+
/**
|
|
9
|
+
* Only pass one type of storefront token at a time.
|
|
10
|
+
*/
|
|
11
|
+
if (secretToken) {
|
|
12
|
+
headers[STOREFRONT_API_SECRET_TOKEN_HEADER] = secretToken;
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
headers[STOREFRONT_API_PUBLIC_TOKEN_HEADER] = storefrontToken;
|
|
16
|
+
}
|
|
17
|
+
if (buyerIp) {
|
|
18
|
+
headers[STOREFRONT_API_BUYER_IP_HEADER] = buyerIp;
|
|
19
|
+
}
|
|
20
|
+
return headers;
|
|
21
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { QueryKey } from '../types';
|
|
1
2
|
/**
|
|
2
3
|
* Wrap the fetch promise in a way that React Suspense understands.
|
|
3
4
|
* Essentially, keep throwing something until you have legit data.
|
|
@@ -5,3 +6,7 @@
|
|
|
5
6
|
export declare function wrapPromise<T>(promise: Promise<T>): {
|
|
6
7
|
read: () => T;
|
|
7
8
|
};
|
|
9
|
+
declare type Await<T> = T extends Promise<infer V> ? V : never;
|
|
10
|
+
export declare const suspendFunction: <Fn extends () => Promise<unknown>>(key: QueryKey, fn: Fn) => Await<ReturnType<Fn>>;
|
|
11
|
+
export declare const preloadFunction: (key: QueryKey, fn: () => Promise<unknown>) => unknown;
|
|
12
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hashKey } from './hash';
|
|
1
2
|
/**
|
|
2
3
|
* Wrap the fetch promise in a way that React Suspense understands.
|
|
3
4
|
* Essentially, keep throwing something until you have legit data.
|
|
@@ -24,3 +25,34 @@ export function wrapPromise(promise) {
|
|
|
24
25
|
};
|
|
25
26
|
return { read };
|
|
26
27
|
}
|
|
28
|
+
const browserCache = {};
|
|
29
|
+
/**
|
|
30
|
+
* Perform an async function in a synchronous way for Suspense support.
|
|
31
|
+
* To be used only in the client.
|
|
32
|
+
* Inspired by https://github.com/pmndrs/suspend-react
|
|
33
|
+
*/
|
|
34
|
+
function query(key, fn, preload = false) {
|
|
35
|
+
const stringKey = hashKey(key);
|
|
36
|
+
if (browserCache[stringKey]) {
|
|
37
|
+
const entry = browserCache[stringKey];
|
|
38
|
+
if (preload)
|
|
39
|
+
return undefined;
|
|
40
|
+
if (entry.error)
|
|
41
|
+
throw entry.error;
|
|
42
|
+
if (entry.response)
|
|
43
|
+
return entry.response;
|
|
44
|
+
if (!preload)
|
|
45
|
+
throw entry.promise;
|
|
46
|
+
}
|
|
47
|
+
const entry = {
|
|
48
|
+
promise: fn()
|
|
49
|
+
.then((response) => (entry.response = response))
|
|
50
|
+
.catch((error) => (entry.error = error)),
|
|
51
|
+
};
|
|
52
|
+
browserCache[stringKey] = entry;
|
|
53
|
+
if (!preload)
|
|
54
|
+
throw entry.promise;
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
export const suspendFunction = (key, fn) => query(key, fn);
|
|
58
|
+
export const preloadFunction = (key, fn) => query(key, fn, true);
|
|
@@ -6,7 +6,7 @@ export function stripScriptsFromTemplate(template) {
|
|
|
6
6
|
var _a;
|
|
7
7
|
const bootstrapScripts = [];
|
|
8
8
|
const bootstrapModules = [];
|
|
9
|
-
const scripts = template.matchAll(/<script
|
|
9
|
+
const scripts = template.matchAll(/<script\n*?.+?src="(?<script>([^"]+?))"\n*.*?><\/script>/g);
|
|
10
10
|
for (const match of scripts) {
|
|
11
11
|
const scriptName = (_a = match.groups) === null || _a === void 0 ? void 0 : _a.script;
|
|
12
12
|
if (!scriptName)
|
package/dist/esnext/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const LIB_VERSION = "0.
|
|
1
|
+
export declare const LIB_VERSION = "0.16.1";
|
package/dist/esnext/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const LIB_VERSION = '0.
|
|
1
|
+
export const LIB_VERSION = '0.16.1';
|
package/dist/node/constants.d.ts
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
1
|
export declare const RSC_PATHNAME = "/__rsc";
|
|
2
|
+
export declare const EVENT_PATHNAME = "/__event";
|
|
3
|
+
export declare const EVENT_PATHNAME_REGEX: RegExp;
|
|
4
|
+
export declare const OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE = "SHOPIFY_STOREFRONT_API_SECRET_TOKEN";
|
|
5
|
+
export declare const STOREFRONT_API_SECRET_TOKEN_HEADER = "Shopify-Storefront-Private-Token";
|
|
6
|
+
export declare const STOREFRONT_API_PUBLIC_TOKEN_HEADER = "X-Shopify-Storefront-Access-Token";
|
|
7
|
+
export declare const STOREFRONT_API_BUYER_IP_HEADER = "Shopify-Storefront-Buyer-IP";
|
package/dist/node/constants.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RSC_PATHNAME = void 0;
|
|
3
|
+
exports.STOREFRONT_API_BUYER_IP_HEADER = exports.STOREFRONT_API_PUBLIC_TOKEN_HEADER = exports.STOREFRONT_API_SECRET_TOKEN_HEADER = exports.OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE = exports.EVENT_PATHNAME_REGEX = exports.EVENT_PATHNAME = exports.RSC_PATHNAME = void 0;
|
|
4
4
|
exports.RSC_PATHNAME = '/__rsc';
|
|
5
|
+
exports.EVENT_PATHNAME = '/__event';
|
|
6
|
+
exports.EVENT_PATHNAME_REGEX = new RegExp(`^${exports.EVENT_PATHNAME}\/`);
|
|
7
|
+
exports.OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE = 'SHOPIFY_STOREFRONT_API_SECRET_TOKEN';
|
|
8
|
+
exports.STOREFRONT_API_SECRET_TOKEN_HEADER = 'Shopify-Storefront-Private-Token';
|
|
9
|
+
exports.STOREFRONT_API_PUBLIC_TOKEN_HEADER = 'X-Shopify-Storefront-Access-Token';
|
|
10
|
+
exports.STOREFRONT_API_BUYER_IP_HEADER = 'Shopify-Storefront-Buyer-IP';
|
|
@@ -19,5 +19,5 @@ interface RequestHandlerOptions {
|
|
|
19
19
|
export interface RequestHandler {
|
|
20
20
|
(request: Request | IncomingMessage, options: RequestHandlerOptions): Promise<Response | undefined>;
|
|
21
21
|
}
|
|
22
|
-
export declare const renderHydrogen: (App: any, { shopifyConfig, routes }: ServerHandlerConfig) => RequestHandler;
|
|
22
|
+
export declare const renderHydrogen: (App: any, { shopifyConfig, routes, serverAnalyticsConnectors }: ServerHandlerConfig) => RequestHandler;
|
|
23
23
|
export default renderHydrogen;
|