@shopify/hydrogen 0.8.1 → 0.9.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.
Files changed (70) hide show
  1. package/dist/esnext/components/ProductProvider/ProductProvider.client.d.ts +3 -2
  2. package/dist/esnext/components/ShopPayButton/ShopPayButton.client.js +1 -1
  3. package/dist/esnext/entry-server.js +27 -20
  4. package/dist/esnext/foundation/RenderCacheProvider/hook.js +1 -1
  5. package/dist/esnext/foundation/RenderCacheProvider/types.d.ts +9 -2
  6. package/dist/esnext/foundation/Router/DefaultRoutes.d.ts +1 -1
  7. package/dist/esnext/foundation/Router/DefaultRoutes.js +6 -2
  8. package/dist/esnext/foundation/useQuery/hooks.js +3 -2
  9. package/dist/esnext/framework/Hydration/ServerComponentRequest.server.js +3 -0
  10. package/dist/esnext/framework/middleware.js +8 -18
  11. package/dist/esnext/framework/plugin.d.ts +1 -1
  12. package/dist/esnext/framework/plugin.js +3 -1
  13. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-middleware.js +2 -0
  14. package/dist/esnext/framework/plugins/vite-plugin-purge-query-cache.d.ts +3 -0
  15. package/dist/esnext/framework/plugins/vite-plugin-purge-query-cache.js +11 -0
  16. package/dist/esnext/handle-event.js +12 -8
  17. package/dist/esnext/hooks/useShopQuery/hooks.d.ts +3 -1
  18. package/dist/esnext/hooks/useShopQuery/hooks.js +27 -7
  19. package/dist/esnext/index.d.ts +2 -1
  20. package/dist/esnext/index.js +2 -1
  21. package/dist/esnext/types.d.ts +9 -8
  22. package/dist/esnext/utilities/apiRoutes.d.ts +21 -0
  23. package/dist/esnext/utilities/apiRoutes.js +73 -0
  24. package/dist/esnext/utilities/fetch.js +3 -6
  25. package/dist/esnext/utilities/flattenConnection/flattenConnection.d.ts +1 -1
  26. package/dist/esnext/utilities/flattenConnection/flattenConnection.js +1 -1
  27. package/dist/esnext/utilities/index.d.ts +0 -1
  28. package/dist/esnext/utilities/index.js +0 -1
  29. package/dist/esnext/utilities/log/log.d.ts +2 -5
  30. package/dist/esnext/utilities/log/log.js +27 -22
  31. package/dist/esnext/utilities/matchPath.d.ts +10 -0
  32. package/dist/esnext/utilities/matchPath.js +54 -0
  33. package/dist/esnext/version.d.ts +1 -1
  34. package/dist/esnext/version.js +1 -1
  35. package/dist/node/framework/Hydration/ServerComponentRequest.server.js +3 -0
  36. package/dist/node/framework/middleware.js +8 -18
  37. package/dist/node/framework/plugin.d.ts +1 -1
  38. package/dist/node/framework/plugin.js +3 -1
  39. package/dist/node/framework/plugins/vite-plugin-hydrogen-middleware.js +2 -0
  40. package/dist/node/framework/plugins/vite-plugin-purge-query-cache.d.ts +3 -0
  41. package/dist/node/framework/plugins/vite-plugin-purge-query-cache.js +16 -0
  42. package/dist/node/handle-event.js +12 -8
  43. package/dist/node/types.d.ts +9 -8
  44. package/dist/node/utilities/apiRoutes.d.ts +21 -0
  45. package/dist/node/utilities/apiRoutes.js +79 -0
  46. package/dist/node/utilities/fetch.js +3 -6
  47. package/dist/node/utilities/flattenConnection/flattenConnection.d.ts +1 -1
  48. package/dist/node/utilities/flattenConnection/flattenConnection.js +1 -1
  49. package/dist/node/utilities/index.d.ts +0 -1
  50. package/dist/node/utilities/index.js +1 -7
  51. package/dist/node/utilities/log/log.d.ts +2 -5
  52. package/dist/node/utilities/log/log.js +28 -23
  53. package/dist/node/utilities/matchPath.d.ts +10 -0
  54. package/dist/node/utilities/matchPath.js +58 -0
  55. package/dist/node/version.d.ts +1 -1
  56. package/dist/node/version.js +1 -1
  57. package/dist/worker/framework/Hydration/ServerComponentRequest.server.js +3 -0
  58. package/dist/worker/handle-event.js +12 -8
  59. package/dist/worker/types.d.ts +9 -8
  60. package/dist/worker/utilities/apiRoutes.d.ts +21 -0
  61. package/dist/worker/utilities/apiRoutes.js +73 -0
  62. package/dist/worker/utilities/log/log.d.ts +2 -5
  63. package/dist/worker/utilities/log/log.js +27 -22
  64. package/dist/worker/utilities/matchPath.d.ts +10 -0
  65. package/dist/worker/utilities/matchPath.js +54 -0
  66. package/package.json +11 -4
  67. package/dist/node/utilities/log/index.d.ts +0 -1
  68. package/dist/node/utilities/log/index.js +0 -9
  69. package/dist/worker/utilities/log/index.d.ts +0 -1
  70. package/dist/worker/utilities/log/index.js +0 -1
