canopycms-next 0.0.68-int.102 → 0.0.68-int.103

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.
@@ -272,7 +272,7 @@ export async function createNextCanopyContext(options) {
272
272
  generateContentStaticParams,
273
273
  generateContentSitemap: boundGenerateContentSitemap,
274
274
  entryToMetadata: boundEntryToMetadata,
275
- createPreviewPage: (previewOptions) => createPreviewPageFor(getCanopy, previewOptions, options.config.deployedAs),
275
+ createPreviewPage: (previewOptions) => createPreviewPageFor(getCanopy, previewOptions, options.config),
276
276
  handler,
277
277
  services,
278
278
  };
@@ -1,6 +1,6 @@
1
- import type { ReactElement, ReactNode } from 'react';
1
+ import { type ReactElement, type ReactNode } from 'react';
2
2
  import type { CanopyConfig } from 'canopycms';
3
- import type { CanopyContext } from 'canopycms/server';
3
+ import { type CanopyContext } from 'canopycms/server';
4
4
  import type { CanopyPreviewProps } from './preview.js';
5
5
  type ReadEntry = NonNullable<Awaited<ReturnType<CanopyContext['readByUrlPath']>>>;
6
6
  /**
@@ -95,5 +95,5 @@ export interface PreviewPageProps {
95
95
  * so the route would show any branch to anyone.
96
96
  * @internal Exported for tests.
97
97
  */
98
- export declare function createPreviewPageFor(getCanopy: () => Promise<CanopyContext>, options: CreatePreviewPageOptions, deployedAs?: CanopyConfig['deployedAs']): (props: PreviewPageProps) => Promise<ReactElement>;
98
+ export declare function createPreviewPageFor(getCanopy: () => Promise<CanopyContext>, options: CreatePreviewPageOptions, config?: Partial<Pick<CanopyConfig, 'deployedAs' | 'basePath'>>): (props: PreviewPageProps) => Promise<ReactElement>;
99
99
  export {};
@@ -1,5 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cache } from 'react';
2
3
  import { notFound } from 'next/navigation';
4
+ import { authenticatedAssetBase, readAssetBase, setServerPreviewAssetBaseGetter, } from 'canopycms/server';
3
5
  /**
4
6
  * Pairs a view with its loader, so TypeScript checks that `load`'s result is what the view's
5
7
  * `extras` prop expects. A record of views cannot infer a distinct `X` per key, so this identity
@@ -13,6 +15,11 @@ function hasLoader(entry) {
13
15
  entry !== null &&
14
16
  Object.prototype.hasOwnProperty.call(entry, 'load'));
15
17
  }
18
+ let previewRequestScope;
19
+ // Created on first use, so this module loads where React has no `cache` (React 18); one instance
20
+ // serves every request.
21
+ const previewRequest = () => (previewRequestScope ??= cache(() => ({})))();
22
+ const readPreviewRequestAssetBase = () => previewRequest().assetBase;
16
23
  /**
17
24
  * The page for a `[[...path]]` route mounted at `editor.previewPrefix`: it reads the entry at
18
25
  * the route's path from the `?branch=` the editor names and renders its view.
@@ -26,9 +33,9 @@ function hasLoader(entry) {
26
33
  * so the route would show any branch to anyone.
27
34
  * @internal Exported for tests.
28
35
  */
