@shopify/hydrogen 2025.4.2 → 2025.5.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.
@@ -1,4 +1,4 @@
1
- import { RouteConfigEntry } from '@remix-run/route-config';
1
+ import { RouteConfigEntry } from '@react-router/dev/dist/routes';
2
2
 
3
3
  declare function hydrogenRoutes(currentRoutes: Array<RouteConfigEntry>): Promise<Array<RouteConfigEntry>>;
4
4
 
@@ -1,11 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('react');
4
- var react$1 = require('@remix-run/react');
4
+ var reactRouter = require('react-router');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
  var hydrogenReact = require('@shopify/hydrogen-react');
7
7
  var cookie = require('worktop/cookie');
8
- var serverRuntime = require('@remix-run/server-runtime');
9
8
  var cspBuilder = require('content-security-policy-builder');
10
9
  require('url');
11
10
  require('path');
@@ -108,7 +107,7 @@ var init_log_seo_tags = __esm({
108
107
  });
109
108
  function AnalyticsView(props) {
110
109
  const { type, data = {}, customData } = props;
111
- const location = react$1.useLocation();
110
+ const location = reactRouter.useLocation();
112
111
  const {
113
112
  publish: publish2,
114
113
  cart,
@@ -258,7 +257,7 @@ function useCustomerPrivacy(props) {
258
257
  ignoreDeprecatedCookies: true
259
258
  });
260
259
  const initialTrackingValues = react.useMemo(hydrogenReact.getTrackingValues, [cookiesReady]);
261
- const { revalidate } = react$1.useRevalidator();
260
+ const { revalidate } = reactRouter.useRevalidator();
262
261
  hydrogenReact.useLoadScript(withPrivacyBanner ? CONSENT_API_WITH_BANNER : CONSENT_API, {
263
262
  attributes: {
264
263
  id: "customer-privacy-api"
@@ -303,7 +302,11 @@ function useCustomerPrivacy(props) {
303
302
  const consentCollectedHandler = (event) => {
304
303
  const latestTrackingValues = hydrogenReact.getTrackingValues();
305
304
  if (initialTrackingValues.visitToken !== latestTrackingValues.visitToken || initialTrackingValues.uniqueToken !== latestTrackingValues.uniqueToken) {
306
- revalidate();
305
+ revalidate().catch(() => {
306
+ console.warn(
307
+ "[h2:warn:useCustomerPrivacy] Revalidation failed after consent change."
308
+ );
309
+ });
307
310
  }
308
311
  if (onVisitorConsentCollected) {
309
312
  const customerPrivacy = getCustomerPrivacy();
@@ -527,7 +530,7 @@ function getPrivacyBanner() {
527
530
  }
528
531
 
529
532
  // package.json
530
- var version = "2025.4.2";
533
+ var version = "2025.5.1";
531
534
 
532
535
  // src/analytics-manager/ShopifyAnalytics.tsx
533
536
  function getCustomerPrivacyRequired() {
@@ -630,11 +633,11 @@ function prepareBasePageViewPayload(payload) {
630
633
  ...payload.shop,
631
634
  hasUserConsent,
632
635
  ...hydrogenReact.getClientBrowserParameters(),
636
+ ccpaEnforced: !customerPrivacy.saleOfDataAllowed(),
637
+ gdprEnforced: !(customerPrivacy.marketingAllowed() && customerPrivacy.analyticsProcessingAllowed()),
633
638
  analyticsAllowed: customerPrivacy.analyticsProcessingAllowed(),
634
639
  marketingAllowed: customerPrivacy.marketingAllowed(),
635
- saleOfDataAllowed: customerPrivacy.saleOfDataAllowed(),
636
- ccpaEnforced: !customerPrivacy.saleOfDataAllowed(),
637
- gdprEnforced: !(customerPrivacy.marketingAllowed() && customerPrivacy.analyticsProcessingAllowed())
640
+ saleOfDataAllowed: customerPrivacy.saleOfDataAllowed()
638
641
  };
639
642
  return eventPayload;
640
643
  }
@@ -1232,6 +1235,16 @@ function getDebugHeaders(request) {
1232
1235
  purpose: request ? getHeader(request, "purpose") : void 0
1233
1236
  };
1234
1237
  }
1238
+ function getStorefrontHeaders(request) {
1239
+ return {
1240
+ requestGroupId: getHeader(request, "request-id"),
1241
+ buyerIp: getHeader(request, "oxygen-buyer-ip"),
1242
+ buyerIpSig: getHeader(request, SHOPIFY_CLIENT_IP_SIG_HEADER),
1243
+ cookie: getHeader(request, "cookie"),
1244
+ // sec-purpose is added by browsers automatically when using link/prefetch or Speculation Rules
1245
+ purpose: getHeader(request, "sec-purpose") || getHeader(request, "purpose")
1246
+ };
1247
+ }
1235
1248
  var SFAPI_RE = /^\/api\/(unstable|2\d{3}-\d{2})\/graphql\.json$/;
1236
1249
  var getSafePathname = (url) => {
1237
1250
  try {
@@ -1797,7 +1810,7 @@ function CartForm({
1797
1810
  route,
1798
1811
  fetcherKey
1799
1812
  }) {
1800
- const fetcher = react$1.useFetcher({ key: fetcherKey });
1813
+ const fetcher = reactRouter.useFetcher({ key: fetcherKey });
1801
1814
  return /* @__PURE__ */ jsxRuntime.jsxs(fetcher.Form, { action: route || "", method: "post", children: [
1802
1815
  (action || inputs) && /* @__PURE__ */ jsxRuntime.jsx(
1803
1816
  "input",
@@ -1881,7 +1894,7 @@ function generateUUID() {
1881
1894
  }
1882
1895
 
1883
1896
  // src/version.ts
1884
- var LIB_VERSION = "2025.4.2";
1897
+ var LIB_VERSION = "2025.5.1";
1885
1898
 
1886
1899
  // src/utils/graphql.ts
1887
1900
  function minifyQuery(string) {
@@ -2134,8 +2147,7 @@ function createStorefrontClient(options) {
2134
2147
  },
2135
2148
  onRawHeaders: (headers2) => {
2136
2149
  collectedSubrequestHeaders ??= {
2137
- // `getSetCookie` may not be available in all environments (e.g., classic Remix compiler)
2138
- setCookie: typeof headers2.getSetCookie === "function" ? headers2.getSetCookie() : [],
2150
+ setCookie: headers2.getSetCookie(),
2139
2151
  serverTiming: headers2.get("server-timing") ?? ""
2140
2152
  };
2141
2153
  }
@@ -3209,7 +3221,7 @@ function createCartHandler(options) {
3209
3221
  }
3210
3222
  }
3211
3223
  function useOptimisticCart(cart) {
3212
- const fetchers = react$1.useFetchers();
3224
+ const fetchers = reactRouter.useFetchers();
3213
3225
  if (!fetchers || !fetchers.length) return cart;
3214
3226
  const optimisticCart = cart?.lines ? structuredClone(cart) : { lines: { nodes: [] } };
3215
3227
  const cartLines = optimisticCart.lines.nodes;
@@ -4136,15 +4148,6 @@ function createHydrogenContext(options) {
4136
4148
  session
4137
4149
  };
4138
4150
  }
4139
- function getStorefrontHeaders(request) {
4140
- return {
4141
- requestGroupId: getHeader(request, "request-id"),
4142
- buyerIp: getHeader(request, "oxygen-buyer-ip"),
4143
- buyerIpSig: getHeader(request, "X-Shopify-Client-IP-Sig"),
4144
- cookie: getHeader(request, "cookie"),
4145
- purpose: getHeader(request, "sec-purpose") || getHeader(request, "purpose")
4146
- };
4147
- }
4148
4151
  function createRequestHandler({
4149
4152
  build,
4150
4153
  mode,
@@ -4153,7 +4156,7 @@ function createRequestHandler({
4153
4156
  collectTrackingInformation = true,
4154
4157
  proxyStandardRoutes = true
4155
4158
  }) {
4156
- const handleRequest = serverRuntime.createRequestHandler(build, mode);
4159
+ const handleRequest = reactRouter.createRequestHandler(build, mode);
4157
4160
  const appendPoweredByHeader = poweredByHeader ? (response) => response.headers.append("powered-by", "Shopify, Hydrogen") : void 0;
4158
4161
  return async (request) => {
4159
4162
  const method = request.method;
@@ -4171,7 +4174,7 @@ function createRequestHandler({
4171
4174
  }
4172
4175
  });
4173
4176
  }
4174
- const context = getLoadContext ? await getLoadContext(request) : void 0;
4177
+ const context = await getLoadContext?.(request);
4175
4178
  const storefront = context?.storefront;
4176
4179
  if (proxyStandardRoutes) {
4177
4180
  if (!storefront) {
@@ -4388,7 +4391,7 @@ async function hydrogenRoutes(currentRoutes) {
4388
4391
  return [...currentRoutes, virtualLayout];
4389
4392
  }
4390
4393
  function useOptimisticData(identifier) {
4391
- const fetchers = react$1.useFetchers();
4394
+ const fetchers = reactRouter.useFetchers();
4392
4395
  const data = {};
4393
4396
  for (const { formData } of fetchers) {
4394
4397
  if (formData?.get("optimistic-identifier") === identifier) {
@@ -4427,9 +4430,9 @@ function Pagination({
4427
4430
  namespace = ""
4428
4431
  }) {
4429
4432
  const [isLoading, setIsLoading] = react.useState(false);
4430
- const transition = react$1.useNavigation();
4431
- const location = react$1.useLocation();
4432
- react$1.useNavigate();
4433
+ const transition = reactRouter.useNavigation();
4434
+ const location = reactRouter.useLocation();
4435
+ reactRouter.useNavigate();
4433
4436
  react.useEffect(() => {
4434
4437
  if (transition.state === "idle") {
4435
4438
  setIsLoading(false);
@@ -4473,7 +4476,7 @@ function Pagination({
4473
4476
  const NextLink = react.useMemo(
4474
4477
  () => react.forwardRef(
4475
4478
  function NextLink2(props, ref) {
4476
- return hasNextPage ? react.createElement(react$1.Link, {
4479
+ return hasNextPage ? react.createElement(reactRouter.Link, {
4477
4480
  preventScrollReset: true,
4478
4481
  ...props,
4479
4482
  to: nextPageUrl,
@@ -4489,7 +4492,7 @@ function Pagination({
4489
4492
  const PreviousLink = react.useMemo(
4490
4493
  () => react.forwardRef(
4491
4494
  function PrevLink(props, ref) {
4492
- return hasPreviousPage ? react.createElement(react$1.Link, {
4495
+ return hasPreviousPage ? react.createElement(reactRouter.Link, {
4493
4496
  preventScrollReset: true,
4494
4497
  ...props,
4495
4498
  to: previousPageUrl,
@@ -4547,9 +4550,9 @@ function usePagination(connection, namespace = "") {
4547
4550
  if (typeof connection.pageInfo.hasPreviousPage === "undefined") {
4548
4551
  makeError("pageInfo.hasPreviousPage");
4549
4552
  }
4550
- const transition = react$1.useNavigation();
4551
- const navigate = react$1.useNavigate();
4552
- const { state, search, pathname } = react$1.useLocation();
4553
+ const transition = reactRouter.useNavigation();
4554
+ const navigate = reactRouter.useNavigate();
4555
+ const { state, search, pathname } = reactRouter.useLocation();
4553
4556
  const cursorParam = namespace ? `${namespace}_cursor` : "cursor";
4554
4557
  const directionParam = namespace ? `${namespace}_direction` : "direction";
4555
4558
  const params = new URLSearchParams(search);
@@ -4669,7 +4672,7 @@ function getPaginationVariables(request, options = { pageBy: 20 }) {
4669
4672
  return variables;
4670
4673
  }
4671
4674
  function useOptimisticVariant(selectedVariant, variants) {
4672
- const navigation = react$1.useNavigation();
4675
+ const navigation = reactRouter.useNavigation();
4673
4676
  const [resolvedVariants, setResolvedVariants] = react.useState([]);
4674
4677
  react.useEffect(() => {
4675
4678
  Promise.resolve(variants).then((productWithVariants) => {
@@ -4823,8 +4826,8 @@ var getSelectedProductOptions = (request) => {
4823
4826
  return selectedOptions;
4824
4827
  };
4825
4828
  function useVariantPath(handle, productPath, waitForNavigation) {
4826
- const { pathname, search } = react$1.useLocation();
4827
- const navigation = react$1.useNavigation();
4829
+ const { pathname, search } = reactRouter.useLocation();
4830
+ const navigation = reactRouter.useNavigation();
4828
4831
  return react.useMemo(() => {
4829
4832
  const match = /(\/[a-zA-Z]{2}-[a-zA-Z]{2}\/)/g.exec(pathname);
4830
4833
  const isLocalePathname = match && match.length > 0;
@@ -4854,7 +4857,7 @@ var RichText = function(props) {
4854
4857
  ...props,
4855
4858
  components: {
4856
4859
  link: ({ node }) => /* @__PURE__ */ jsxRuntime.jsx(
4857
- react$1.Link,
4860
+ reactRouter.Link,
4858
4861
  {
4859
4862
  to: node.url,
4860
4863
  title: node.title,
@@ -5805,8 +5808,8 @@ function getSeoMeta(...seoInputs) {
5805
5808
  }
5806
5809
  var SeoLogger = react.lazy(() => Promise.resolve().then(() => (init_log_seo_tags(), log_seo_tags_exports)));
5807
5810
  function Seo({ debug }) {
5808
- const matches = react$1.useMatches();
5809
- const location = react$1.useLocation();
5811
+ const matches = reactRouter.useMatches();
5812
+ const location = reactRouter.useLocation();
5810
5813
  console.warn(
5811
5814
  "[h2:warn:Seo] The `<Seo/>` component is deprecated. Use `getSeoMeta` instead.\nSee: https://shopify.dev/docs/api/hydrogen/2025-04/utilities/getseometa"
5812
5815
  );