@@ -0,0 +1,73 @@
1
+ import { matchPath } from './matchPath';
2
+ import { logServerResponse } from '../utilities/log/log';
3
+ let cachedRoutes = [];
4
+ export function getApiRoutesFromPages(pages, topLevelPath = '*') {
5
+ if ((cachedRoutes === null || cachedRoutes === void 0 ? void 0 : cachedRoutes.length) || !pages)
6
+ return cachedRoutes;
7
+ const topLevelPrefix = topLevelPath.replace('*', '').replace(/\/$/, '');
8
+ const routes = Object.keys(pages)
9
+ .filter((key) => pages[key].api)
10
+ .map((key) => {
11
+ const path = key
12
+ .replace('./pages', '')
13
+ .replace(/\.server\.(t|j)sx?$/, '')
14
+ /**
15
+ * Replace /index with /
16
+ */
17
+ .replace(/\/index$/i, '/')
18
+ /**
19
+ * Only lowercase the first letter. This allows the developer to use camelCase
20
+ * dynamic paths while ensuring their standard routes are normalized to lowercase.
21
+ */
22
+ .replace(/\b[A-Z]/, (firstLetter) => firstLetter.toLowerCase())
23
+ /**
24
+ * Convert /[handle].jsx and /[...handle].jsx to /:handle.jsx for react-router-dom
25
+ */
26
+ .replace(/\[(?:[.]{3})?(\w+?)\]/g, (_match, param) => `:${param}`);
27
+ /**
28
+ * Catch-all routes [...handle].jsx don't need an exact match
29
+ * https://reactrouter.com/core/api/Route/exact-bool
30
+ */
31
+ const exact = !/\[(?:[.]{3})(\w+?)\]/.test(key);
32
+ return {
33
+ path: topLevelPrefix + path,
34
+ resource: pages[key].api,
35
+ hasServerComponent: !!pages[key].default,
36
+ exact,
37
+ };
38
+ });
39
+ cachedRoutes = [
40
+ ...routes.filter((route) => !route.path.includes(':')),
41
+ ...routes.filter((route) => route.path.includes(':')),
42
+ ];
43
+ return cachedRoutes;
44
+ }
45
+ export function getApiRouteFromURL(url, routes) {
46
+ let foundRoute, foundRouteDetails;
47
+ for (let i = 0; i < routes.length; i++) {
48
+ foundRouteDetails = matchPath(url.pathname, routes[i]);
49
+ if (foundRouteDetails) {
50
+ foundRoute = routes[i];
51
+ break;
52
+ }
53
+ }
54
+ if (!foundRoute)
55
+ return null;
56
+ return {
57
+ resource: foundRoute.resource,
58
+ params: foundRouteDetails.params,
59
+ hasServerComponent: foundRoute.hasServerComponent,
60
+ };
61
+ }
62
+ export async function renderApiRoute(request, route, log) {
63
+ let response;
64
+ try {
65
+ response = await route.resource(request, { params: route.params });
66
+ }
67
+ catch (e) {
68
+ log.error(e);
69
+ response = new Response('Error processing: ' + request.url, { status: 500 });
70
+ }
71
+ logServerResponse('api', log, request, response.status);
72
+ return response;
73
+ }
@@ -38,6 +38,9 @@ export function fetchBuilder(request) {
38
38
  headers,
39
39
  method: clonedRequest.method,
40
40
  });