29
- export function createPreviewPageFor(getCanopy, options, deployedAs = 'server') {
36
+ export function createPreviewPageFor(getCanopy, options, config = {}) {
30
37
  return async function CanopyPreviewPage({ params, searchParams }) {
31
- if (deployedAs === 'static')
38
+ if (config.deployedAs === 'static')
32
39
  notFound();
33
40
  const [{ path = [] }, query] = await Promise.all([params, searchParams]);
34
41
  const branch = query.branch;
@@ -48,9 +55,14 @@ export function createPreviewPageFor(getCanopy, options, deployedAs = 'server')
48
55
  : undefined;
49
56
  if (!viewEntry)
50
57
  notFound();
58
+ // From here the response is a preview, so its `/assets/t/` URLs go behind the signed-in route:
59
+ // server components (a `load`, `extras`) through the getter, the view through its prop.
60
+ const previewAssetBase = readAssetBase(authenticatedAssetBase(config.basePath));
61
+ previewRequest().assetBase = previewAssetBase;
62
+ setServerPreviewAssetBaseGetter(readPreviewRequestAssetBase);
51
63
  if (!hasLoader(viewEntry)) {
52
64
  const View = viewEntry;
53
- return _jsx(View, { initialData: result.data, editorOrigin: options.editorOrigin });
65
+ return (_jsx(View, { initialData: result.data, editorOrigin: options.editorOrigin, previewAssetBase: previewAssetBase }));
54
66
  }
55
67
  const { meta } = result;
56
68
  const entry = {
@@ -65,6 +77,6 @@ export function createPreviewPageFor(getCanopy, options, deployedAs = 'server')
65
77
  // The record erases `view` to `extras?: never`, so it cannot check `load`'s result against the
66
78
  // view; `previewView` does, where a pair is written.
67
79
  const View = viewEntry.view;
68
- return _jsx(View, { initialData: result.data, editorOrigin: options.editorOrigin, extras: extras });
80
+ return (_jsx(View, { initialData: result.data, editorOrigin: options.editorOrigin, extras: extras, previewAssetBase: previewAssetBase }));
69
81
  };
70
82
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * What `createPreviewPage` hands a view beside `CanopyPreviewProps`. Kept out of that public type,
3
+ * so adopter view code never sees it; `withCanopyPreview` consumes it and passes nothing on.
4
+ */
5
+ export interface PreviewRouteProps {
6
+ /** The signed-in asset route, which `usePreviewAssetBaseGate` sets before the view renders. */
7
+ previewAssetBase?: string;
8
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/preview.d.ts CHANGED
@@ -16,7 +16,9 @@ export interface CanopyPreviewProps<T, X = undefined> {
16
16
  }
17
17
  /**
18
18
  * Wraps a view so it renders the editor's live draft of `initialData`, through `useCanopyPreview`;
19
- * outside an editor frame it renders `initialData` unchanged.
19
+ * outside an editor frame it renders `initialData` unchanged. On `createPreviewPage`'s route the
20
+ * view is not server-rendered: it renders right after hydration, with images behind the signed-in
21
+ * asset route.
20
22
  *
21
23
  * Call it in your own `'use client'` module and pass the result to `createPreviewPage`'s `views`,
22
24
  * or render it on a public page. The wrapping lives in your module, not in `createPreviewPage`,
package/dist/preview.js CHANGED
@@ -1,9 +1,11 @@
1
1
  'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { useCanopyPreview } from 'canopycms/preview';
2
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useCanopyPreview, usePreviewAssetBaseGate } from 'canopycms/preview';
4
4
  /**
5
5
  * Wraps a view so it renders the editor's live draft of `initialData`, through `useCanopyPreview`;
6
- * outside an editor frame it renders `initialData` unchanged.
6
+ * outside an editor frame it renders `initialData` unchanged. On `createPreviewPage`'s route the
7
+ * view is not server-rendered: it renders right after hydration, with images behind the signed-in
8
+ * asset route.
7
9
  *
8
10
  * Call it in your own `'use client'` module and pass the result to `createPreviewPage`'s `views`,
9
11
  * or render it on a public page. The wrapping lives in your module, not in `createPreviewPage`,
@@ -15,10 +17,15 @@ import { useCanopyPreview } from 'canopycms/preview';
15
17
  * which JSX accepts on every supported TypeScript, where `ReactNode` needs `JSX.ElementType`.
16
18
  */
17
19
  export function withCanopyPreview(View) {
18
- return function CanopyPreview({ initialData, editorOrigin, extras }) {
20
+ return function CanopyPreview(props) {
21
+ const { initialData, editorOrigin, extras } = props;
22
+ const { previewAssetBase } = props;
19
23
  const preview = useCanopyPreview({ initialData, editorOrigin });
24
+ // On `createPreviewPage`'s route the view renders only once the gate has set the asset
25
+ // prefix; see `usePreviewAssetBaseGate`.
26
+ const open = usePreviewAssetBaseGate(previewAssetBase);
20
27
  // `extras` is a prop of its own, never spread into the hook's result, so it cannot shadow
21
28
  // `data` or `fieldProps`.
22
- return _jsx(View, { ...preview, extras: extras });
29
+ return open ? _jsx(View, { ...preview, extras: extras }) : _jsx(_Fragment, {});
23
30
  };
24
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canopycms-next",
3
- "version": "0.0.68-int.102",
3
+ "version": "0.0.68-int.103",
4
4
  "description": "Next.js adapter for CanopyCMS",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -43,7 +43,7 @@
43
43
  "peerDependencies": {
44
44
  "next": "^13.5.7 || ^14.2.25 || ^15.5.21 || >=16.0.0 <16.2.0 || >=16.3.0 <17.0.0",
45
45
  "react": "^18.0.0 || ^19.0.0",
46
- "canopycms": "0.0.68-int.102"
46
+ "canopycms": "0.0.68-int.103"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^22.9.0",
@@ -53,7 +53,7 @@
53
53
  "react": "^18.3.1",
54
54
  "typescript": "^5.6.3",
55
55
  "vitest": "^4.1.0",
56
- "canopycms": "0.0.68-int.102"
56
+ "canopycms": "0.0.68-int.103"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/add-js-extensions.mjs dist && esbuild src/config.ts --bundle --platform=node --packages=external --external:next --format=cjs --outfile=dist/config.cjs && esbuild src/config.ts --bundle --platform=node --packages=external --external:next --format=esm --outfile=dist/config.mjs",