@decocms/blocks 7.3.0 → 7.3.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/package.json +2 -1
- package/src/cms/sectionMixins.ts +1 -1
- package/src/hooks/Picture.tsx +18 -2
- package/src/sdk/detectDevice.ts +45 -0
- package/src/sdk/requestContext.ts +1 -1
- package/src/sdk/useDevice.ts +12 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/blocks",
|
|
3
|
-
"version": "7.3.
|
|
3
|
+
"version": "7.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Deco framework-agnostic core: CMS block resolution, section registry, matchers, portable SDK utilities",
|
|
6
6
|
"repository": {
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"./sdk/cookie": "./src/sdk/cookie.ts",
|
|
47
47
|
"./sdk/crypto": "./src/sdk/crypto.ts",
|
|
48
48
|
"./sdk/csp": "./src/sdk/csp.ts",
|
|
49
|
+
"./sdk/detectDevice": "./src/sdk/detectDevice.ts",
|
|
49
50
|
"./sdk/djb2": "./src/sdk/djb2.ts",
|
|
50
51
|
"./sdk/encoding": "./src/sdk/encoding.ts",
|
|
51
52
|
"./sdk/env": "./src/sdk/env.ts",
|
package/src/cms/sectionMixins.ts
CHANGED
package/src/hooks/Picture.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ComponentPropsWithoutRef,
|
|
3
|
+
type Context,
|
|
3
4
|
createContext,
|
|
4
5
|
forwardRef,
|
|
5
6
|
type ReactNode,
|
|
@@ -12,13 +13,27 @@ import { type FitOptions, getOptimizedMediaUrl, getSrcSet } from "./Image";
|
|
|
12
13
|
// Preload context — flows from <Picture preload> to child <Source> elements
|
|
13
14
|
// so each source can inject its own <link rel="preload"> with the correct
|
|
14
15
|
// media query for responsive art direction.
|
|
16
|
+
//
|
|
17
|
+
// Created LAZILY (first render), not at module scope: this file has no
|
|
18
|
+
// "use client" directive, so in a react-server graph (a Server Component
|
|
19
|
+
// page, or ANY Next.js route handler — which ignores "use client"
|
|
20
|
+
// entirely) the module evaluates against React's react-server build, where
|
|
21
|
+
// `createContext` does not exist. A module-scope call crashes the whole
|
|
22
|
+
// graph at import time ("createContext is not a function") even if
|
|
23
|
+
// Picture/Source never render. Deferring to render time is safe: renders
|
|
24
|
+
// only ever happen under a full React build (client or SSR), and both
|
|
25
|
+
// components resolve the same memoized context object.
|
|
15
26
|
// -------------------------------------------------------------------------
|
|
16
27
|
|
|
17
28
|
interface PreloadContextValue {
|
|
18
29
|
preload: boolean;
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
let _preloadContext: Context<PreloadContextValue> | null = null;
|
|
33
|
+
function getPreloadContext(): Context<PreloadContextValue> {
|
|
34
|
+
_preloadContext ??= createContext<PreloadContextValue>({ preload: false });
|
|
35
|
+
return _preloadContext;
|
|
36
|
+
}
|
|
22
37
|
|
|
23
38
|
// -------------------------------------------------------------------------
|
|
24
39
|
// Source — composable <source> with automatic srcSet optimization and
|
|
@@ -41,7 +56,7 @@ export const Source = forwardRef<HTMLSourceElement, SourceProps>(function Source
|
|
|
41
56
|
{ src, width, height, fetchPriority, fit = "cover", ...rest },
|
|
42
57
|
ref,
|
|
43
58
|
) {
|
|
44
|
-
const { preload } = useContext(
|
|
59
|
+
const { preload } = useContext(getPreloadContext());
|
|
45
60
|
|
|
46
61
|
const optimizedSrc = getOptimizedMediaUrl({
|
|
47
62
|
originalSrc: src,
|
|
@@ -93,6 +108,7 @@ export const Picture = forwardRef<HTMLPictureElement, PictureProps>(function Pic
|
|
|
93
108
|
ref,
|
|
94
109
|
) {
|
|
95
110
|
const value = useMemo(() => ({ preload }), [preload]);
|
|
111
|
+
const PreloadContext = getPreloadContext();
|
|
96
112
|
|
|
97
113
|
return (
|
|
98
114
|
<PreloadContext.Provider value={value}>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure User-Agent device detection — no React, no RequestContext, no
|
|
3
|
+
* side effects. Import from HERE (not `./useDevice`) in any module that can
|
|
4
|
+
* end up in a Next.js App Router **route handler** graph.
|
|
5
|
+
*
|
|
6
|
+
* Why this file exists: `useDevice.ts` re-exports the `"use client"`
|
|
7
|
+
* context half (`./useDeviceContext`, which calls `createContext` at module
|
|
8
|
+
* scope) for backward compatibility. That re-export is harmless in Server
|
|
9
|
+
* Components — Next honors the `"use client"` boundary there — but route
|
|
10
|
+
* handlers (App Router route.ts files) have NO client graph: the directive
|
|
11
|
+
* is ignored, the module executes against Next's vendored **RSC** React
|
|
12
|
+
* (`vendored/rsc/react.js`), and that build has no `createContext`, so the
|
|
13
|
+
* whole route module crashes at evaluation time
|
|
14
|
+
* ("...createContext is not a function"). `@decocms/nextjs`'s admin route
|
|
15
|
+
* handlers reach device detection via `cms/sectionMixins.ts`
|
|
16
|
+
* (`detectDevice`) and `sdk/requestContext.ts` (`isMobileUA`) — both now
|
|
17
|
+
* import this leaf so their graphs never touch the client context file.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export type Device = "mobile" | "tablet" | "desktop";
|
|
21
|
+
|
|
22
|
+
// Android phones include "Mobile" in their UA; Android tablets do not.
|
|
23
|
+
// Check TABLET_RE first so `android(?!.*mobile)` captures tablets before
|
|
24
|
+
// the MOBILE_RE `android.*mobile` branch matches phones.
|
|
25
|
+
export const MOBILE_RE = /mobile|android.*mobile|iphone|ipod|webos|blackberry|opera mini|iemobile/i;
|
|
26
|
+
export const TABLET_RE = /ipad|tablet|kindle|silk|playbook|android(?!.*mobile)/i;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Simple mobile-or-not check (mobile + tablet = true).
|
|
30
|
+
* Use this for cache key splitting or any context where you
|
|
31
|
+
* only need a mobile/desktop binary decision.
|
|
32
|
+
*/
|
|
33
|
+
export function isMobileUA(userAgent: string): boolean {
|
|
34
|
+
return MOBILE_RE.test(userAgent) || TABLET_RE.test(userAgent);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Detect device type from a User-Agent string.
|
|
39
|
+
* Pure function — no side effects, works anywhere.
|
|
40
|
+
*/
|
|
41
|
+
export function detectDevice(userAgent: string): Device {
|
|
42
|
+
if (TABLET_RE.test(userAgent)) return "tablet";
|
|
43
|
+
if (MOBILE_RE.test(userAgent)) return "mobile";
|
|
44
|
+
return "desktop";
|
|
45
|
+
}
|
|
@@ -87,7 +87,7 @@ export interface RequestContextData {
|
|
|
87
87
|
// Storage
|
|
88
88
|
// -------------------------------------------------------------------------
|
|
89
89
|
|
|
90
|
-
import { isMobileUA } from "./
|
|
90
|
+
import { isMobileUA } from "./detectDevice";
|
|
91
91
|
|
|
92
92
|
const BOT_RE =
|
|
93
93
|
/bot|crawl|spider|slurp|bingpreview|facebookexternalhit|linkedinbot|twitterbot|whatsapp|telegram|googlebot|yandex|baidu|duckduck/i;
|
package/src/sdk/useDevice.ts
CHANGED
|
@@ -21,52 +21,32 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import { detectDevice } from "./detectDevice";
|
|
24
25
|
import { RequestContext } from "./requestContext";
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
// The pure UA-parsing half (`Device`, `MOBILE_RE`, `TABLET_RE`,
|
|
28
|
+
// `isMobileUA`, `detectDevice`) lives in `./detectDevice` — a leaf with no
|
|
29
|
+
// React and no RequestContext — and is re-exported below for backward
|
|
30
|
+
// compatibility. Modules that can land in a Next.js route-handler graph
|
|
31
|
+
// (`cms/sectionMixins.ts`, `sdk/requestContext.ts`) MUST import that leaf
|
|
32
|
+
// directly, not this file: this file also re-exports the `"use client"`
|
|
33
|
+
// context half, which crashes route handlers (see ./detectDevice's doc
|
|
34
|
+
// comment for the vendored-RSC-React mechanics).
|
|
35
|
+
export { type Device, detectDevice, isMobileUA, MOBILE_RE, TABLET_RE } from "./detectDevice";
|
|
27
36
|
|
|
28
37
|
// `DeviceContext`, the `useDevice()` hook, and `DeviceProvider` live in
|
|
29
38
|
// `./useDeviceContext` (a `"use client"` file) and are re-exported below for
|
|
30
39
|
// full backward compatibility with this module's public API — see that
|
|
31
|
-
// file's doc comment for why the split exists.
|
|
32
|
-
// *this* file is a plain, hook-free function, safe to import from
|
|
33
|
-
// server-only code (e.g. `cms/sectionMixins.ts`'s `withDevice()`/
|
|
34
|
-
// `withMobile()` loader mixins) without dragging a client-only hook/context
|
|
35
|
-
// boundary into a Server Component's module graph.
|
|
40
|
+
// file's doc comment for why the split exists.
|
|
36
41
|
export { DeviceContext, useDevice, DeviceProvider } from "./useDeviceContext";
|
|
37
42
|
|
|
38
|
-
// Android phones include "Mobile" in their UA; Android tablets do not.
|
|
39
|
-
// Check TABLET_RE first so `android(?!.*mobile)` captures tablets before
|
|
40
|
-
// the MOBILE_RE `android.*mobile` branch matches phones.
|
|
41
|
-
export const MOBILE_RE = /mobile|android.*mobile|iphone|ipod|webos|blackberry|opera mini|iemobile/i;
|
|
42
|
-
export const TABLET_RE = /ipad|tablet|kindle|silk|playbook|android(?!.*mobile)/i;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Simple mobile-or-not check (mobile + tablet = true).
|
|
46
|
-
* Use this for cache key splitting or any context where you
|
|
47
|
-
* only need a mobile/desktop binary decision.
|
|
48
|
-
*/
|
|
49
|
-
export function isMobileUA(userAgent: string): boolean {
|
|
50
|
-
return MOBILE_RE.test(userAgent) || TABLET_RE.test(userAgent);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Detect device type from a User-Agent string.
|
|
55
|
-
* Pure function — no side effects, works anywhere.
|
|
56
|
-
*/
|
|
57
|
-
export function detectDevice(userAgent: string): Device {
|
|
58
|
-
if (TABLET_RE.test(userAgent)) return "tablet";
|
|
59
|
-
if (MOBILE_RE.test(userAgent)) return "mobile";
|
|
60
|
-
return "desktop";
|
|
61
|
-
}
|
|
62
|
-
|
|
63
43
|
/**
|
|
64
44
|
* Resolve the current device from the ambient runtime (RequestContext on the
|
|
65
45
|
* server, `navigator.userAgent` on the client) — no React hooks involved.
|
|
66
46
|
* Used both by the plain `check*()` helpers below and by `useDevice()`/
|
|
67
47
|
* `DeviceProvider` in `./useDeviceContext`.
|
|
68
48
|
*/
|
|
69
|
-
export function resolveDeviceFromRuntime():
|
|
49
|
+
export function resolveDeviceFromRuntime(): ReturnType<typeof detectDevice> {
|
|
70
50
|
// Server: use RequestContext UA header
|
|
71
51
|
if (typeof document === "undefined") {
|
|
72
52
|
const ctx = RequestContext.current;
|