@prismicio/next 0.1.2 → 0.2.0-alpha.0

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/dist/index.cjs CHANGED
@@ -7,8 +7,6 @@ const React = require('react');
7
7
  const react = require('@prismicio/react');
8
8
  const router = require('next/router');
9
9
 
10
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
-
12
10
  function _interopNamespace(e) {
13
11
  if (e && e.__esModule) return e;
14
12
  const n = Object.create(null);
@@ -28,7 +26,7 @@ function _interopNamespace(e) {
28
26
  }
29
27
 
30
28
  const prismic__namespace = /*#__PURE__*/_interopNamespace(prismic);
31
- const React__default = /*#__PURE__*/_interopDefaultLegacy(React);
29
+ const React__namespace = /*#__PURE__*/_interopNamespace(React);
32
30
 
33
31
  function setPreviewData({ req, res }) {
34
32
  const ref = req.query.token || req.cookies[prismic__namespace.cookie.preview];
@@ -38,34 +36,24 @@ function setPreviewData({ req, res }) {
38
36
  }
39
37
 
40
38
  function exitPreview(config) {
41
- const { req } = config;
42
39
  config.res.clearPreviewData();
43
- if (req.headers.referer) {
44
- const url = new URL(req.headers.referer);
45
- if (url.pathname !== "/api/exit-preview") {
46
- config.res.redirect(req.headers.referer);
47
- return;
48
- }
49
- }
50
- config.res.redirect("/");
40
+ config.res.json({ success: true });
51
41
  }
52
42
 
53
43
  const readValue = (value) => {
54
44
  return value.replace(/%3B/g, ";");
55
45
  };
56
- const parse = (cookieString) => {
57
- const result = {};
58
- const cookies = cookieString.split("; ");
46
+ const getCookie = (name) => {
47
+ const cookies = document.cookie.split("; ");
59
48
  for (const cookie of cookies) {
60
49
  const parts = cookie.split("=");
61
50
  const value = parts.slice(1).join("=");
62
- const name = readValue(parts[0]).replace(/%3D/g, "=");
63
- result[name] = readValue(value);
51
+ const thisName = readValue(parts[0]).replace(/%3D/g, "=");
52
+ if (thisName === name) {
53
+ return readValue(value);
54
+ }
64
55
  }
65
- return result;
66
56
  };
67
- const getAll = (cookieStore) => parse(cookieStore);
68
- const getCookie = (name, cookieStore) => getAll(cookieStore)[name];
69
57
 
70
58
  const extractFirstSubdomain = (host) => host.split(".")[0];
71
59
  const extractRepositoryNameFromObjectRef = (previewRef) => {
@@ -94,51 +82,64 @@ const extractPreviewRefRepositoryName = (previewRef) => {
94
82
  return extractRepositoryNameFromObjectRef(previewRef) || extractRepositoryNameFromURLRef(previewRef);
95
83
  };
96
84
 
97
- const isPrismicUpdateToolbarEvent = (event) => {
98
- return "detail" in event && typeof event.detail.ref === "string";
85
+ const updatePreviewMode = async (updatePreviewURL) => {
86
+ const res = await globalThis.fetch(updatePreviewURL);
87
+ if (res.ok) {
88
+ window.location.reload();
89
+ } else {
90
+ console.error(`[<PrismicPreview>] Failed to start or update Preview Mode using the "${updatePreviewURL}" API endpoint. Does it exist?`);
91
+ }
92
+ };
93
+ const exitPreviewMode = async (exitPreviewURL) => {
94
+ const res = await globalThis.fetch(exitPreviewURL);
95
+ if (res.ok) {
96
+ window.location.reload();
97
+ } else {
98
+ console.error(`[<PrismicPreview>] Failed to exit Preview Mode using the "${exitPreviewURL}" API endpoint. Does it exist?`);
99
+ }
99
100
  };
100
101
  function PrismicPreview({
101
102
  repositoryName,
102
- children,
103
103
  updatePreviewURL = "/api/preview",
104
- exitPreviewURL = "/api/exit-preview"
104
+ exitPreviewURL = "/api/exit-preview",
105
+ children
105
106
  }) {
106
107
  const router$1 = router.useRouter();
107
- React.useEffect(() => {
108
- const previewRefRepositoryName = extractPreviewRefRepositoryName(getCookie("io.prismic.preview", globalThis.document.cookie));
109
- const startPreviewIfLoadedFromSharedLink = async () => {
110
- if (previewRefRepositoryName === repositoryName && !router$1.isPreview) {
111
- await fetch(updatePreviewURL);
112
- window.location.reload();
113
- }
114
- };
115
- startPreviewIfLoadedFromSharedLink();
108
+ const resolvedUpdatePreviewURL = router$1.basePath + updatePreviewURL;
109
+ const resolvedExitPreviewURL = router$1.basePath + exitPreviewURL;
110
+ React__namespace.useEffect(() => {
111
+ const previewCookie = getCookie(prismic__namespace.cookie.preview);
112
+ const prismicPreviewSessionIsForThisRepo = Boolean(previewCookie && extractPreviewRefRepositoryName(previewCookie) === repositoryName);
113
+ const locationIsDescendantOfBasePath = window.location.href.startsWith(window.location.origin + router$1.basePath);
114
+ const loadedFromShareLink = !router$1.isPreview && previewCookie;
115
+ if (prismicPreviewSessionIsForThisRepo && locationIsDescendantOfBasePath && loadedFromShareLink) {
116
+ updatePreviewMode(resolvedUpdatePreviewURL);
117
+ return;
118
+ }
116
119
  const handlePrismicPreviewUpdate = async (event) => {
117
- if (isPrismicUpdateToolbarEvent(event)) {
118
- event.preventDefault();
119
- await fetch(updatePreviewURL);
120
- window.location.reload();
121
- }
120
+ event.preventDefault();
121
+ await updatePreviewMode(resolvedUpdatePreviewURL);
122
122
  };
123
123
  const handlePrismicPreviewEnd = async (event) => {
124
124
  event.preventDefault();
125
- await fetch(exitPreviewURL);
126
- window.location.reload();
125
+ await exitPreviewMode(resolvedExitPreviewURL);
127
126
  };
128
- if (window) {
129
- window.addEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
130
- window.addEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
131
- }
127
+ window.addEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
128
+ window.addEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
132
129
  return () => {
133
- if (window) {
134
- window.removeEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
135
- window.removeEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
136
- }
130
+ window.removeEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
131
+ window.removeEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
137
132
  };
138
- }, []);
139
- return /* @__PURE__ */ React__default["default"].createElement(React__default["default"].Fragment, null, /* @__PURE__ */ React__default["default"].createElement(react.PrismicToolbar, {
133
+ }, [
134
+ repositoryName,
135
+ resolvedUpdatePreviewURL,
136
+ resolvedExitPreviewURL,
137
+ router$1.isPreview,
138
+ router$1.basePath
139
+ ]);
140
+ return /* @__PURE__ */ React__namespace.createElement(React__namespace.Fragment, null, children, /* @__PURE__ */ React__namespace.createElement(react.PrismicToolbar, {
140
141
  repositoryName
141
- }), children);
142
+ }));
142
143
  }
143
144
 
144
145
  const isPrismicNextPreviewData = (previewData) => {
@@ -155,26 +156,23 @@ const enableAutoPreviews = (config) => {
155
156
  }
156
157
  };
157
158
 
158
- const isPrismicNextQuery = (query) => typeof query.documentId === "string" && typeof query.token === "string";
159
- async function redirectToPreviewURL({
160
- req,
161
- res,
162
- client,
163
- linkResolver,
164
- defaultURL = "/"
165
- }) {
166
- if (isPrismicNextQuery(req.query)) {
167
- const { documentId, token } = req.query;
168
- const previewUrl = await client.resolvePreviewURL({
169
- linkResolver,
159
+ const isPrismicNextQuery = (query) => {
160
+ return typeof query.documentId === "string" && typeof query.token === "string";
161
+ };
162
+ async function redirectToPreviewURL(config) {
163
+ const defaultURL = config.defaultURL || "/";
164
+ const basePath = config.basePath || "";
165
+ if (isPrismicNextQuery(config.req.query)) {
166
+ const previewUrl = await config.client.resolvePreviewURL({
167
+ linkResolver: config.linkResolver,
170
168
  defaultURL,
171
- documentID: documentId,
172
- previewToken: token
169
+ documentID: config.req.query.documentId,
170
+ previewToken: config.req.query.token
173
171
  });
174
- res.redirect(previewUrl);
172
+ config.res.redirect(basePath + previewUrl);
175
173
  return;
176
174
  }
177
- res.redirect(defaultURL);
175
+ config.res.redirect(basePath + defaultURL);
178
176
  }
179
177
 
180
178
  exports.PrismicPreview = PrismicPreview;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NextApiRequest, NextApiResponse, PreviewData } from 'next';
2
+ import * as React from 'react';
2
3
  import { Client, HttpRequestLike } from '@prismicio/client';
3
- import React from 'react';
4
4
  import { LinkResolverFunction } from '@prismicio/helpers';
5
5
 
6
6
  /**
@@ -13,61 +13,20 @@ declare type SetPreviewDataConfig = {
13
13
  *
14
14
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
15
15
  */
16
- req: {
17
- query: NextApiRequest["query"];
18
- cookies: NextApiRequest["cookies"];
19
- };
16
+ req: Pick<NextApiRequest, "query" | "cookies">;
20
17
  /**
21
18
  * The `res` object from a Next.js API route. This is given as a parameter to
22
19
  * the API route.
23
20
  *
24
21
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
25
22
  */
26
- res: {
27
- setPreviewData: NextApiResponse["setPreviewData"];
28
- };
23
+ res: Pick<NextApiResponse, "setPreviewData">;
29
24
  };
30
25
  /**
31
26
  * Set Prismic preview data for Next.js's Preview Mode.
32
27
  */
33
28
  declare function setPreviewData({ req, res }: SetPreviewDataConfig): void;
34
29
 
35
- /**
36
- * Configuration for `enableAutoPreviews`.
37
- *
38
- * @typeParam TPreviewData - Next.js preview data object.
39
- */
40
- declare type EnableAutoPreviewsConfig<TPreviewData extends PreviewData = PreviewData> = {
41
- /**
42
- * Prismic client with which automatic previews will be enabled.
43
- */
44
- client: Client;
45
- } & ({
46
- /**
47
- * A Next.js context object (such as the context object from
48
- * `getStaticProps` or `getServerSideProps`).
49
- *
50
- * Pass a `context` object when using `enableAutoPreviews` outside a
51
- * Next.js API endpoint.
52
- */
53
- previewData?: TPreviewData;
54
- } | {
55
- /**
56
- * A Next.js API endpoint request object.
57
- *
58
- * Pass a `req` object when using `enableAutoPreviews` in a Next.js API endpoint.
59
- */
60
- req?: HttpRequestLike;
61
- });
62
- /**
63
- * Configures a Prismic client to automatically query draft content during a
64
- * preview session. It either takes in a Next.js `getStaticProps` context object
65
- * or a Next.js API endpoint request object.
66
- *
67
- * @param config - Configuration for the function.
68
- */
69
- declare const enableAutoPreviews: <TPreviewData extends PreviewData>(config: EnableAutoPreviewsConfig<TPreviewData>) => void;
70
-
71
30
  /**
72
31
  * Configuration for `exitPreview`.
73
32
  */
@@ -78,27 +37,17 @@ declare type ExitPreviewConfig = {
78
37
  *
79
38
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
80
39
  */
81
- req: {
82
- headers: {
83
- referer?: NextApiRequest["headers"]["referer"];
84
- };
85
- };
40
+ req: Pick<NextApiRequest, "headers">;
86
41
  /**
87
42
  * The `res` object from a Next.js API route. This is given as a parameter to
88
43
  * the API route.
89
44
  *
90
45
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
91
46
  */
92
- res: {
93
- clearPreviewData: NextApiResponse["clearPreviewData"];
94
- redirect: NextApiResponse["redirect"];
95
- };
47
+ res: Pick<NextApiResponse, "clearPreviewData" | "json">;
96
48
  };
97
49
  /**
98
50
  * Exits Next.js's Preview Mode from within a Next.js API route.
99
- *
100
- * If the user was sent to the endpoint from a page, the user will be redirected
101
- * back to that page after exiting Preview Mode.
102
51
  */
103
52
  declare function exitPreview(config: ExitPreviewConfig): void;
104
53
 
@@ -114,13 +63,23 @@ declare type PrismicPreviewProps = {
114
63
  /**
115
64
  * The URL of your app's Prismic preview endpoint (default: `/api/preview`).
116
65
  * This URL will be fetched on preview update events.
66
+ *
67
+ * **Note**: If your `next.config.js` file contains a `basePath`, it is
68
+ * automatically included.
117
69
  */
118
70
  updatePreviewURL?: string;
119
71
  /**
120
72
  * The URL of your app's exit preview endpoint (default: `/api/exit-preview`).
121
73
  * This URL will be fetched on preview exit events.
74
+ *
75
+ * **Note**: If your `next.config.js` file contains a `basePath`, it is
76
+ * automatically included.
122
77
  */
123
78
  exitPreviewURL?: string;
79
+ /**
80
+ * Children to render adjacent to the Prismic Toolbar. The Prismic Toolbar
81
+ * will be rendered last.
82
+ */
124
83
  children?: React.ReactNode;
125
84
  };
126
85
  /**
@@ -132,55 +91,62 @@ declare type PrismicPreviewProps = {
132
91
  * This component can be wrapped around your app or added anywhere in your app's
133
92
  * tree. It must be rendered on every page.
134
93
  */
135
- declare function PrismicPreview({ repositoryName, children, updatePreviewURL, exitPreviewURL, }: PrismicPreviewProps): JSX.Element;
136
-
137
- /**
138
- * Redirects a user to the URL of a previewed Prismic document from within a
139
- * Next.js API route.
140
- */
141
- declare function redirectToPreviewURL<TLinkResolverFunction extends LinkResolverFunction<any>>({ req, res, client, linkResolver, defaultURL, }: PreviewConfig<TLinkResolverFunction>): Promise<void>;
94
+ declare function PrismicPreview({ repositoryName, updatePreviewURL, exitPreviewURL, children, }: PrismicPreviewProps): JSX.Element;
142
95
 
143
96
  /**
144
- * Configuration for creating a Prismic client with automatic preview support in
145
- * Next.js apps.
97
+ * Configuration for `enableAutoPreviews`.
98
+ *
99
+ * @typeParam TPreviewData - Next.js preview data object.
146
100
  */
147
- declare type CreateClientConfig = {
101
+ declare type EnableAutoPreviewsConfig<TPreviewData extends PreviewData = PreviewData> = {
148
102
  /**
149
- * Preview data coming from Next.js context object. This context object comes
150
- * from `getStaticProps` or `getServerSideProps`.
103
+ * Prismic client with which automatic previews will be enabled.
104
+ */
105
+ client: Client;
106
+ } & ({
107
+ /**
108
+ * A Next.js context object (such as the context object from
109
+ * `getStaticProps` or `getServerSideProps`).
151
110
  *
152
- * Pass `previewData` when using outside a Next.js API endpoint.
111
+ * Pass a `context` object when using `enableAutoPreviews` outside a
112
+ * Next.js API endpoint.
153
113
  */
154
- previewData?: PreviewData;
114
+ previewData?: TPreviewData;
115
+ } | {
155
116
  /**
156
117
  * A Next.js API endpoint request object.
157
118
  *
158
- * Pass a `req` object when using in a Next.js API endpoint.
119
+ * Pass a `req` object when using `enableAutoPreviews` in a Next.js API endpoint.
159
120
  */
160
- req?: NextApiRequest;
161
- };
121
+ req?: HttpRequestLike;
122
+ });
123
+ /**
124
+ * Configures a Prismic client to automatically query draft content during a
125
+ * preview session. It either takes in a Next.js `getStaticProps` context object
126
+ * or a Next.js API endpoint request object.
127
+ *
128
+ * @param config - Configuration for the function.
129
+ */
130
+ declare const enableAutoPreviews: <TPreviewData extends PreviewData>(config: EnableAutoPreviewsConfig<TPreviewData>) => void;
131
+
162
132
  /**
163
133
  * Preview config for enabling previews with redirectToPreviewURL
164
134
  */
165
- declare type PreviewConfig<TLinkResolverFunction extends LinkResolverFunction<any> = LinkResolverFunction> = {
135
+ declare type RedirectToPreviewURLConfig<TLinkResolverFunction extends LinkResolverFunction<any> = LinkResolverFunction> = {
166
136
  /**
167
137
  * The `req` object from a Next.js API route. This is given as a parameter to
168
138
  * the API route.
169
139
  *
170
140
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
171
141
  */
172
- req: {
173
- query: NextApiRequest["query"];
174
- };
142
+ req: Pick<NextApiRequest, "query">;
175
143
  /**
176
144
  * The `res` object from a Next.js API route. This is given as a parameter to
177
145
  * the API route.
178
146
  *
179
147
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
180
148
  */
181
- res: {
182
- redirect: NextApiResponse["redirect"];
183
- };
149
+ res: Pick<NextApiResponse, "redirect">;
184
150
  /**
185
151
  * The Prismic client configured for the preview session's repository.
186
152
  */
@@ -193,8 +159,46 @@ declare type PreviewConfig<TLinkResolverFunction extends LinkResolverFunction<an
193
159
  linkResolver?: TLinkResolverFunction;
194
160
  /**
195
161
  * The default redirect URL if a URL cannot be determined for the previewed document.
162
+ *
163
+ * **Note**: If your `next.config.js` file contains a `basePath`, you must
164
+ * include the `basePath` as part of this option. `redirectToPreviewURL()`
165
+ * cannot read your global `basePath`.
196
166
  */
197
167
  defaultURL?: string;
168
+ /**
169
+ * The `basePath` for the Next.js app as it is defined in `next.config.js`.
170
+ * This option can be omitted if the app does not have a `basePath`.
171
+ *
172
+ * @remarks
173
+ * The API route is unable to detect the app's `basePath` automatically. It
174
+ * must be provided to `redirectToPreviewURL()` manually.
175
+ */
176
+ basePath?: string;
177
+ };
178
+ /**
179
+ * Redirects a user to the URL of a previewed Prismic document from within a
180
+ * Next.js API route.
181
+ */
182
+ declare function redirectToPreviewURL<TLinkResolverFunction extends LinkResolverFunction<any>>(config: RedirectToPreviewURLConfig<TLinkResolverFunction>): Promise<void>;
183
+
184
+ /**
185
+ * Configuration for creating a Prismic client with automatic preview support in
186
+ * Next.js apps.
187
+ */
188
+ declare type CreateClientConfig = {
189
+ /**
190
+ * Preview data coming from Next.js context object. This context object comes
191
+ * from `getStaticProps` or `getServerSideProps`.
192
+ *
193
+ * Pass `previewData` when using outside a Next.js API endpoint.
194
+ */
195
+ previewData?: PreviewData;
196
+ /**
197
+ * A Next.js API endpoint request object.
198
+ *
199
+ * Pass a `req` object when using in a Next.js API endpoint.
200
+ */
201
+ req?: NextApiRequest;
198
202
  };
199
203
 
200
- export { CreateClientConfig, EnableAutoPreviewsConfig, ExitPreviewConfig as ExitPreviewParams, PreviewConfig, PrismicPreview, PrismicPreviewProps, SetPreviewDataConfig, enableAutoPreviews, exitPreview, redirectToPreviewURL, setPreviewData };
204
+ export { CreateClientConfig, EnableAutoPreviewsConfig, ExitPreviewConfig, PrismicPreview, PrismicPreviewProps, RedirectToPreviewURLConfig, SetPreviewDataConfig, enableAutoPreviews, exitPreview, redirectToPreviewURL, setPreviewData };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as prismic from '@prismicio/client';
2
- import React, { useEffect } from 'react';
2
+ import * as React from 'react';
3
3
  import { PrismicToolbar } from '@prismicio/react';
4
4
  import { useRouter } from 'next/router';
5
5
 
@@ -11,34 +11,24 @@ function setPreviewData({ req, res }) {
11
11
  }
12
12
 
13
13
  function exitPreview(config) {
14
- const { req } = config;
15
14
  config.res.clearPreviewData();
16
- if (req.headers.referer) {
17
- const url = new URL(req.headers.referer);
18
- if (url.pathname !== "/api/exit-preview") {
19
- config.res.redirect(req.headers.referer);
20
- return;
21
- }
22
- }
23
- config.res.redirect("/");
15
+ config.res.json({ success: true });
24
16
  }
25
17
 
26
18
  const readValue = (value) => {
27
19
  return value.replace(/%3B/g, ";");
28
20
  };
29
- const parse = (cookieString) => {
30
- const result = {};
31
- const cookies = cookieString.split("; ");
21
+ const getCookie = (name) => {
22
+ const cookies = document.cookie.split("; ");
32
23
  for (const cookie of cookies) {
33
24
  const parts = cookie.split("=");
34
25
  const value = parts.slice(1).join("=");
35
- const name = readValue(parts[0]).replace(/%3D/g, "=");
36
- result[name] = readValue(value);
26
+ const thisName = readValue(parts[0]).replace(/%3D/g, "=");
27
+ if (thisName === name) {
28
+ return readValue(value);
29
+ }
37
30
  }
38
- return result;
39
31
  };
40
- const getAll = (cookieStore) => parse(cookieStore);
41
- const getCookie = (name, cookieStore) => getAll(cookieStore)[name];
42
32
 
43
33
  const extractFirstSubdomain = (host) => host.split(".")[0];
44
34
  const extractRepositoryNameFromObjectRef = (previewRef) => {
@@ -67,51 +57,64 @@ const extractPreviewRefRepositoryName = (previewRef) => {
67
57
  return extractRepositoryNameFromObjectRef(previewRef) || extractRepositoryNameFromURLRef(previewRef);
68
58
  };
69
59
 
70
- const isPrismicUpdateToolbarEvent = (event) => {
71
- return "detail" in event && typeof event.detail.ref === "string";
60
+ const updatePreviewMode = async (updatePreviewURL) => {
61
+ const res = await globalThis.fetch(updatePreviewURL);
62
+ if (res.ok) {
63
+ window.location.reload();
64
+ } else {
65
+ console.error(`[<PrismicPreview>] Failed to start or update Preview Mode using the "${updatePreviewURL}" API endpoint. Does it exist?`);
66
+ }
67
+ };
68
+ const exitPreviewMode = async (exitPreviewURL) => {
69
+ const res = await globalThis.fetch(exitPreviewURL);
70
+ if (res.ok) {
71
+ window.location.reload();
72
+ } else {
73
+ console.error(`[<PrismicPreview>] Failed to exit Preview Mode using the "${exitPreviewURL}" API endpoint. Does it exist?`);
74
+ }
72
75
  };
73
76
  function PrismicPreview({
74
77
  repositoryName,
75
- children,
76
78
  updatePreviewURL = "/api/preview",
77
- exitPreviewURL = "/api/exit-preview"
79
+ exitPreviewURL = "/api/exit-preview",
80
+ children
78
81
  }) {
79
82
  const router = useRouter();
80
- useEffect(() => {
81
- const previewRefRepositoryName = extractPreviewRefRepositoryName(getCookie("io.prismic.preview", globalThis.document.cookie));
82
- const startPreviewIfLoadedFromSharedLink = async () => {
83
- if (previewRefRepositoryName === repositoryName && !router.isPreview) {
84
- await fetch(updatePreviewURL);
85
- window.location.reload();
86
- }
87
- };
88
- startPreviewIfLoadedFromSharedLink();
83
+ const resolvedUpdatePreviewURL = router.basePath + updatePreviewURL;
84
+ const resolvedExitPreviewURL = router.basePath + exitPreviewURL;
85
+ React.useEffect(() => {
86
+ const previewCookie = getCookie(prismic.cookie.preview);
87
+ const prismicPreviewSessionIsForThisRepo = Boolean(previewCookie && extractPreviewRefRepositoryName(previewCookie) === repositoryName);
88
+ const locationIsDescendantOfBasePath = window.location.href.startsWith(window.location.origin + router.basePath);
89
+ const loadedFromShareLink = !router.isPreview && previewCookie;
90
+ if (prismicPreviewSessionIsForThisRepo && locationIsDescendantOfBasePath && loadedFromShareLink) {
91
+ updatePreviewMode(resolvedUpdatePreviewURL);
92
+ return;
93
+ }
89
94
  const handlePrismicPreviewUpdate = async (event) => {
90
- if (isPrismicUpdateToolbarEvent(event)) {
91
- event.preventDefault();
92
- await fetch(updatePreviewURL);
93
- window.location.reload();
94
- }
95
+ event.preventDefault();
96
+ await updatePreviewMode(resolvedUpdatePreviewURL);
95
97
  };
96
98
  const handlePrismicPreviewEnd = async (event) => {
97
99
  event.preventDefault();
98
- await fetch(exitPreviewURL);
99
- window.location.reload();
100
+ await exitPreviewMode(resolvedExitPreviewURL);
100
101
  };
101
- if (window) {
102
- window.addEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
103
- window.addEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
104
- }
102
+ window.addEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
103
+ window.addEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
105
104
  return () => {
106
- if (window) {
107
- window.removeEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
108
- window.removeEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
109
- }
105
+ window.removeEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
106
+ window.removeEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
110
107
  };
111
- }, []);
112
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(PrismicToolbar, {
108
+ }, [
109
+ repositoryName,
110
+ resolvedUpdatePreviewURL,
111
+ resolvedExitPreviewURL,
112
+ router.isPreview,
113
+ router.basePath
114
+ ]);
115
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, children, /* @__PURE__ */ React.createElement(PrismicToolbar, {
113
116
  repositoryName
114
- }), children);
117
+ }));
115
118
  }
116
119
 
117
120
  const isPrismicNextPreviewData = (previewData) => {
@@ -128,26 +131,23 @@ const enableAutoPreviews = (config) => {
128
131
  }
129
132
  };
130
133
 
131
- const isPrismicNextQuery = (query) => typeof query.documentId === "string" && typeof query.token === "string";
132
- async function redirectToPreviewURL({
133
- req,
134
- res,
135
- client,
136
- linkResolver,
137
- defaultURL = "/"
138
- }) {
139
- if (isPrismicNextQuery(req.query)) {
140
- const { documentId, token } = req.query;
141
- const previewUrl = await client.resolvePreviewURL({
142
- linkResolver,
134
+ const isPrismicNextQuery = (query) => {
135
+ return typeof query.documentId === "string" && typeof query.token === "string";
136
+ };
137
+ async function redirectToPreviewURL(config) {
138
+ const defaultURL = config.defaultURL || "/";
139
+ const basePath = config.basePath || "";
140
+ if (isPrismicNextQuery(config.req.query)) {
141
+ const previewUrl = await config.client.resolvePreviewURL({
142
+ linkResolver: config.linkResolver,
143
143
  defaultURL,
144
- documentID: documentId,
145
- previewToken: token
144
+ documentID: config.req.query.documentId,
145
+ previewToken: config.req.query.token
146
146
  });
147
- res.redirect(previewUrl);
147
+ config.res.redirect(basePath + previewUrl);
148
148
  return;
149
149
  }
150
- res.redirect(defaultURL);
150
+ config.res.redirect(basePath + defaultURL);
151
151
  }
152
152
 
153
153
  export { PrismicPreview, enableAutoPreviews, exitPreview, redirectToPreviewURL, setPreviewData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismicio/next",
3
- "version": "0.1.2",
3
+ "version": "0.2.0-alpha.0",
4
4
  "description": "Helpers to integrate Prismic into Next.js apps",
5
5
  "keywords": [
6
6
  "typescript",
@@ -41,44 +41,38 @@
41
41
  "release:dry": "standard-version --dry-run",
42
42
  "size": "size-limit",
43
43
  "test": "npm run lint && npm run unit && npm run build && npm run size",
44
- "unit": "nyc --reporter=lcovonly --reporter=text --exclude-after-remap=false ava"
44
+ "unit": "vitest run --coverage",
45
+ "unit:watch": "vitest watch"
45
46
  },
46
47
  "dependencies": {
47
- "@prismicio/react": "^2.2.0"
48
+ "@prismicio/client": "^6.0.0",
49
+ "@prismicio/react": "^2.2.0",
50
+ "@prismicio/types": "^0.1.27"
48
51
  },
49
52
  "devDependencies": {
50
- "@prismicio/client": "^6.4.2",
51
- "@prismicio/mock": "^0.0.9",
52
- "@prismicio/types": "^0.1.27",
53
53
  "@size-limit/preset-small-lib": "^7.0.8",
54
- "@testing-library/react": "^13.0.0",
55
- "@types/jsdom": "^16.2.14",
56
- "@types/jsdom-global": "^3.0.2",
57
54
  "@types/react-test-renderer": "^17.0.1",
58
- "@types/sinon": "^10.0.11",
59
55
  "@typescript-eslint/eslint-plugin": "^5.17.0",
60
56
  "@typescript-eslint/parser": "^5.17.0",
61
- "ava": "^4.1.0",
57
+ "c8": "^7.11.0",
62
58
  "eslint": "^8.12.0",
63
59
  "eslint-config-prettier": "^8.5.0",
64
60
  "eslint-plugin-prettier": "^4.0.0",
61
+ "eslint-plugin-react": "^7.29.4",
62
+ "eslint-plugin-react-hooks": "^4.4.0",
65
63
  "eslint-plugin-tsdoc": "^0.2.14",
66
- "jsdom": "^19.0.0",
67
- "jsdom-global": "^3.0.2",
68
- "msw": "^0.39.2",
64
+ "happy-dom": "^2.55.0",
69
65
  "next": "^12.1.4",
70
- "node-fetch": "^3.2.3",
71
66
  "nyc": "^15.1.0",
72
67
  "prettier": "^2.6.1",
73
68
  "prettier-plugin-jsdoc": "^0.3.35",
74
69
  "react": "^18.0.0",
75
- "react-dom": "^18.0.0",
76
- "sinon": "^13.0.1",
70
+ "react-test-renderer": "^18.0.0",
77
71
  "siroc": "^0.16.0",
78
72
  "size-limit": "^7.0.8",
79
73
  "standard-version": "^9.3.2",
80
- "ts-eager": "^2.0.2",
81
- "typescript": "^4.6.3"
74
+ "typescript": "^4.6.3",
75
+ "vitest": "^0.8.1"
82
76
  },
83
77
  "peerDependencies": {
84
78
  "@prismicio/client": "^6.0.0",
@@ -1,4 +1,5 @@
1
- import React, { useEffect } from "react";
1
+ import * as React from "react";
2
+ import * as prismic from "@prismicio/client";
2
3
  import { PrismicToolbar } from "@prismicio/react";
3
4
  import { useRouter } from "next/router";
4
5
 
@@ -18,31 +19,65 @@ export type PrismicPreviewProps = {
18
19
  /**
19
20
  * The URL of your app's Prismic preview endpoint (default: `/api/preview`).
20
21
  * This URL will be fetched on preview update events.
22
+ *
23
+ * **Note**: If your `next.config.js` file contains a `basePath`, it is
24
+ * automatically included.
21
25
  */
22
26
  updatePreviewURL?: string;
23
27
 
24
28
  /**
25
29
  * The URL of your app's exit preview endpoint (default: `/api/exit-preview`).
26
30
  * This URL will be fetched on preview exit events.
31
+ *
32
+ * **Note**: If your `next.config.js` file contains a `basePath`, it is
33
+ * automatically included.
27
34
  */
28
35
  exitPreviewURL?: string;
29
36
 
37
+ /**
38
+ * Children to render adjacent to the Prismic Toolbar. The Prismic Toolbar
39
+ * will be rendered last.
40
+ */
30
41
  children?: React.ReactNode;
31
42
  };
32
43
 
33
44
  /**
34
- * Determines if an Event object came from the Prismic Toolbar.
45
+ * Updates (or starts) Next.js Preview Mode using a given API endpoint and
46
+ * reloads the page.
35
47
  *
36
- * @param event - Event to check.
48
+ * @param updatePreviewURL - The API endpoint that sets Preview Mode data.
49
+ */
50
+ const updatePreviewMode = async (updatePreviewURL: string): Promise<void> => {
51
+ // Start Next.js Preview Mode via the given preview API endpoint.
52
+ const res = await globalThis.fetch(updatePreviewURL);
53
+
54
+ if (res.ok) {
55
+ // Reload the page with an active Preview Mode.
56
+ window.location.reload();
57
+ } else {
58
+ console.error(
59
+ `[<PrismicPreview>] Failed to start or update Preview Mode using the "${updatePreviewURL}" API endpoint. Does it exist?`,
60
+ );
61
+ }
62
+ };
63
+
64
+ /**
65
+ * Exits Next.js Preview Mode using a given API endpoint and reloads the page.
37
66
  *
38
- * @returns `true` if `event` came from the Prismic Toolbar, `false` otherwise.
67
+ * @param exitPreviewURL - The API endpoint that exits Preview Mode.
39
68
  */
40
- const isPrismicUpdateToolbarEvent = (
41
- event: Event,
42
- ): event is CustomEvent<{ ref: string }> => {
43
- return (
44
- "detail" in event && typeof (event as CustomEvent).detail.ref === "string"
45
- );
69
+ const exitPreviewMode = async (exitPreviewURL: string): Promise<void> => {
70
+ // Exit Next.js Preview Mode via the given exit preview API endpoint.
71
+ const res = await globalThis.fetch(exitPreviewURL);
72
+
73
+ if (res.ok) {
74
+ // Reload the page with an inactive Preview Mode.
75
+ window.location.reload();
76
+ } else {
77
+ console.error(
78
+ `[<PrismicPreview>] Failed to exit Preview Mode using the "${exitPreviewURL}" API endpoint. Does it exist?`,
79
+ );
80
+ }
46
81
  };
47
82
 
48
83
  /**
@@ -56,78 +91,96 @@ const isPrismicUpdateToolbarEvent = (
56
91
  */
57
92
  export function PrismicPreview({
58
93
  repositoryName,
59
- children,
60
94
  updatePreviewURL = "/api/preview",
61
95
  exitPreviewURL = "/api/exit-preview",
96
+ children,
62
97
  }: PrismicPreviewProps): JSX.Element {
63
98
  const router = useRouter();
64
99
 
65
- useEffect(() => {
66
- const previewRefRepositoryName = extractPreviewRefRepositoryName(
67
- getCookie("io.prismic.preview", globalThis.document.cookie) as string,
100
+ const resolvedUpdatePreviewURL = router.basePath + updatePreviewURL;
101
+ const resolvedExitPreviewURL = router.basePath + exitPreviewURL;
102
+
103
+ React.useEffect(() => {
104
+ /**
105
+ * The Prismic preview cookie.
106
+ */
107
+ const previewCookie = getCookie(prismic.cookie.preview);
108
+
109
+ /**
110
+ * Determines if the current Prismic preview session is for the repository
111
+ * configured for this instance of `<PrismicPreview>`.
112
+ */
113
+ const prismicPreviewSessionIsForThisRepo = Boolean(
114
+ previewCookie &&
115
+ extractPreviewRefRepositoryName(previewCookie) === repositoryName,
68
116
  );
69
117
 
70
- const startPreviewIfLoadedFromSharedLink = async () => {
71
- if (previewRefRepositoryName === repositoryName && !router.isPreview) {
72
- await fetch(updatePreviewURL);
73
- window.location.reload();
74
- }
75
- };
118
+ /**
119
+ * Determines if the current location is a descendant of the app's base path.
120
+ *
121
+ * This is used to prevent infinite refrehes; when `isDescendantOfBasePath`
122
+ * is `false`, `router.isPreview` is also `false`.
123
+ *
124
+ * If the app does not have a base path, this should always be `true`.
125
+ */
126
+ const locationIsDescendantOfBasePath = window.location.href.startsWith(
127
+ window.location.origin + router.basePath,
128
+ );
76
129
 
77
- startPreviewIfLoadedFromSharedLink();
130
+ /**
131
+ * Determines if the current user loaded the page from a share link.
132
+ *
133
+ * Currently, we can only deduce this by checking if router.isPreview is
134
+ * false (i.e. Preview Mode is inactive) and a Prismic preview cookie is present.
135
+ */
136
+ const loadedFromShareLink = !router.isPreview && previewCookie;
137
+
138
+ if (
139
+ prismicPreviewSessionIsForThisRepo &&
140
+ locationIsDescendantOfBasePath &&
141
+ loadedFromShareLink
142
+ ) {
143
+ updatePreviewMode(resolvedUpdatePreviewURL);
144
+
145
+ return;
146
+ }
78
147
 
79
148
  const handlePrismicPreviewUpdate = async (event: Event) => {
80
- if (isPrismicUpdateToolbarEvent(event)) {
81
- // Prevent the toolbar from reloading the page.
82
- event.preventDefault();
83
-
84
- // Start Next.js Preview Mode via the given preview API endpoint.
85
- await fetch(updatePreviewURL);
86
-
87
- // Reload the page with an active Preview Mode.
88
- window.location.reload();
89
- }
149
+ // Prevent the toolbar from reloading the page.
150
+ event.preventDefault();
151
+ await updatePreviewMode(resolvedUpdatePreviewURL);
90
152
  };
91
153
 
92
154
  const handlePrismicPreviewEnd = async (event: Event) => {
93
155
  // Prevent the toolbar from reloading the page.
94
156
  event.preventDefault();
95
-
96
- // Exit Next.js Preview Mode via the given preview API endpoint.
97
- await fetch(exitPreviewURL);
98
-
99
- // Reload the page with an active Preview Mode.
100
- window.location.reload();
157
+ await exitPreviewMode(resolvedExitPreviewURL);
101
158
  };
102
159
 
103
160
  // Register Prismic Toolbar event handlers.
104
- if (window) {
105
- window.addEventListener(
106
- "prismicPreviewUpdate",
107
- handlePrismicPreviewUpdate,
108
- );
109
- window.addEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
110
- }
161
+ window.addEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
162
+ window.addEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
111
163
 
112
164
  // On cleanup, unregister Prismic Toolbar event handlers.
113
165
  return () => {
114
- if (window) {
115
- window.removeEventListener(
116
- "prismicPreviewUpdate",
117
- handlePrismicPreviewUpdate,
118
- );
119
- window.removeEventListener(
120
- "prismicPreviewEnd",
121
- handlePrismicPreviewEnd,
122
- );
123
- }
166
+ window.removeEventListener(
167
+ "prismicPreviewUpdate",
168
+ handlePrismicPreviewUpdate,
169
+ );
170
+ window.removeEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
124
171
  };
125
- }, []);
172
+ }, [
173
+ repositoryName,
174
+ resolvedUpdatePreviewURL,
175
+ resolvedExitPreviewURL,
176
+ router.isPreview,
177
+ router.basePath,
178
+ ]);
126
179
 
127
180
  return (
128
181
  <>
129
- <PrismicToolbar repositoryName={repositoryName} />
130
182
  {children}
183
+ <PrismicToolbar repositoryName={repositoryName} />
131
184
  </>
132
185
  );
133
186
  }
@@ -60,19 +60,17 @@ export type EnableAutoPreviewsConfig<
60
60
  export const enableAutoPreviews = <TPreviewData extends PreviewData>(
61
61
  config: EnableAutoPreviewsConfig<TPreviewData>,
62
62
  ): void => {
63
- /**
64
- * If preview data is being passed from Next Context then use queryContentFromRef
65
- */
66
63
  if ("previewData" in config && config.previewData) {
64
+ // If preview data is being passed from Next Context then use queryContentFromRef
65
+
67
66
  const { previewData } = config;
68
67
 
69
68
  if (isPrismicNextPreviewData(previewData) && previewData.ref) {
70
69
  config.client.queryContentFromRef(previewData.ref);
71
70
  }
72
- /**
73
- * If the req object is passed then use enableAutoPreviewsFromReq
74
- */
75
71
  } else if ("req" in config && config.req) {
72
+ // If the req object is passed then use enableAutoPreviewsFromReq
73
+
76
74
  config.client.enableAutoPreviewsFromReq(config.req);
77
75
  }
78
76
  };
@@ -1,4 +1,4 @@
1
- import { NextApiResponse, NextApiRequest } from "next";
1
+ import type { NextApiRequest, NextApiResponse } from "next";
2
2
 
3
3
  /**
4
4
  * Configuration for `exitPreview`.
@@ -10,11 +10,17 @@ export type ExitPreviewConfig = {
10
10
  *
11
11
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
12
12
  */
13
- req: {
14
- headers: {
15
- referer?: NextApiRequest["headers"]["referer"];
16
- };
17
- };
13
+ // `req` is no longer used in `exitPreview()`. It previously would
14
+ // redirect the user to the referring URL, but it no longer has that
15
+ // behavior.
16
+ //
17
+ // `req` is retained as a parameter to make setting up an
18
+ // exit preview API route easier (this eliminates the awkward need to
19
+ // handle an unused `req` param).
20
+ //
21
+ // It is also retained in case it is needed in the future, such as
22
+ // reading headers or metadata about the request.
23
+ req: Pick<NextApiRequest, "headers">;
18
24
 
19
25
  /**
20
26
  * The `res` object from a Next.js API route. This is given as a parameter to
@@ -22,33 +28,15 @@ export type ExitPreviewConfig = {
22
28
  *
23
29
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
24
30
  */
25
- res: {
26
- clearPreviewData: NextApiResponse["clearPreviewData"];
27
- redirect: NextApiResponse["redirect"];
28
- };
31
+ res: Pick<NextApiResponse, "clearPreviewData" | "json">;
29
32
  };
30
33
 
31
34
  /**
32
35
  * Exits Next.js's Preview Mode from within a Next.js API route.
33
- *
34
- * If the user was sent to the endpoint from a page, the user will be redirected
35
- * back to that page after exiting Preview Mode.
36
36
  */
37
37
  export function exitPreview(config: ExitPreviewConfig): void {
38
- const { req } = config;
39
- // Exit the current user from "Preview Mode". This function accepts no args.
38
+ // Exit the current user from Preview Mode.
40
39
  config.res.clearPreviewData();
41
40
 
42
- if (req.headers.referer) {
43
- const url = new URL(req.headers.referer);
44
-
45
- if (url.pathname !== "/api/exit-preview") {
46
- // Redirect the user to the referrer page.
47
- config.res.redirect(req.headers.referer);
48
-
49
- return;
50
- }
51
- }
52
-
53
- config.res.redirect("/");
41
+ config.res.json({ success: true });
54
42
  }
package/src/index.ts CHANGED
@@ -1,10 +1,16 @@
1
- export type { SetPreviewDataConfig } from "./setPreviewData";
2
- export type { EnableAutoPreviewsConfig } from "./enableAutoPreviews";
3
- export type { ExitPreviewConfig as ExitPreviewParams } from "./exitPreview";
4
- export type { PrismicPreviewProps } from "./PrismicPreview";
5
1
  export { setPreviewData } from "./setPreviewData";
2
+ export type { SetPreviewDataConfig } from "./setPreviewData";
3
+
6
4
  export { exitPreview } from "./exitPreview";
5
+ export type { ExitPreviewConfig } from "./exitPreview";
6
+
7
7
  export { PrismicPreview } from "./PrismicPreview";
8
+ export type { PrismicPreviewProps } from "./PrismicPreview";
9
+
8
10
  export { enableAutoPreviews } from "./enableAutoPreviews";
11
+ export type { EnableAutoPreviewsConfig } from "./enableAutoPreviews";
12
+
9
13
  export { redirectToPreviewURL } from "./redirectToPreviewURL";
10
- export type { CreateClientConfig, PreviewConfig } from "./types";
14
+ export type { RedirectToPreviewURLConfig } from "./redirectToPreviewURL";
15
+
16
+ export type { CreateClientConfig } from "./types";
@@ -2,6 +2,8 @@
2
2
  * The following code is a modifed version of `es-cookie` taken from
3
3
  * https://github.com/theodorejb/es-cookie
4
4
  *
5
+ * It only contains a simplified version of `get()`.
6
+ *
5
7
  * Copyright 2017 Theodore Brown
6
8
  *
7
9
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -27,32 +29,23 @@ const readValue = (value: string): string => {
27
29
  return value.replace(/%3B/g, ";");
28
30
  };
29
31
 
30
- export const parse = (cookieString: string): { [name: string]: string } => {
31
- const result: { [name: string]: string } = {};
32
- const cookies = cookieString.split("; ");
33
-
34
- for (const cookie of cookies) {
35
- const parts = cookie.split("=");
36
- const value = parts.slice(1).join("=");
37
- const name = readValue(parts[0]).replace(/%3D/g, "=");
38
- result[name] = readValue(value);
39
- }
40
-
41
- return result;
42
- };
43
-
44
- const getAll = (cookieStore: string): { [name: string]: string } =>
45
- parse(cookieStore);
46
-
47
32
  /**
48
33
  * Returns the value of a cookie from a given cookie store.
49
34
  *
50
35
  * @param name - Of the cookie.
51
- * @param cookieStore - The stringified cookie store from which to read the cookie.
52
36
  *
53
37
  * @returns The value of the cookie, if it exists.
54
38
  */
55
- export const getCookie = (
56
- name: string,
57
- cookieStore: string,
58
- ): string | undefined => getAll(cookieStore)[name];
39
+ export const getCookie = (name: string): string | undefined => {
40
+ const cookies = document.cookie.split("; ");
41
+
42
+ for (const cookie of cookies) {
43
+ const parts = cookie.split("=");
44
+ const value = parts.slice(1).join("=");
45
+ const thisName = readValue(parts[0]).replace(/%3D/g, "=");
46
+
47
+ if (thisName === name) {
48
+ return readValue(value);
49
+ }
50
+ }
51
+ };
@@ -1,7 +1,6 @@
1
- import { NextApiRequest } from "next";
2
- import { LinkResolverFunction } from "@prismicio/helpers";
3
-
4
- import { PreviewConfig } from "./";
1
+ import type { Client } from "@prismicio/client";
2
+ import type { NextApiRequest, NextApiResponse } from "next";
3
+ import type { LinkResolverFunction } from "@prismicio/helpers";
5
4
 
6
5
  type PrismicNextQuery = {
7
6
  documentId: string;
@@ -18,8 +17,66 @@ type PrismicNextQuery = {
18
17
  */
19
18
  const isPrismicNextQuery = (
20
19
  query: NextApiRequest["query"],
21
- ): query is PrismicNextQuery =>
22
- typeof query.documentId === "string" && typeof query.token === "string";
20
+ ): query is PrismicNextQuery => {
21
+ return (
22
+ typeof query.documentId === "string" && typeof query.token === "string"
23
+ );
24
+ };
25
+
26
+ /**
27
+ * Preview config for enabling previews with redirectToPreviewURL
28
+ */
29
+ export type RedirectToPreviewURLConfig<
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ TLinkResolverFunction extends LinkResolverFunction<any> = LinkResolverFunction,
32
+ > = {
33
+ /**
34
+ * The `req` object from a Next.js API route. This is given as a parameter to
35
+ * the API route.
36
+ *
37
+ * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
38
+ */
39
+ req: Pick<NextApiRequest, "query">;
40
+
41
+ /**
42
+ * The `res` object from a Next.js API route. This is given as a parameter to
43
+ * the API route.
44
+ *
45
+ * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
46
+ */
47
+ res: Pick<NextApiResponse, "redirect">;
48
+
49
+ /**
50
+ * The Prismic client configured for the preview session's repository.
51
+ */
52
+ client: Client;
53
+
54
+ /**
55
+ * A Link Resolver used to resolve the previewed document's URL.
56
+ *
57
+ * @see To learn more about Link Resolver: {@link https://prismic.io/docs/core-concepts/link-resolver-route-resolver}
58
+ */
59
+ linkResolver?: TLinkResolverFunction;
60
+
61
+ /**
62
+ * The default redirect URL if a URL cannot be determined for the previewed document.
63
+ *
64
+ * **Note**: If your `next.config.js` file contains a `basePath`, you must
65
+ * include the `basePath` as part of this option. `redirectToPreviewURL()`
66
+ * cannot read your global `basePath`.
67
+ */
68
+ defaultURL?: string;
69
+
70
+ /**
71
+ * The `basePath` for the Next.js app as it is defined in `next.config.js`.
72
+ * This option can be omitted if the app does not have a `basePath`.
73
+ *
74
+ * @remarks
75
+ * The API route is unable to detect the app's `basePath` automatically. It
76
+ * must be provided to `redirectToPreviewURL()` manually.
77
+ */
78
+ basePath?: string;
79
+ };
23
80
 
24
81
  /**
25
82
  * Redirects a user to the URL of a previewed Prismic document from within a
@@ -28,26 +85,22 @@ const isPrismicNextQuery = (
28
85
  export async function redirectToPreviewURL<
29
86
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
87
  TLinkResolverFunction extends LinkResolverFunction<any>,
31
- >({
32
- req,
33
- res,
34
- client,
35
- linkResolver,
36
- defaultURL = "/",
37
- }: PreviewConfig<TLinkResolverFunction>): Promise<void> {
38
- if (isPrismicNextQuery(req.query)) {
39
- const { documentId, token } = req.query;
40
- const previewUrl = await client.resolvePreviewURL({
41
- linkResolver,
88
+ >(config: RedirectToPreviewURLConfig<TLinkResolverFunction>): Promise<void> {
89
+ const defaultURL = config.defaultURL || "/";
90
+ const basePath = config.basePath || "";
91
+
92
+ if (isPrismicNextQuery(config.req.query)) {
93
+ const previewUrl = await config.client.resolvePreviewURL({
94
+ linkResolver: config.linkResolver,
42
95
  defaultURL,
43
- documentID: documentId,
44
- previewToken: token,
96
+ documentID: config.req.query.documentId,
97
+ previewToken: config.req.query.token,
45
98
  });
46
99
 
47
- res.redirect(previewUrl);
100
+ config.res.redirect(basePath + previewUrl);
48
101
 
49
102
  return;
50
103
  }
51
104
 
52
- res.redirect(defaultURL);
105
+ config.res.redirect(basePath + defaultURL);
53
106
  }
@@ -11,10 +11,7 @@ export type SetPreviewDataConfig = {
11
11
  *
12
12
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
13
13
  */
14
- req: {
15
- query: NextApiRequest["query"];
16
- cookies: NextApiRequest["cookies"];
17
- };
14
+ req: Pick<NextApiRequest, "query" | "cookies">;
18
15
 
19
16
  /**
20
17
  * The `res` object from a Next.js API route. This is given as a parameter to
@@ -22,9 +19,7 @@ export type SetPreviewDataConfig = {
22
19
  *
23
20
  * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
24
21
  */
25
- res: {
26
- setPreviewData: NextApiResponse["setPreviewData"];
27
- };
22
+ res: Pick<NextApiResponse, "setPreviewData">;
28
23
  };
29
24
 
30
25
  /**
package/src/types.ts CHANGED
@@ -1,6 +1,4 @@
1
- import { PreviewData, NextApiRequest, NextApiResponse } from "next";
2
- import { LinkResolverFunction } from "@prismicio/helpers";
3
- import { Client } from "@prismicio/client";
1
+ import { PreviewData, NextApiRequest } from "next";
4
2
 
5
3
  /**
6
4
  * Configuration for creating a Prismic client with automatic preview support in
@@ -22,48 +20,3 @@ export type CreateClientConfig = {
22
20
  */
23
21
  req?: NextApiRequest;
24
22
  };
25
-
26
- /**
27
- * Preview config for enabling previews with redirectToPreviewURL
28
- */
29
- export type PreviewConfig<
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- TLinkResolverFunction extends LinkResolverFunction<any> = LinkResolverFunction,
32
- > = {
33
- /**
34
- * The `req` object from a Next.js API route. This is given as a parameter to
35
- * the API route.
36
- *
37
- * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
38
- */
39
- req: {
40
- query: NextApiRequest["query"];
41
- };
42
-
43
- /**
44
- * The `res` object from a Next.js API route. This is given as a parameter to
45
- * the API route.
46
- *
47
- * @see Next.js API route docs: {@link https://nextjs.org/docs/api-routes/introduction}
48
- */
49
- res: {
50
- redirect: NextApiResponse["redirect"];
51
- };
52
-
53
- /**
54
- * The Prismic client configured for the preview session's repository.
55
- */
56
- client: Client;
57
-
58
- /**
59
- * A Link Resolver used to resolve the previewed document's URL.
60
- *
61
- * @see To learn more about Link Resolver: {@link https://prismic.io/docs/core-concepts/link-resolver-route-resolver}
62
- */
63
- linkResolver?: TLinkResolverFunction;
64
-
65
- /**
66
- * The default redirect URL if a URL cannot be determined for the previewed document.
67
- */
68
- defaultURL?: string;
69
- };