41
+ if (!response.ok) {
42
+ throw response;
43
+ }
41
44
  const data = await response.json();
42
45
  return data;
43
46
  };
@@ -50,12 +53,6 @@ export function graphqlRequestBody(query, variables) {
50
53
  });
51
54
  }
52
55
  export function decodeShopifyId(id) {
53
- if (!id.startsWith('gid://')) {
54
- id =
55
- typeof btoa !== 'undefined'
56
- ? btoa(id)
57
- : Buffer.from(id, 'base64').toString('ascii');
58
- }
59
56
  if (!id.startsWith('gid://')) {
60
57
  throw new Error('invalid Shopify ID');
61
58
  }
@@ -1,5 +1,5 @@
1
1
  import { GraphQLConnection } from '../../types';
2
2
  /**
3
- * The `flattenConnection` utility transforms a connection object from the Storefront API (for example, [Product-related connections](api/storefront/reference/products/product#connections)) into a flat array of nodes.
3
+ * The `flattenConnection` utility transforms a connection object from the Storefront API (for example, [Product-related connections](/api/storefront/reference/products/product)) into a flat array of nodes.
4
4
  */
5
5
  export declare function flattenConnection<T>(connection: GraphQLConnection<T>): T[];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The `flattenConnection` utility transforms a connection object from the Storefront API (for example, [Product-related connections](api/storefront/reference/products/product#connections)) into a flat array of nodes.
2
+ * The `flattenConnection` utility transforms a connection object from the Storefront API (for example, [Product-related connections](/api/storefront/reference/products/product)) into a flat array of nodes.
3
3
  */
4
4
  export function flattenConnection(connection) {
5
5
  var _a;
@@ -5,7 +5,6 @@ export { wrapPromise } from './suspense';
5
5
  export { flattenConnection } from './flattenConnection';
6
6
  export { isClient } from './isClient';
7
7
  export { isServer } from './isServer';
8
- export { log, setLogger, Logger, logServerResponse, getLoggerFromContext, resetLogger, } from './log';
9
8
  export { getMeasurementAsParts, getMeasurementAsString } from './measurement';
10
9
  export { parseMetafieldValue } from './parseMetafieldValue';
11
10
  export { fetchBuilder, graphqlRequestBody, decodeShopifyId } from './fetch';
@@ -5,7 +5,6 @@ export { wrapPromise } from './suspense';
5
5
  export { flattenConnection } from './flattenConnection';
6
6
  export { isClient } from './isClient';
7
7
  export { isServer } from './isServer';
8
- export { log, setLogger, logServerResponse, getLoggerFromContext, resetLogger, } from './log';
9
8
  export { getMeasurementAsParts, getMeasurementAsString } from './measurement';
10
9
  export { parseMetafieldValue } from './parseMetafieldValue';
11
10
  export { fetchBuilder, graphqlRequestBody, decodeShopifyId } from './fetch';
@@ -3,9 +3,6 @@ import { ServerComponentRequest } from '../../framework/Hydration/ServerComponen
3
3
  * Use by importing `log` `@shopify/hydrogen` or by using a `log` prop passed to each page
4
4
  * component. Using the latter is ideal, because it will ty your log to the current request in progress.
5
5
  */
6
- declare global {
7
- var __hlogger: Logger;
8
- }
9
6
  export interface Logger {
10
7
  trace: (...args: Array<any>) => void;
11
8
  debug: (...args: Array<any>) => void;
@@ -14,7 +11,7 @@ export interface Logger {
14
11
  fatal: (...args: Array<any>) => void;
15
12
  }
16
13
  export declare function getLoggerFromContext(context: any): Logger;
17
- export declare function setLogger(_logger: Logger): void;
14
+ export declare function setLogger(newLogger: Logger): void;
18
15
  export declare function resetLogger(): void;
19
16
  export declare const log: Logger;
20
- export declare function logServerResponse(type: 'str' | 'rsc' | 'ssr', log: Logger, request: ServerComponentRequest, responseStatus: number): void;
17
+ export declare function logServerResponse(type: 'str' | 'rsc' | 'ssr' | 'api', log: Logger, request: ServerComponentRequest, responseStatus: number): void;
@@ -1,17 +1,6 @@
1
1
  import { yellow, red, green, italic, lightBlue } from 'kolorist';
2
2
  import { getTime } from '../timing';
3
- export function getLoggerFromContext(context) {
4
- return {
5
- trace: (...args) => globalThis.__hlogger.trace(context, ...args),
6
- debug: (...args) => globalThis.__hlogger.debug(context, ...args),
7
- warn: (...args) => globalThis.__hlogger.warn(context, ...args),
8
- error: (...args) => globalThis.__hlogger.error(context, ...args),
9
- fatal: (...args) => globalThis.__hlogger.fatal(context, ...args),
10
- };
11
- }
12
- // @todo - multiple instances of log.ts are loaded, we utilitze the
13
- // global in order to make sure that the logger is a singleton
14
- const defaultLogger = (globalThis.__hlogger = {
3
+ const defaultLogger = {
15
4
  trace(context, ...args) {
16
5
  console.log(...args);
17
6
  },
@@ -27,30 +16,45 @@ const defaultLogger = (globalThis.__hlogger = {
27
16
  fatal(context, ...args) {
28
17
  console.error(red('FATAL: '), ...args);
29
18
  },
30
- });
31
- export function setLogger(_logger) {
32
- globalThis.__hlogger = _logger;
19
+ };
20
+ let logger = defaultLogger;
21
+ export function getLoggerFromContext(context) {
22
+ return {
23
+ trace: (...args) => logger.trace(context, ...args),
24
+ debug: (...args) => logger.debug(context, ...args),
25
+ warn: (...args) => logger.warn(context, ...args),
26
+ error: (...args) => logger.error(context, ...args),
27
+ fatal: (...args) => logger.fatal(context, ...args),
28
+ };
29
+ }
30
+ export function setLogger(newLogger) {
31
+ logger = newLogger;
33
32
  }
34
33
  export function resetLogger() {
35
- globalThis.__hlogger = defaultLogger;
34
+ logger = defaultLogger;
36
35
  }
37
36
  export const log = {
38
37
  trace(...args) {
39
- return globalThis.__hlogger.trace({}, ...args);
38
+ return logger.trace({}, ...args);
40
39
  },
41
40
  debug(...args) {
42
- return globalThis.__hlogger.debug({}, ...args);
41
+ return logger.debug({}, ...args);
43
42
  },
44
43
  warn(...args) {
45
- return globalThis.__hlogger.warn({}, ...args);
44
+ return logger.warn({}, ...args);
46
45
  },
47
46
  error(...args) {
48
- return globalThis.__hlogger.error({}, ...args);
47
+ return logger.error({}, ...args);
49
48
  },
50
49
  fatal(...args) {
51
- return globalThis.__hlogger.fatal({}, ...args);
50
+ return logger.fatal({}, ...args);
52
51
  },
53
52
  };
53
+ const SERVER_RESPONSE_MAP = {
54
+ str: 'streaming SSR',
55
+ rsc: 'server Components',
56
+ ssr: 'buffered SSR',
57
+ };
54
58
  export function logServerResponse(type, log, request, responseStatus) {
55
59
  const coloredResponseStatus = responseStatus >= 500
56
60
  ? red(responseStatus)
@@ -59,7 +63,8 @@ export function logServerResponse(type, log, request, responseStatus) {
59
63
  : responseStatus >= 300
60
64
  ? lightBlue(responseStatus)
61
65
  : green(responseStatus);
62
- const styledType = italic(type);
66
+ const fullType = SERVER_RESPONSE_MAP[type] || type;
67
+ const styledType = italic(pad(fullType, ' '));
63
68
  const paddedTiming = pad((getTime() - request.time).toFixed(2) + ' ms', ' ');
64
69
  const url = type === 'rsc'
65
70
  ? decodeURIComponent(request.url.substring(request.url.indexOf('=') + 1))
@@ -0,0 +1,10 @@
1
+ import { TokensToRegexpOptions } from 'path-to-regexp';
2
+ interface MatchPathOptions extends TokensToRegexpOptions {
3
+ path?: string;
4
+ exact?: boolean;
5
+ }
6
+ /**
7
+ * Public API for matching a URL pathname to a path.
8
+ */
9
+ export declare function matchPath(pathname: string, options?: MatchPathOptions): any;
10
+ export {};
@@ -0,0 +1,54 @@
1
+ import { pathToRegexp } from 'path-to-regexp';
2
+ // Modified from React Router v5
3
+ // https://github.com/remix-run/react-router/blob/v5/packages/react-router/modules/matchPath.js
4
+ const cache = {};
5
+ const cacheLimit = 10000;
6
+ let cacheCount = 0;
7
+ function compilePath(path, options) {
8
+ const cacheKey = `${options.end}${options.strict}${options.sensitive}`;
9
+ const pathCache = cache[cacheKey] || (cache[cacheKey] = {});
10
+ if (pathCache[path])
11
+ return pathCache[path];
12
+ const keys = [];
13
+ const regexp = pathToRegexp(path, keys, options);
14
+ const result = { regexp, keys };
15
+ if (cacheCount < cacheLimit) {
16
+ pathCache[path] = result;
17
+ cacheCount++;
18
+ }
19
+ return result;
20
+ }
21
+ /**
22
+ * Public API for matching a URL pathname to a path.
23
+ */
24
+ export function matchPath(pathname, options = {}) {
25
+ const { path, exact = false, strict = false, sensitive = false } = options;
26
+ const paths = [].concat(path);
27
+ return paths.reduce((matched, path) => {
28
+ if (!path && path !== '')
29
+ return null;
30
+ if (matched)
31
+ return matched;
32
+ const { regexp, keys } = compilePath(path, {
33
+ end: exact,
34
+ strict,
35
+ sensitive,
36
+ });
37
+ const match = regexp.exec(pathname);
38
+ if (!match)
39
+ return null;
40
+ const [url, ...values] = match;
41
+ const isExact = pathname === url;
42
+ if (exact && !isExact)
43
+ return null;
44
+ return {
45
+ path,
46
+ url: path === '/' && url === '' ? '/' : url,
47
+ isExact,
48
+ params: keys.reduce((memo, key, index) => {
49
+ memo[key.name] = values[index];
50
+ return memo;
51
+ }, {}),
52
+ };
53
+ }, null);
54
+ }
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "0.8.1";
1
+ export declare const LIB_VERSION = "0.9.1";
@@ -1 +1 @@
1
- export const LIB_VERSION = '0.8.1';
1
+ export const LIB_VERSION = '0.9.1';
@@ -18,6 +18,9 @@ class ServerComponentRequest extends Request {
18
18
  super(getUrlFromNodeRequest(input), {
19
19
  headers: new Headers(input.headers),
20
20
  method: input.method,
21
+ body: input.method !== 'GET' && input.method !== 'HEAD'
22
+ ? input.body
23
+ : undefined,
21
24
  });
22
25
  }
23
26
  this.time = (0, timing_1.getTime)();
@@ -42,17 +42,6 @@ exports.graphiqlMiddleware = graphiqlMiddleware;
42
42
  function hydrogenMiddleware({ dev, cache, indexTemplate, getServerEntrypoint, devServer, }) {
43
43
  return async function (request, response, next) {
44
44
  const url = new URL('http://' + request.headers.host + request.originalUrl);
45
- const isReactHydrationRequest = url.pathname === '/react';
46
- /**
47
- * If it's a dev environment, it's assumed that Vite's dev server is handling
48
- * any static or JS requests, so we need to ensure that we don't try to handle them.
49
- *
50
- * If it's a product environment, it's assumed that the developer is handling
51
- * static requests with e.g. static middleware.
52
- */
53
- if (dev && !shouldInterceptRequest(request, isReactHydrationRequest)) {
54
- return next();
55
- }
56
45
  try {
57
46
  /**
58
47
  * We're running in the Node.js runtime without access to `fetch`,
@@ -60,14 +49,17 @@ function hydrogenMiddleware({ dev, cache, indexTemplate, getServerEntrypoint, de
60
49
  */
61
50
  if (!globalThis.fetch) {
62
51
  const fetch = await Promise.resolve().then(() => __importStar(require('node-fetch')));
52
+ const { default: AbortController } = await Promise.resolve().then(() => __importStar(require('abort-controller')));
63
53
  // @ts-ignore
64
- globalThis.fetch = fetch.default;
54
+ globalThis.fetch = fetch;
65
55
  // @ts-ignore
66
56
  globalThis.Request = fetch.Request;
67
57
  // @ts-ignore
68
58
  globalThis.Response = fetch.Response;
69
59
  // @ts-ignore
70
60
  globalThis.Headers = fetch.Headers;
61
+ // @ts-ignore
62
+ globalThis.AbortController = AbortController;
71
63
  }
72
64
  /**
73
65
  * Dynamically import ServerComponentResponse after the `fetch`
@@ -95,7 +87,10 @@ function hydrogenMiddleware({ dev, cache, indexTemplate, getServerEntrypoint, de
95
87
  response.setHeader(key, value);
96
88
  });
97
89
  response.statusCode = eventResponse.status;
98
- response.end(eventResponse.body);
90
+ if (eventResponse.body) {
91
+ response.write(eventResponse.body);
92
+ }
93
+ response.end();
99
94
  }
100
95
  }
101
96
  catch (e) {
@@ -126,11 +121,6 @@ function hydrogenMiddleware({ dev, cache, indexTemplate, getServerEntrypoint, de
126
121
  };
127
122
  }
128
123
  exports.hydrogenMiddleware = hydrogenMiddleware;
129
- function shouldInterceptRequest(request, isReactHydrationRequest) {
130
- var _a;
131
- return (/text\/html|application\/hydrogen/.test((_a = request.headers['accept']) !== null && _a !== void 0 ? _a : '') ||
132
- isReactHydrationRequest);
133
- }
134
124
  /**
135
125
  * /graphiql and /___graphql are supported
136
126
  */
@@ -1,3 +1,3 @@
1
1
  import type { HydrogenVitePluginOptions, ShopifyConfig } from '../types';
2
- declare const _default: (shopifyConfig: ShopifyConfig, pluginOptions: HydrogenVitePluginOptions) => ("" | import("vite").Plugin | import("vite").PluginOption[] | undefined)[];
2
+ declare const _default: (shopifyConfig: ShopifyConfig, pluginOptions?: HydrogenVitePluginOptions) => (false | "" | import("vite").Plugin | import("vite").PluginOption[] | undefined)[];
3
3
  export default _default;
@@ -6,14 +6,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const vite_plugin_hydrogen_config_1 = __importDefault(require("./plugins/vite-plugin-hydrogen-config"));
7
7
  const vite_plugin_hydrogen_middleware_1 = __importDefault(require("./plugins/vite-plugin-hydrogen-middleware"));
8
8
  const vite_plugin_react_server_components_shim_1 = __importDefault(require("./plugins/vite-plugin-react-server-components-shim"));
9
+ const vite_plugin_purge_query_cache_1 = __importDefault(require("./plugins/vite-plugin-purge-query-cache"));
9
10
  const vite_plugin_inspect_1 = __importDefault(require("vite-plugin-inspect"));
10
11
  const plugin_react_1 = __importDefault(require("@vitejs/plugin-react"));
11
- exports.default = (shopifyConfig, pluginOptions) => {
12
+ exports.default = (shopifyConfig, pluginOptions = {}) => {
12
13
  return [
13
14
  process.env.VITE_INSPECT && (0, vite_plugin_inspect_1.default)(),
14
15
  (0, vite_plugin_hydrogen_config_1.default)(),
15
16
  (0, vite_plugin_hydrogen_middleware_1.default)(shopifyConfig, pluginOptions),
16
17
  (0, vite_plugin_react_server_components_shim_1.default)(),
17
18
  (0, plugin_react_1.default)(),
19
+ pluginOptions.purgeQueryCacheOnBuild && (0, vite_plugin_purge_query_cache_1.default)(),
18
20
  ];
19
21
  };
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const vite_1 = require("vite");
7
+ const body_parser_1 = __importDefault(require("body-parser"));
7
8
  const path_1 = __importDefault(require("path"));
8
9
  const fs_1 = require("fs");
9
10
  const middleware_1 = require("../middleware");
@@ -30,6 +31,7 @@ exports.default = (shopifyConfig, pluginOptions) => {
30
31
  shopifyConfig,
31
32
  dev: true,
32
33
  }));
34
+ server.middlewares.use(body_parser_1.default.raw({ type: '*/*' }));
33
35
  return () => server.middlewares.use((0, middleware_1.hydrogenMiddleware)({
34
36
  dev: true,
35
37
  shopifyConfig,
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from 'vite';
2
+ declare const _default: () => Plugin;
3
+ export default _default;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const crypto_1 = __importDefault(require("crypto"));
7
+ exports.default = () => {
8
+ const buildCacheId = crypto_1.default.randomBytes(8).toString('hex').slice(0, 8);
9
+ return {
10
+ name: 'vite-plugin-purge-query-cache',
11
+ enforce: 'pre',
12
+ transform(code) {
13
+ return code.replace('__QUERY_CACHE_ID__', buildCacheId);
14
+ },
15
+ };
16
+ };
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const cache_1 = require("./framework/cache");
4
4
  const runtime_1 = require("./framework/runtime");
5
5
  const config_1 = require("./framework/config");
6
- const log_1 = require("./utilities/log");
6
+ const apiRoutes_1 = require("./utilities/apiRoutes");
7
7
  async function handleEvent(event, { request, entrypoint, indexTemplate, assetHandler, streamableResponse, dev, cache, context, }) {
8
8
  var _a, _b, _c, _d, _e;
9
9
  const url = new URL(request.url);
@@ -24,15 +24,23 @@ async function handleEvent(event, { request, entrypoint, indexTemplate, assetHan
24
24
  assetHandler) {
25
25
  return assetHandler(event, url);
26
26
  }
27
- const { render, hydrate, stream } = entrypoint.default || entrypoint;
27
+ const { render, hydrate, stream, getApiRoute, log } = entrypoint.default || entrypoint;
28
28
  // @ts-ignore
29
- if (dev && !(render && hydrate && stream)) {
29
+ if (dev && !(render && hydrate && stream && getApiRoute)) {
30
30
  throw new Error(`entry-server.jsx could not be loaded. This likely occurred because of a Vite compilation error.\n` +
31
31
  `Please check your server logs for more information.`);
32
32
  }
33
+ if (!isReactHydrationRequest) {
34
+ const apiRoute = getApiRoute(url);
35
+ // The API Route might have a default export, making it also a server component
36
+ // If it does, only render the API route if the request method is GET
37
+ if (apiRoute &&
38
+ (!apiRoute.hasServerComponent || request.method !== 'GET')) {
39
+ return (0, apiRoutes_1.renderApiRoute)(request, apiRoute, log);
40
+ }
41
+ }
33
42
  const userAgent = request.headers.get('user-agent');
34
43
  const isStreamable = streamableResponse && !isBotUA(url, userAgent);
35
- const logger = (0, log_1.getLoggerFromContext)(request);
36
44
  /**
37
45
  * Stream back real-user responses, but for bots/etc,
38
46
  * use `render` instead. This is because we need to inject <head>
@@ -45,7 +53,6 @@ async function handleEvent(event, { request, entrypoint, indexTemplate, assetHan
45
53
  request,
46
54
  response: streamableResponse,
47
55
  dev,
48
- log: logger,
49
56
  });
50
57
  }
51
58
  else {
@@ -55,7 +62,6 @@ async function handleEvent(event, { request, entrypoint, indexTemplate, assetHan
55
62
  response: streamableResponse,
56
63
  template,
57
64
  dev,
58
- log: logger,
59
65
  });
60
66
  }
61
67
  return;
@@ -65,7 +71,6 @@ async function handleEvent(event, { request, entrypoint, indexTemplate, assetHan
65
71
  context: {},
66
72
  isReactHydrationRequest,
67
73
  dev,
68
- log: logger,
69
74
  });
70
75
  const headers = componentResponse.headers;
71
76
  /**
@@ -102,7 +107,6 @@ async function handleEvent(event, { request, entrypoint, indexTemplate, assetHan
102
107
  headers,
103
108
  });
104
109
  }
105
- (0, log_1.logServerResponse)('ssr', logger, request, response.status);
106
110
  return response;
107
111
  }
108
112
  exports.default = handleEvent;
@@ -1,15 +1,15 @@
1
1
  /// <reference types="node" />
2
2
  import { ServerResponse } from 'http';
3
- import type { Logger } from './utilities/log/log';
4
3
  import type { ServerComponentResponse } from './framework/Hydration/ServerComponentResponse.server';
5
4
  import type { ServerComponentRequest } from './framework/Hydration/ServerComponentRequest.server';
6
5
  import type { Metafield, Image, MediaContentType } from './graphql/types/types';
6
+ import { ApiRouteMatch } from './utilities/apiRoutes';
7
+ import { Logger } from './utilities/log/log';
7
8
  export declare type Renderer = (url: URL, options: {
8
9
  request: ServerComponentRequest;
9
10
  context?: Record<string, any>;
10
11
  isReactHydrationRequest?: boolean;
11
12
  dev?: boolean;
12
- log: Logger;
13
13
  }) => Promise<{
14
14
  body: string;
15
15
  componentResponse: ServerComponentResponse;
@@ -19,20 +19,20 @@ export declare type Streamer = (url: URL, options: {
19
19
  request: ServerComponentRequest;
20
20
  response: ServerResponse;
21
21
  template: string;
22
- log: Logger;
23
22
  dev?: boolean;
24
23
  }) => void;
25
24
  export declare type Hydrator = (url: URL, options: {
26
25
  context: any;
27
26
  request: ServerComponentRequest;
28
27
  response: ServerResponse;
29
- log: Logger;
30
28
  dev?: boolean;
31
29
  }) => void;
32
30
  export declare type EntryServerHandler = {
33
31
  render: Renderer;
34
32
  stream: Streamer;
35
33
  hydrate: Hydrator;
34
+ getApiRoute: (url: URL) => ApiRouteMatch | null;
35
+ log: Logger;
36
36
  };
37
37
  export declare type ShopifyConfig = {
38
38
  locale?: string;
@@ -43,11 +43,11 @@ export declare type ShopifyConfig = {
43
43
  export declare type Hook = (params: {
44
44
  url: URL;
45
45
  } & Record<string, any>) => any | Promise<any>;
46
- export declare type ServerHandler = (App: any, hook?: Hook) => {
47
- render: Renderer;
48
- stream: Streamer;
49
- hydrate: Hydrator;
46
+ export declare type ImportGlobEagerOutput = Record<string, Record<'default' | 'api', any>>;
47
+ export declare type ServerHandlerConfig = {
48
+ pages?: ImportGlobEagerOutput;
50
49
  };
50
+ export declare type ServerHandler = (App: any, config?: ServerHandlerConfig, hook?: Hook) => EntryServerHandler;
51
51
  export declare type ClientHandler = (App: any, hook?: Hook) => Promise<void>;
52
52
  export interface GraphQLConnection<T> {
53
53
  edges?: {
@@ -91,5 +91,6 @@ export interface CacheOptions {
91
91
  }
92
92
  export interface HydrogenVitePluginOptions {
93
93
  devCache?: boolean;
94
+ purgeQueryCacheOnBuild?: boolean;
94
95
  }
95
96
  export {};
@@ -0,0 +1,21 @@
1
+ import { ImportGlobEagerOutput } from '../types';
2
+ import { Logger } from '../utilities/log/log';
3
+ declare type RouteParams = Record<string, string>;
4
+ declare type RequestOptions = {
5
+ params: RouteParams;
6
+ };
7
+ declare type ResourceGetter = (request: Request, requestOptions: RequestOptions) => Promise<Response>;
8
+ interface HydrogenApiRoute {
9
+ path: string;
10
+ resource: ResourceGetter;
11
+ hasServerComponent: boolean;
12
+ }
13
+ export declare type ApiRouteMatch = {
14
+ resource: ResourceGetter;
15
+ hasServerComponent: boolean;
16
+ params: RouteParams;
17
+ };
18
+ export declare function getApiRoutesFromPages(pages: ImportGlobEagerOutput | undefined, topLevelPath?: string): Array<HydrogenApiRoute>;
19
+ export declare function getApiRouteFromURL(url: URL, routes: Array<HydrogenApiRoute>): ApiRouteMatch | null;
20
+ export declare function renderApiRoute(request: Request, route: ApiRouteMatch, log: Logger): Promise<Response>;
21
+ export {};