hydrogen-sanity
Version:
Sanity.io toolkit for Hydrogen
232 lines (225 loc) • 9.1 kB
JavaScript
import { CacheLong, createWithCache } from '@shopify/hydrogen';
import { SanityClient, createClient } from '@sanity/client';
import { createElement, useMemo, lazy, Suspense, useSyncExternalStore, useId, useEffect } from 'react';
import { isPreviewEnabled, usePreviewMode } from './preview/index.js';
import { SanityProvider, useSanityProviderValue } from './_chunks-es/provider.js';
export { Sanity } from './_chunks-es/provider.js';
import { getPerspectiveFromUrl, supportsPerspectiveStack, getPerspective, hashQuery, isServer } from './_chunks-es/utils.js';
import { createImageUrlBuilder } from '@sanity/image-url';
import { jsx } from 'react/jsx-runtime';
import { useQuery as useQuery$1 } from '@sanity/react-loader';
export { useEncodeDataAttribute } from '@sanity/react-loader';
import { registerQuery } from './_chunks-es/registry.js';
export { createDataAttribute } from '@sanity/core-loader/create-data-attribute';
const DEFAULT_API_VERSION = "v2025-02-19";
const DEFAULT_CACHE_STRATEGY = CacheLong();
let didWarnAboutNoApiVersion = false;
let didWarnAboutNoPerspectiveSupport = false;
let didWarnAboutLoadQuery = false;
async function createSanityContext(options) {
const { cache, waitUntil = () => Promise.resolve(), request, preview, defaultStrategy } = options;
const withCache = cache ? createWithCache({ cache, waitUntil, request }) : null;
let client = options.client instanceof SanityClient ? options.client : createClient(options.client);
if (client.config().apiVersion === "1") {
if (process.env.NODE_ENV === "development" && !didWarnAboutNoApiVersion) {
console.warn(
`
No API version specified, defaulting to \`${DEFAULT_API_VERSION}\` which supports perspectives and Content Releases.
You can find the latest version in the Sanity changelog: https://www.sanity.io/changelog.
`.trim()
);
didWarnAboutNoApiVersion = true;
}
client = client.withConfig({ apiVersion: DEFAULT_API_VERSION });
}
let previewEnabled = false;
if (preview) {
if (!preview.token) {
throw new Error("Enabling preview mode requires a token.");
}
previewEnabled = isPreviewEnabled(client.config().projectId, preview.session);
if (previewEnabled) {
const apiVersion2 = client.config().apiVersion;
let perspective;
const urlPerspective = getPerspectiveFromUrl(request.url);
if (urlPerspective !== void 0 && !(Array.isArray(urlPerspective) && !supportsPerspectiveStack(apiVersion2))) {
perspective = urlPerspective;
} else if (supportsPerspectiveStack(apiVersion2)) {
perspective = getPerspective(preview.session);
} else {
if (process.env.NODE_ENV === "development" && !didWarnAboutNoPerspectiveSupport) {
console.warn(
`API version \`${apiVersion2}\` does not support perspective stacks. Using \`previewDrafts\` perspective. Consider upgrading to \`v2025-02-19\` or later for full perspective support.`
);
didWarnAboutNoPerspectiveSupport = true;
}
perspective = "previewDrafts";
}
client = client.withConfig({
useCdn: false,
token: preview.token,
perspective
});
}
}
const { apiHost, projectId, dataset, apiVersion } = client.config();
const providerValue = {
projectId,
dataset,
apiHost,
apiVersion,
previewEnabled,
perspective: client.config().perspective || "published",
stegaEnabled: client.config().stega?.enabled ?? false
};
return {
/**
* Loads a Sanity query with client-side loader support and Hydrogen cache integration.
* Bypasses Hydrogen cache in preview mode.
*/
async loadQuery(query, params, loaderOptions) {
const { setServerClient } = await import('@sanity/react-loader');
setServerClient(client);
if (!previewEnabled && process.env.NODE_ENV === "development" && !didWarnAboutLoadQuery) {
console.warn(
`\`loadQuery\` is being called outside of preview mode. Consider using \`query\` instead, which automatically handles both preview and production modes efficiently, or use \`fetch\`. \`loadQuery\` is intended to be called conditionally in preview and visual editing contexts.`
);
didWarnAboutLoadQuery = true;
}
if (!withCache || previewEnabled) {
const { loadQuery } = await import('@sanity/react-loader');
const resolvedOptions = previewEnabled && !loaderOptions?.perspective ? { ...loaderOptions, perspective: client.config().perspective } : loaderOptions;
return await loadQuery(query, params, resolvedOptions);
}
const cacheStrategy = loaderOptions?.hydrogen?.cache || defaultStrategy || DEFAULT_CACHE_STRATEGY;
const queryHash = await hashQuery(query, params);
const shouldCacheResult = loaderOptions?.hydrogen?.shouldCacheResult ?? (() => true);
return await withCache.run(
{ cacheKey: queryHash, cacheStrategy, shouldCacheResult },
async ({
addDebugData
}) => {
const displayName = loaderOptions?.hydrogen?.debug?.displayName || "query Sanity";
addDebugData({
displayName
});
const { loadQuery } = await import('@sanity/react-loader');
return await loadQuery(query, params, loaderOptions);
}
);
},
/**
* Executes a Sanity query with Hydrogen cache integration.
* Direct client fetch without loader integration. Bypasses cache in preview mode.
*/
async fetch(query, params = {}, fetchOptions) {
if (!withCache || previewEnabled) {
return await client.fetch(query, params, fetchOptions);
}
const cacheStrategy = fetchOptions?.hydrogen?.cache || defaultStrategy || DEFAULT_CACHE_STRATEGY;
const queryHash = await hashQuery(query, params);
return await withCache.run(
{ cacheKey: queryHash, cacheStrategy, shouldCacheResult: () => true },
async ({ addDebugData }) => {
const displayName = fetchOptions?.hydrogen?.debug?.displayName || "fetch Sanity";
addDebugData({
displayName
});
return await client.fetch(query, params, fetchOptions);
}
);
},
/**
* Automatic query method that automatically adapts based on preview mode state.
* Uses `loadQuery` (with client-side loader integration) when preview is enabled, `fetch` otherwise.
* Bypasses cache in preview mode.
*/
async query(query, params, queryOptions) {
return await (previewEnabled ? this.loadQuery : this.fetch)(query, params, queryOptions);
},
/** The configured Sanity client instance */
client,
/** Preview configuration with session-based state, undefined when preview is not configured */
preview: preview ? { ...preview, enabled: previewEnabled } : void 0,
/**
* React Provider component that serializes Sanity configuration across server-client boundary.
*/
SanityProvider({ children }) {
return createElement(
SanityProvider,
{
value: Object.freeze(providerValue)
},
children
);
}
};
}
function useImageUrlBuilder() {
const { projectId, dataset, apiHost } = useSanityProviderValue();
return useMemo(() => {
return createImageUrlBuilder({
config: () => ({ projectId, dataset, apiHost })
});
}, [apiHost, dataset, projectId]);
}
function useImageUrl(source) {
const builder = useImageUrlBuilder();
return builder.image(source);
}
function SanityQueryFallback() {
return null;
}
function useIsHydrated() {
return useSyncExternalStore(
// eslint-disable-next-line no-empty-function
() => () => {
},
() => true,
() => false
);
}
const QueryClient = isServer() ? SanityQueryFallback : lazy(
() => (
/**
* `lazy` expects the component as the default export
* @see https://react.dev/reference/react/lazy
*/
import('./_chunks-es/Query.client.js')
)
);
const noopEncodeDataAttribute = Object.assign(() => void 0, {
scope: () => noopEncodeDataAttribute
});
function Query({
query,
params,
options,
children,
...suspenseProps
}) {
const isPreviewMode = usePreviewMode();
const isHydrated = useIsHydrated();
if (isPreviewMode && isHydrated) {
return /* @__PURE__ */ jsx(Suspense, { ...suspenseProps, fallback: suspenseProps.fallback ?? /* @__PURE__ */ jsx(SanityQueryFallback, {}), children: /* @__PURE__ */ jsx(
QueryClient,
{
query,
params,
options,
children
}
) });
}
return children(options.initial, noopEncodeDataAttribute);
}
function useQuery(query, params, options) {
const id = useId();
useEffect(() => {
const unregister = registerQuery(id);
return unregister;
}, [id]);
return useQuery$1(query, params, options);
}
export { DEFAULT_API_VERSION, DEFAULT_CACHE_STRATEGY, Query, createSanityContext, getPerspectiveFromUrl, useImageUrl, useImageUrlBuilder, useQuery, useSanityProviderValue };
//# sourceMappingURL=index.js.map