@prismicio/next 1.2.0 → 1.3.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.
Files changed (47) hide show
  1. package/dist/PrismicPreview.cjs +3 -1
  2. package/dist/PrismicPreview.cjs.map +1 -1
  3. package/dist/PrismicPreview.js +3 -1
  4. package/dist/PrismicPreview.js.map +1 -1
  5. package/dist/PrismicPreviewClient.cjs +9 -13
  6. package/dist/PrismicPreviewClient.cjs.map +1 -1
  7. package/dist/PrismicPreviewClient.d.ts +4 -2
  8. package/dist/PrismicPreviewClient.js +9 -13
  9. package/dist/PrismicPreviewClient.js.map +1 -1
  10. package/dist/_node_modules/@prismicio/client/dist/helpers/asLink.cjs +12 -3
  11. package/dist/_node_modules/@prismicio/client/dist/helpers/asLink.cjs.map +1 -1
  12. package/dist/_node_modules/@prismicio/client/dist/helpers/asLink.js +12 -3
  13. package/dist/_node_modules/@prismicio/client/dist/helpers/asLink.js.map +1 -1
  14. package/dist/_node_modules/@prismicio/client/dist/helpers/isFilled.cjs.map +1 -1
  15. package/dist/_node_modules/@prismicio/client/dist/helpers/isFilled.js.map +1 -1
  16. package/dist/enableAutoPreviews.cjs +11 -2
  17. package/dist/enableAutoPreviews.cjs.map +1 -1
  18. package/dist/enableAutoPreviews.js +12 -3
  19. package/dist/enableAutoPreviews.js.map +1 -1
  20. package/dist/exitPreview.cjs +14 -2
  21. package/dist/exitPreview.cjs.map +1 -1
  22. package/dist/exitPreview.d.ts +36 -6
  23. package/dist/exitPreview.js +14 -2
  24. package/dist/exitPreview.js.map +1 -1
  25. package/dist/index.d.ts +1 -1
  26. package/dist/lib/getPreviewCookieRepositoryName.cjs +1 -1
  27. package/dist/lib/getPreviewCookieRepositoryName.cjs.map +1 -1
  28. package/dist/lib/getPreviewCookieRepositoryName.js +1 -1
  29. package/dist/lib/getPreviewCookieRepositoryName.js.map +1 -1
  30. package/dist/package.json.cjs +1 -1
  31. package/dist/package.json.js +1 -1
  32. package/dist/types.d.ts +7 -1
  33. package/package.json +12 -10
  34. package/src/PrismicPreview.tsx +8 -2
  35. package/src/PrismicPreviewClient.tsx +17 -20
  36. package/src/enableAutoPreviews.ts +24 -15
  37. package/src/exitPreview.ts +62 -13
  38. package/src/index.ts +1 -1
  39. package/src/lib/getPreviewCookieRepositoryName.ts +1 -1
  40. package/src/types.ts +8 -1
  41. package/bin/prismic-next.js +0 -3
  42. package/dist/cli/index.d.ts +0 -1
  43. package/dist/cli.cjs +0 -123
  44. package/dist/cli.cjs.map +0 -1
  45. package/dist/cli.js +0 -104
  46. package/dist/cli.js.map +0 -1
  47. package/src/cli/index.ts +0 -168
@@ -1,6 +1,18 @@
1
+ import { draftMode } from "next/headers";
1
2
  async function exitPreview(config) {
2
- config.res.clearPreviewData();
3
- config.res.status(205).json({ success: true });
3
+ if (config == null ? void 0 : config.res) {
4
+ config.res.clearPreviewData();
5
+ config.res.json({ success: true });
6
+ config.res.setHeader("Cache-Control", "no-store");
7
+ return;
8
+ } else {
9
+ draftMode().disable();
10
+ return new Response(JSON.stringify({ success: true }), {
11
+ headers: {
12
+ "Cache-Control": "no-store"
13
+ }
14
+ });
15
+ }
4
16
  }
5
17
  export {
6
18
  exitPreview
@@ -1 +1 @@
1
- {"version":3,"file":"exitPreview.js","sources":["../../src/exitPreview.ts"],"sourcesContent":["import { NextResponse } from \"next/server\";\n\nimport { NextApiRequestLike, NextApiResponseLike } from \"./types\";\n\n/**\n * Configuration for `exitPreview`.\n */\nexport type ExitPreviewConfig = {\n\t/**\n\t * The `req` object from a Next.js API route.\n\t *\n\t * @see Next.js API route docs: \\<https://nextjs.org/docs/api-routes/introduction\\>\n\t */\n\treq: NextApiRequestLike;\n\n\t/**\n\t * The `res` object from a Next.js API route.\n\t *\n\t * @see Next.js API route docs: \\<https://nextjs.org/docs/api-routes/introduction\\>\n\t */\n\tres: NextApiResponseLike;\n};\n\n/**\n * **Only use this function in the Pages Directory (/pages).**\n *\n * Exits Next.js's Preview Mode from within a Next.js API route.\n */\nexport async function exitPreview(\n\tconfig: ExitPreviewConfig,\n): Promise<NextResponse | void> {\n\t// Exit the current user from Preview Mode.\n\tconfig.res.clearPreviewData();\n\n\t// 205 status is used to prevent CDN-level caching. The default 200\n\t// status code is typically treated as non-changing and cacheable.\n\tconfig.res.status(205).json({ success: true });\n}\n"],"names":[],"mappings":"AA4BA,eAAsB,YACrB,QAAyB;AAGzB,SAAO,IAAI;AAIJ,SAAA,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,MAAM;AAC9C;"}
1
+ {"version":3,"file":"exitPreview.js","sources":["../../src/exitPreview.ts"],"sourcesContent":["import { draftMode } from \"next/headers\";\n\nimport { NextApiRequestLike, NextApiResponseLike } from \"./types\";\n\n/**\n * Configuration for `exitPreview`.\n */\nexport type ExitPreviewConfig = {\n\t/**\n\t * **Only use this parameter in the Pages Directory (/pages).**\n\t *\n\t * The `req` object from a Next.js API route.\n\t *\n\t * @see Next.js API route docs: \\<https://nextjs.org/docs/api-routes/introduction\\>\n\t */\n\treq?: NextApiRequestLike;\n\n\t/**\n\t * **Only use this parameter in the Pages Directory (/pages).**\n\t *\n\t * The `res` object from a Next.js API route.\n\t *\n\t * @see Next.js API route docs: \\<https://nextjs.org/docs/api-routes/introduction\\>\n\t */\n\tres?: NextApiResponseLike;\n};\n\n/**\n * Ends a Prismic preview session within a Next.js app. This function should be\n * used in a Router Handler or an API Route, depending on which you are using\n * the App Router or Pages Router.\n *\n * `exitPreview()` assumes Draft Mode is being used unless a Pages Router API\n * Route `res` object is provided to the function.\n *\n * @example Usage within an App Router Route Handler.\n *\n * ```typescript\n * // src/app/exit-preview/route.js\n *\n * import { exitPreview } from \"@prismicio/next\";\n *\n * export async function GET() {\n * \tawait exitPreview();\n * }\n * ```\n *\n * @example Usage within a Pages Router API Route.\n *\n * ```typescript\n * // src/pages/api/exit-preview.js\n *\n * import { exitPreview } from \"@prismicio/next\";\n *\n * export default async function handler(req, res) {\n * \tawait exitPreview({ req, res });\n * }\n * ```\n */\nexport async function exitPreview(\n\tconfig?: ExitPreviewConfig,\n): Promise<Response | void> {\n\tif (config?.res) {\n\t\t// Assume Preview Mode is being used.\n\n\t\tconfig.res.clearPreviewData();\n\n\t\t// 205 status is used to prevent CDN-level caching. The default 200\n\t\t// status code is typically treated as non-changing and cacheable.\n\t\tconfig.res.json({ success: true });\n\t\tconfig.res.setHeader(\"Cache-Control\", \"no-store\");\n\n\t\treturn;\n\t} else {\n\t\t// Assume Draft Mode is being used.\n\n\t\tdraftMode().disable();\n\n\t\t// 205 status is used to prevent CDN-level caching. The default 200\n\t\t// status code is typically treated as non-changing and cacheable.\n\t\treturn new Response(JSON.stringify({ success: true }), {\n\t\t\theaders: {\n\t\t\t\t\"Cache-Control\": \"no-store\",\n\t\t\t},\n\t\t});\n\t}\n}\n"],"names":[],"mappings":";AA2DA,eAAsB,YACrB,QAA0B;AAE1B,MAAI,iCAAQ,KAAK;AAGhB,WAAO,IAAI;AAIX,WAAO,IAAI,KAAK,EAAE,SAAS,KAAM,CAAA;AAC1B,WAAA,IAAI,UAAU,iBAAiB,UAAU;AAEhD;AAAA,EAAA,OACM;AAGN,cAAA,EAAY;AAIL,WAAA,IAAI,SAAS,KAAK,UAAU,EAAE,SAAS,KAAA,CAAM,GAAG;AAAA,MACtD,SAAS;AAAA,QACR,iBAAiB;AAAA,MACjB;AAAA,IAAA,CACD;AAAA,EACD;AACF;"}
package/dist/index.d.ts CHANGED
@@ -13,4 +13,4 @@ export type { RedirectToPreviewURLConfig } from "./redirectToPreviewURL";
13
13
  export { PrismicNextImage } from "./PrismicNextImage";
14
14
  export type { PrismicNextImageProps } from "./PrismicNextImage";
15
15
  export { imgixLoader } from "./imgixLoader";
16
- export type { CreateClientConfig } from "./types";
16
+ export type { CreateClientConfig, PrismicPreviewData } from "./types";
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const getPreviewCookieRepositoryName = (previewCookie) => {
4
- return (decodeURIComponent(previewCookie).match(/"(.+).prismic.io"/) || [])[1];
4
+ return (decodeURIComponent(previewCookie).match(/,"(.+).prismic.io"/) || [])[1];
5
5
  };
6
6
  exports.getPreviewCookieRepositoryName = getPreviewCookieRepositoryName;
7
7
  //# sourceMappingURL=getPreviewCookieRepositoryName.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"getPreviewCookieRepositoryName.cjs","sources":["../../../src/lib/getPreviewCookieRepositoryName.ts"],"sourcesContent":["/**\n * Extracts preview reference repo name from stringified Prismic preview cookie\n *\n * @param previewCookie - The Prismic preview cookie.\n *\n * @returns The repository name for the Prismic preview cookie. If the cookie\n * represents an inactive preview session, `undefined` will be returned.\n */\nexport const getPreviewCookieRepositoryName = (\n\tpreviewCookie: string,\n): string | undefined => {\n\treturn (decodeURIComponent(previewCookie).match(/\"(.+).prismic.io\"/) ||\n\t\t[])[1];\n};\n"],"names":[],"mappings":";;AAQa,MAAA,iCAAiC,CAC7C,kBACuB;AACf,UAAA,mBAAmB,aAAa,EAAE,MAAM,mBAAmB,KAClE,CAAA,GAAI,CAAC;AACP;;"}
1
+ {"version":3,"file":"getPreviewCookieRepositoryName.cjs","sources":["../../../src/lib/getPreviewCookieRepositoryName.ts"],"sourcesContent":["/**\n * Extracts preview reference repo name from stringified Prismic preview cookie\n *\n * @param previewCookie - The Prismic preview cookie.\n *\n * @returns The repository name for the Prismic preview cookie. If the cookie\n * represents an inactive preview session, `undefined` will be returned.\n */\nexport const getPreviewCookieRepositoryName = (\n\tpreviewCookie: string,\n): string | undefined => {\n\treturn (decodeURIComponent(previewCookie).match(/,\"(.+).prismic.io\"/) ||\n\t\t[])[1];\n};\n"],"names":[],"mappings":";;AAQa,MAAA,iCAAiC,CAC7C,kBACuB;AACf,UAAA,mBAAmB,aAAa,EAAE,MAAM,oBAAoB,KACnE,CAAA,GAAI,CAAC;AACP;;"}
@@ -1,5 +1,5 @@
1
1
  const getPreviewCookieRepositoryName = (previewCookie) => {
2
- return (decodeURIComponent(previewCookie).match(/"(.+).prismic.io"/) || [])[1];
2
+ return (decodeURIComponent(previewCookie).match(/,"(.+).prismic.io"/) || [])[1];
3
3
  };
4
4
  export {
5
5
  getPreviewCookieRepositoryName
@@ -1 +1 @@
1
- {"version":3,"file":"getPreviewCookieRepositoryName.js","sources":["../../../src/lib/getPreviewCookieRepositoryName.ts"],"sourcesContent":["/**\n * Extracts preview reference repo name from stringified Prismic preview cookie\n *\n * @param previewCookie - The Prismic preview cookie.\n *\n * @returns The repository name for the Prismic preview cookie. If the cookie\n * represents an inactive preview session, `undefined` will be returned.\n */\nexport const getPreviewCookieRepositoryName = (\n\tpreviewCookie: string,\n): string | undefined => {\n\treturn (decodeURIComponent(previewCookie).match(/\"(.+).prismic.io\"/) ||\n\t\t[])[1];\n};\n"],"names":[],"mappings":"AAQa,MAAA,iCAAiC,CAC7C,kBACuB;AACf,UAAA,mBAAmB,aAAa,EAAE,MAAM,mBAAmB,KAClE,CAAA,GAAI,CAAC;AACP;"}
1
+ {"version":3,"file":"getPreviewCookieRepositoryName.js","sources":["../../../src/lib/getPreviewCookieRepositoryName.ts"],"sourcesContent":["/**\n * Extracts preview reference repo name from stringified Prismic preview cookie\n *\n * @param previewCookie - The Prismic preview cookie.\n *\n * @returns The repository name for the Prismic preview cookie. If the cookie\n * represents an inactive preview session, `undefined` will be returned.\n */\nexport const getPreviewCookieRepositoryName = (\n\tpreviewCookie: string,\n): string | undefined => {\n\treturn (decodeURIComponent(previewCookie).match(/,\"(.+).prismic.io\"/) ||\n\t\t[])[1];\n};\n"],"names":[],"mappings":"AAQa,MAAA,iCAAiC,CAC7C,kBACuB;AACf,UAAA,mBAAmB,aAAa,EAAE,MAAM,oBAAoB,KACnE,CAAA,GAAI,CAAC;AACP;"}
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const version = "1.2.0";
3
+ const version = "1.3.0-alpha.0";
4
4
  exports.version = version;
5
5
  //# sourceMappingURL=package.json.cjs.map
@@ -1,4 +1,4 @@
1
- const version = "1.2.0";
1
+ const version = "1.3.0-alpha.0";
2
2
  export {
3
3
  version
4
4
  };
package/dist/types.d.ts CHANGED
@@ -5,6 +5,12 @@ declare module "@prismicio/client" {
5
5
  next?: RequestInit["next"];
6
6
  }
7
7
  }
8
+ /**
9
+ * Prismic data saved in Next.js Preview Mode's object.
10
+ */
11
+ export type PrismicPreviewData = {
12
+ ref: string;
13
+ };
8
14
  /**
9
15
  * Configuration for creating a Prismic client with automatic preview support in
10
16
  * Next.js apps.
@@ -58,7 +64,7 @@ export type NextApiRequestLike = {
58
64
  export type NextApiResponseLike = {
59
65
  redirect(url: string): NextApiResponseLike;
60
66
  clearPreviewData(): NextApiResponseLike;
61
- status(statusCode: number): NextApiResponseLike;
67
+ setHeader(name: string, value: string | string[]): NextApiResponseLike;
62
68
  json(body: any): void;
63
69
  setPreviewData(data: object | string): NextApiResponseLike;
64
70
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismicio/next",
3
- "version": "1.2.0",
3
+ "version": "1.3.0-alpha.0",
4
4
  "description": "Helpers to integrate Prismic into Next.js apps",
5
5
  "keywords": [
6
6
  "typescript",
@@ -18,8 +18,14 @@
18
18
  "sideEffects": false,
19
19
  "exports": {
20
20
  ".": {
21
- "import": "./dist/index.js",
22
- "require": "./dist/index.cjs"
21
+ "require": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.cjs"
24
+ },
25
+ "import": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ }
23
29
  },
24
30
  "./package.json": "./package.json"
25
31
  },
@@ -45,15 +51,11 @@
45
51
  "size": "size-limit",
46
52
  "test": "npm run lint && npm run types && npm run unit && npm run build && npm run size"
47
53
  },
48
- "bin": {
49
- "prismic-next": "./bin/prismic-next.js"
50
- },
51
54
  "dependencies": {
52
- "imgix-url-builder": "^0.0.3",
53
- "mri": "^1.2.0"
55
+ "imgix-url-builder": "^0.0.3"
54
56
  },
55
57
  "devDependencies": {
56
- "@prismicio/client": "^7.0.0-alpha.3",
58
+ "@prismicio/client": "^7.0.1",
57
59
  "@prismicio/mock": "^0.2.0",
58
60
  "@size-limit/preset-small-lib": "^8.2.4",
59
61
  "@types/react-test-renderer": "^18.0.0",
@@ -69,7 +71,7 @@
69
71
  "eslint-plugin-tsdoc": "^0.2.17",
70
72
  "happy-dom": "^9.9.2",
71
73
  "memfs": "^3.5.1",
72
- "next": "^13.4.0",
74
+ "next": "^13.4.5-canary.9",
73
75
  "node-fetch": "^3.3.1",
74
76
  "prettier": "^2.8.8",
75
77
  "prettier-plugin-jsdoc": "^0.4.2",
@@ -1,6 +1,7 @@
1
+ import Script from "next/script";
2
+ import { draftMode } from "next/headers";
1
3
  import * as React from "react";
2
4
  import * as prismic from "@prismicio/client";
3
- import Script from "next/script";
4
5
 
5
6
  import { PrismicPreviewClient } from "./PrismicPreviewClient";
6
7
 
@@ -51,11 +52,16 @@ export function PrismicPreview({
51
52
  ...props
52
53
  }: PrismicPreviewProps): JSX.Element {
53
54
  const toolbarSrc = prismic.getToolbarSrc(repositoryName);
55
+ const isDraftMode = draftMode().isEnabled;
54
56
 
55
57
  return (
56
58
  <>
57
59
  {children}
58
- <PrismicPreviewClient repositoryName={repositoryName} {...props} />
60
+ <PrismicPreviewClient
61
+ repositoryName={repositoryName}
62
+ isDraftMode={isDraftMode}
63
+ {...props}
64
+ />
59
65
  <Script src={toolbarSrc} strategy="lazyOnload" />
60
66
  </>
61
67
  );
@@ -9,15 +9,18 @@ import { getPreviewCookieRepositoryName } from "./lib/getPreviewCookieRepository
9
9
 
10
10
  import { PrismicPreviewProps } from "./PrismicPreview";
11
11
 
12
- type PrismicPreviewClientProps = Omit<PrismicPreviewProps, "children">;
12
+ type PrismicPreviewClientProps = Omit<PrismicPreviewProps, "children"> & {
13
+ isDraftMode: boolean;
14
+ };
13
15
 
14
16
  export function PrismicPreviewClient({
15
17
  repositoryName,
16
18
  updatePreviewURL = "/api/preview",
17
19
  exitPreviewURL = "/api/exit-preview",
20
+ isDraftMode,
18
21
  }: PrismicPreviewClientProps): null {
22
+ let isPreviewActive = isDraftMode;
19
23
  let isAppRouter = true;
20
- let isPreviewMode = false;
21
24
  let basePath = "";
22
25
  let refresh: () => void;
23
26
 
@@ -27,7 +30,7 @@ export function PrismicPreviewClient({
27
30
 
28
31
  isAppRouter = false;
29
32
  basePath = router.basePath;
30
- isPreviewMode = router.isPreview;
33
+ isPreviewActive ||= router.isPreview;
31
34
  refresh = () => router.replace(router.asPath, undefined, { scroll: false });
32
35
  } catch {
33
36
  // Assume we are in App Router. Ignore the error.
@@ -77,30 +80,24 @@ export function PrismicPreviewClient({
77
80
  // Prevent the toolbar from reloading the page.
78
81
  event.preventDefault();
79
82
 
80
- if (isAppRouter) {
83
+ const resolvedExitPreviewURL = basePath + exitPreviewURL;
84
+
85
+ // Exit Next.js Preview Mode via the given preview API endpoint.
86
+ const res = await globalThis.fetch(resolvedExitPreviewURL);
87
+
88
+ if (res.ok) {
81
89
  refresh();
82
90
  } else {
83
- const resolvedExitPreviewURL = basePath + exitPreviewURL;
84
-
85
- // Exit Next.js Preview Mode via the given preview API endpoint.
86
- const res = await globalThis.fetch(resolvedExitPreviewURL);
87
-
88
- if (res.ok) {
89
- // TODO: Can we do better than a full page reload?
90
- // globalThis.location.reload();
91
- refresh();
92
- } else {
93
- console.error(
94
- `[<PrismicPreview>] Failed to exit Preview Mode using the "${resolvedExitPreviewURL}" API endpoint. Does it exist?`,
95
- );
96
- }
91
+ console.error(
92
+ `[<PrismicPreview>] Failed to exit Preview Mode using the "${resolvedExitPreviewURL}" API endpoint. Does it exist?`,
93
+ );
97
94
  }
98
95
  };
99
96
 
100
97
  window.addEventListener("prismicPreviewUpdate", handlePrismicPreviewUpdate);
101
98
  window.addEventListener("prismicPreviewEnd", handlePrismicPreviewEnd);
102
99
 
103
- if (!isPreviewMode) {
100
+ if (!isPreviewActive) {
104
101
  const prismicPreviewCookie = getPrismicPreviewCookie(
105
102
  globalThis.document.cookie,
106
103
  );
@@ -149,7 +146,7 @@ export function PrismicPreviewClient({
149
146
  basePath,
150
147
  exitPreviewURL,
151
148
  isAppRouter,
152
- isPreviewMode,
149
+ isPreviewActive,
153
150
  refresh,
154
151
  repositoryName,
155
152
  updatePreviewURL,
@@ -1,8 +1,8 @@
1
1
  import { PreviewData } from "next";
2
- import { cookies } from "next/headers";
2
+ import { cookies, draftMode } from "next/headers";
3
3
  import * as prismic from "@prismicio/client";
4
4
 
5
- import { NextApiRequestLike } from "./types";
5
+ import { NextApiRequestLike, PrismicPreviewData } from "./types";
6
6
 
7
7
  /**
8
8
  * Configuration for `enableAutoPreviews`.
@@ -41,6 +41,10 @@ export type EnableAutoPreviewsConfig<
41
41
  req?: NextApiRequestLike;
42
42
  };
43
43
 
44
+ const isPrismicPreviewData = (input: unknown): input is PrismicPreviewData => {
45
+ return typeof input === "object" && input !== null && "ref" in input;
46
+ };
47
+
44
48
  /**
45
49
  * Configures a Prismic client to automatically query draft content during a
46
50
  * preview session. It either takes in a Next.js `getStaticProps` context object
@@ -56,11 +60,7 @@ export const enableAutoPreviews = <TPreviewData extends PreviewData>(
56
60
  // `getServerSideProps()` with active Preview Mode (`pages`
57
61
  // directory).
58
62
 
59
- if (
60
- typeof config.previewData === "object" &&
61
- "ref" in config.previewData &&
62
- typeof config.previewData.ref === "string"
63
- ) {
63
+ if (isPrismicPreviewData(config.previewData)) {
64
64
  config.client.queryContentFromRef(config.previewData.ref);
65
65
  }
66
66
  } else if ("req" in config && config.req) {
@@ -75,21 +75,30 @@ export const enableAutoPreviews = <TPreviewData extends PreviewData>(
75
75
  // We use a function value so the cookie is checked on every
76
76
  // request. We don't have a static value to read from.
77
77
  config.client.queryContentFromRef(() => {
78
+ let isDraftModeEnabled = false;
79
+ try {
80
+ isDraftModeEnabled = draftMode().isEnabled;
81
+ } catch {
82
+ // This catch block may be reached if
83
+ // `draftMode()` is called in a place that does
84
+ // not have access to its async storage. We can
85
+ // ignore this case.
86
+
87
+ return;
88
+ }
89
+
78
90
  let cookie: string | undefined;
79
91
  try {
80
92
  cookie = cookies().get(prismic.cookie.preview)?.value;
81
93
  } catch {
82
- // noop - We are probably in
83
- // `getStaticProps()` or `getServerSideProps()`
84
- // with inactive Preview Mode where `cookies()`
85
- // does not work. We don't need to do any
86
- // preview handling.
94
+ // We are probably in `getStaticProps()` or
95
+ // `getServerSideProps()` with inactive Preview
96
+ // Mode where `cookies()` does not work. We
97
+ // don't need to do any preview handling.
87
98
 
88
99
  return;
89
100
  }
90
101
 
91
- // We are probably in App Router (`app` directory).
92
-
93
102
  // We only return the cookie if a Prismic Preview session is active.
94
103
  //
95
104
  // An inactive cookie looks like this (URL encoded):
@@ -104,7 +113,7 @@ export const enableAutoPreviews = <TPreviewData extends PreviewData>(
104
113
  // preview: "https://example-prismic-repo.prismic.io/previews/abc:123?websitePreviewId=xyz"
105
114
  // }
106
115
  // }
107
- if (cookie && /\.prismic\.io/.test(cookie)) {
116
+ if (isDraftModeEnabled && cookie && /\.prismic\.io/.test(cookie)) {
108
117
  return cookie;
109
118
  }
110
119
  });
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ import { draftMode } from "next/headers";
2
2
 
3
3
  import { NextApiRequestLike, NextApiResponseLike } from "./types";
4
4
 
@@ -7,32 +7,81 @@ import { NextApiRequestLike, NextApiResponseLike } from "./types";
7
7
  */
8
8
  export type ExitPreviewConfig = {
9
9
  /**
10
+ * **Only use this parameter in the Pages Directory (/pages).**
11
+ *
10
12
  * The `req` object from a Next.js API route.
11
13
  *
12
14
  * @see Next.js API route docs: \<https://nextjs.org/docs/api-routes/introduction\>
13
15
  */
14
- req: NextApiRequestLike;
16
+ req?: NextApiRequestLike;
15
17
 
16
18
  /**
19
+ * **Only use this parameter in the Pages Directory (/pages).**
20
+ *
17
21
  * The `res` object from a Next.js API route.
18
22
  *
19
23
  * @see Next.js API route docs: \<https://nextjs.org/docs/api-routes/introduction\>
20
24
  */
21
- res: NextApiResponseLike;
25
+ res?: NextApiResponseLike;
22
26
  };
23
27
 
24
28
  /**
25
- * **Only use this function in the Pages Directory (/pages).**
29
+ * Ends a Prismic preview session within a Next.js app. This function should be
30
+ * used in a Router Handler or an API Route, depending on which you are using
31
+ * the App Router or Pages Router.
32
+ *
33
+ * `exitPreview()` assumes Draft Mode is being used unless a Pages Router API
34
+ * Route `res` object is provided to the function.
35
+ *
36
+ * @example Usage within an App Router Route Handler.
37
+ *
38
+ * ```typescript
39
+ * // src/app/exit-preview/route.js
40
+ *
41
+ * import { exitPreview } from "@prismicio/next";
42
+ *
43
+ * export async function GET() {
44
+ * await exitPreview();
45
+ * }
46
+ * ```
47
+ *
48
+ * @example Usage within a Pages Router API Route.
26
49
  *
27
- * Exits Next.js's Preview Mode from within a Next.js API route.
50
+ * ```typescript
51
+ * // src/pages/api/exit-preview.js
52
+ *
53
+ * import { exitPreview } from "@prismicio/next";
54
+ *
55
+ * export default async function handler(req, res) {
56
+ * await exitPreview({ req, res });
57
+ * }
58
+ * ```
28
59
  */
29
60
  export async function exitPreview(
30
- config: ExitPreviewConfig,
31
- ): Promise<NextResponse | void> {
32
- // Exit the current user from Preview Mode.
33
- config.res.clearPreviewData();
34
-
35
- // 205 status is used to prevent CDN-level caching. The default 200
36
- // status code is typically treated as non-changing and cacheable.
37
- config.res.status(205).json({ success: true });
61
+ config?: ExitPreviewConfig,
62
+ ): Promise<Response | void> {
63
+ if (config?.res) {
64
+ // Assume Preview Mode is being used.
65
+
66
+ config.res.clearPreviewData();
67
+
68
+ // 205 status is used to prevent CDN-level caching. The default 200
69
+ // status code is typically treated as non-changing and cacheable.
70
+ config.res.json({ success: true });
71
+ config.res.setHeader("Cache-Control", "no-store");
72
+
73
+ return;
74
+ } else {
75
+ // Assume Draft Mode is being used.
76
+
77
+ draftMode().disable();
78
+
79
+ // 205 status is used to prevent CDN-level caching. The default 200
80
+ // status code is typically treated as non-changing and cacheable.
81
+ return new Response(JSON.stringify({ success: true }), {
82
+ headers: {
83
+ "Cache-Control": "no-store",
84
+ },
85
+ });
86
+ }
38
87
  }
package/src/index.ts CHANGED
@@ -21,4 +21,4 @@ export type { PrismicNextImageProps } from "./PrismicNextImage";
21
21
 
22
22
  export { imgixLoader } from "./imgixLoader";
23
23
 
24
- export type { CreateClientConfig } from "./types";
24
+ export type { CreateClientConfig, PrismicPreviewData } from "./types";
@@ -9,6 +9,6 @@
9
9
  export const getPreviewCookieRepositoryName = (
10
10
  previewCookie: string,
11
11
  ): string | undefined => {
12
- return (decodeURIComponent(previewCookie).match(/"(.+).prismic.io"/) ||
12
+ return (decodeURIComponent(previewCookie).match(/,"(.+).prismic.io"/) ||
13
13
  [])[1];
14
14
  };
package/src/types.ts CHANGED
@@ -8,6 +8,13 @@ declare module "@prismicio/client" {
8
8
  }
9
9
  }
10
10
 
11
+ /**
12
+ * Prismic data saved in Next.js Preview Mode's object.
13
+ */
14
+ export type PrismicPreviewData = {
15
+ ref: string;
16
+ };
17
+
11
18
  /**
12
19
  * Configuration for creating a Prismic client with automatic preview support in
13
20
  * Next.js apps.
@@ -65,7 +72,7 @@ export type NextApiRequestLike = {
65
72
  export type NextApiResponseLike = {
66
73
  redirect(url: string): NextApiResponseLike;
67
74
  clearPreviewData(): NextApiResponseLike;
68
- status(statusCode: number): NextApiResponseLike;
75
+ setHeader(name: string, value: string | string[]): NextApiResponseLike;
69
76
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
77
  json(body: any): void;
71
78
  setPreviewData(data: object | string): NextApiResponseLike;
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import("../dist/cli.cjs").then((mod) => mod.run(process.argv));
@@ -1 +0,0 @@
1
- export declare function run(argv: string[]): Promise<void>;
package/dist/cli.cjs DELETED
@@ -1,123 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const mri = require("mri");
4
- const path = require("node:path");
5
- const fs = require("node:fs/promises");
6
- const tty = require("node:tty");
7
- const node_buffer = require("node:buffer");
8
- const _package = require("./package.json.cjs");
9
- function _interopNamespaceDefault(e) {
10
- const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
11
- if (e) {
12
- for (const k in e) {
13
- if (k !== "default") {
14
- const d = Object.getOwnPropertyDescriptor(e, k);
15
- Object.defineProperty(n, k, d.get ? d : {
16
- enumerable: true,
17
- get: () => e[k]
18
- });
19
- }
20
- }
21
- }
22
- n.default = e;
23
- return Object.freeze(n);
24
- }
25
- const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
26
- const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs);
27
- const tty__namespace = /* @__PURE__ */ _interopNamespaceDefault(tty);
28
- async function pathExists(filePath) {
29
- try {
30
- await fs__namespace.access(filePath);
31
- return true;
32
- } catch {
33
- return false;
34
- }
35
- }
36
- function color(colorCode, string) {
37
- return tty__namespace.WriteStream.prototype.hasColors() ? "\x1B[" + colorCode + "m" + string + "\x1B[39m" : string;
38
- }
39
- function warn(string) {
40
- return console.warn(`${color(33, "warn")} - ${string}`);
41
- }
42
- function info(string) {
43
- return console.info(`${color(35, "info")} - ${string}`);
44
- }
45
- async function run(argv) {
46
- const args = mri(argv.slice(2), {
47
- boolean: ["help", "version"],
48
- alias: {
49
- help: "h",
50
- version: "v"
51
- },
52
- default: {
53
- help: false,
54
- version: false
55
- }
56
- });
57
- const command = args._[0];
58
- switch (command) {
59
- case "clear-cache": {
60
- warn("`prismic-next clear-cache` is an experimental utility. It may be replaced with a different solution in the future.");
61
- async function getAppRootDir() {
62
- let currentDir = process.cwd();
63
- while (!await pathExists(path__namespace.join(currentDir, ".next")) && !await pathExists(path__namespace.join(currentDir, "package.json"))) {
64
- if (currentDir === path__namespace.resolve("/")) {
65
- break;
66
- }
67
- currentDir = path__namespace.join(currentDir, "..");
68
- }
69
- if (await pathExists(path__namespace.join(currentDir, ".next")) || await pathExists(path__namespace.join(currentDir, "package.json"))) {
70
- return currentDir;
71
- }
72
- }
73
- const appRootDir = await getAppRootDir();
74
- if (!appRootDir) {
75
- warn("Could not find the Next.js app root. Run `prismic-next clear-cache` in a Next.js project with a `.next` directory or `package.json` file.");
76
- return;
77
- }
78
- const fetchCacheDir = path__namespace.join(appRootDir, ".next", "cache", "fetch-cache");
79
- if (!await pathExists(fetchCacheDir)) {
80
- info("No Next.js fetch cache directory found. You are good to go!");
81
- return;
82
- }
83
- const cacheEntries = await fs__namespace.readdir(fetchCacheDir);
84
- await Promise.all(cacheEntries.map(async (entry) => {
85
- try {
86
- const contents = await fs__namespace.readFile(path__namespace.join(fetchCacheDir, entry), "utf8");
87
- const payload = JSON.parse(contents);
88
- if (payload.kind !== "FETCH") {
89
- return;
90
- }
91
- const bodyPayload = JSON.parse(node_buffer.Buffer.from(payload.data.body, "base64").toString());
92
- if (/\.prismic\.io\/auth$/.test(bodyPayload.oauth_initiate)) {
93
- await fs__namespace.unlink(path__namespace.join(fetchCacheDir, entry));
94
- info(`Prismic /api/v2 request cache cleared: ${entry}`);
95
- }
96
- } catch (e) {
97
- }
98
- }));
99
- info("The Prismic request cache has been cleared. Uncached requests will begin on the next Next.js server start-up.");
100
- return;
101
- }
102
- default: {
103
- if (command && (!args.version || !args.help)) {
104
- warn("Invalid command.\n");
105
- }
106
- if (args.version) {
107
- console.info(_package.version);
108
- return;
109
- }
110
- console.info(`
111
- Usage:
112
- prismic-next <command> [options...]
113
- Available commands:
114
- clear-cache
115
- Options:
116
- --help, -h Show help text
117
- --version, -v Show version
118
- `.trim());
119
- }
120
- }
121
- }
122
- exports.run = run;
123
- //# sourceMappingURL=cli.cjs.map
package/dist/cli.cjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.cjs","sources":["../../src/cli/index.ts"],"sourcesContent":["import mri from \"mri\";\nimport * as path from \"node:path\";\nimport * as fs from \"node:fs/promises\";\nimport * as tty from \"node:tty\";\nimport { Buffer } from \"node:buffer\";\n\nimport * as pkg from \"../../package.json\";\n\nasync function pathExists(filePath: string) {\n\ttry {\n\t\tawait fs.access(filePath);\n\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction color(colorCode: number, string: string) {\n\treturn tty.WriteStream.prototype.hasColors()\n\t\t? \"\\u001B[\" + colorCode + \"m\" + string + \"\\u001B[39m\"\n\t\t: string;\n}\n\nfunction warn(string: string) {\n\t// Yellow\n\treturn console.warn(`${color(33, \"warn\")} - ${string}`);\n}\n\nfunction info(string: string) {\n\t// Magenta\n\treturn console.info(`${color(35, \"info\")} - ${string}`);\n}\n\ntype Args = {\n\thelp: boolean;\n\tversion: boolean;\n};\n\nexport async function run(argv: string[]) {\n\tconst args = mri<Args>(argv.slice(2), {\n\t\tboolean: [\"help\", \"version\"],\n\t\talias: {\n\t\t\thelp: \"h\",\n\t\t\tversion: \"v\",\n\t\t},\n\t\tdefault: {\n\t\t\thelp: false,\n\t\t\tversion: false,\n\t\t},\n\t});\n\n\tconst command = args._[0];\n\n\tswitch (command) {\n\t\tcase \"clear-cache\": {\n\t\t\twarn(\n\t\t\t\t\"`prismic-next clear-cache` is an experimental utility. It may be replaced with a different solution in the future.\",\n\t\t\t);\n\n\t\t\tasync function getAppRootDir() {\n\t\t\t\tlet currentDir = process.cwd();\n\n\t\t\t\twhile (\n\t\t\t\t\t!(await pathExists(path.join(currentDir, \".next\"))) &&\n\t\t\t\t\t!(await pathExists(path.join(currentDir, \"package.json\")))\n\t\t\t\t) {\n\t\t\t\t\tif (currentDir === path.resolve(\"/\")) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrentDir = path.join(currentDir, \"..\");\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t(await pathExists(path.join(currentDir, \".next\"))) ||\n\t\t\t\t\t(await pathExists(path.join(currentDir, \"package.json\")))\n\t\t\t\t) {\n\t\t\t\t\treturn currentDir;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst appRootDir = await getAppRootDir();\n\n\t\t\tif (!appRootDir) {\n\t\t\t\twarn(\n\t\t\t\t\t\"Could not find the Next.js app root. Run `prismic-next clear-cache` in a Next.js project with a `.next` directory or `package.json` file.\",\n\t\t\t\t);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst fetchCacheDir = path.join(\n\t\t\t\tappRootDir,\n\t\t\t\t\".next\",\n\t\t\t\t\"cache\",\n\t\t\t\t\"fetch-cache\",\n\t\t\t);\n\n\t\t\tif (!(await pathExists(fetchCacheDir))) {\n\t\t\t\tinfo(\"No Next.js fetch cache directory found. You are good to go!\");\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst cacheEntries = await fs.readdir(fetchCacheDir);\n\n\t\t\tawait Promise.all(\n\t\t\t\tcacheEntries.map(async (entry) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst contents = await fs.readFile(\n\t\t\t\t\t\t\tpath.join(fetchCacheDir, entry),\n\t\t\t\t\t\t\t\"utf8\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst payload = JSON.parse(contents);\n\n\t\t\t\t\t\tif (payload.kind !== \"FETCH\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst bodyPayload = JSON.parse(\n\t\t\t\t\t\t\tBuffer.from(payload.data.body, \"base64\").toString(),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Delete `/api/v2` requests.\n\t\t\t\t\t\tif (/\\.prismic\\.io\\/auth$/.test(bodyPayload.oauth_initiate)) {\n\t\t\t\t\t\t\tawait fs.unlink(path.join(fetchCacheDir, entry));\n\n\t\t\t\t\t\t\tinfo(`Prismic /api/v2 request cache cleared: ${entry}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// noop\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tinfo(\n\t\t\t\t\"The Prismic request cache has been cleared. Uncached requests will begin on the next Next.js server start-up.\",\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\tdefault: {\n\t\t\tif (command && (!args.version || !args.help)) {\n\t\t\t\twarn(\"Invalid command.\\n\");\n\t\t\t}\n\n\t\t\tif (args.version) {\n\t\t\t\tconsole.info(pkg.version);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconsole.info(\n\t\t\t\t`\nUsage:\n prismic-next <command> [options...]\nAvailable commands:\n clear-cache\nOptions:\n --help, -h Show help text\n --version, -v Show version\n`.trim(),\n\t\t\t);\n\t\t}\n\t}\n}\n"],"names":["fs","tty","path","Buffer","pkg.version"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,eAAe,WAAW,UAAgB;AACrC,MAAA;AACG,UAAAA,cAAG,OAAO,QAAQ;AAEjB,WAAA;AAAA,EAAA,QACN;AACM,WAAA;AAAA,EACP;AACF;AAEA,SAAS,MAAM,WAAmB,QAAc;AACxC,SAAAC,eAAI,YAAY,UAAU,cAC9B,UAAY,YAAY,MAAM,SAAS,aACvC;AACJ;AAEA,SAAS,KAAK,QAAc;AAE3B,SAAO,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,QAAQ,QAAQ;AACxD;AAEA,SAAS,KAAK,QAAc;AAE3B,SAAO,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,QAAQ,QAAQ;AACxD;AAOA,eAAsB,IAAI,MAAc;AACvC,QAAM,OAAO,IAAU,KAAK,MAAM,CAAC,GAAG;AAAA,IACrC,SAAS,CAAC,QAAQ,SAAS;AAAA,IAC3B,OAAO;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IACT;AAAA,IACD,SAAS;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACT;AAAA,EAAA,CACD;AAEK,QAAA,UAAU,KAAK,EAAE,CAAC;AAExB,UAAQ,SAAS;AAAA,IAChB,KAAK,eAAe;AACnB,WACC,oHAAoH;AAGrH,qBAAe,gBAAa;AACvB,YAAA,aAAa,QAAQ;AAEzB,eACC,CAAE,MAAM,WAAWC,gBAAK,KAAK,YAAY,OAAO,CAAC,KACjD,CAAE,MAAM,WAAWA,gBAAK,KAAK,YAAY,cAAc,CAAC,GACvD;AACD,cAAI,eAAeA,gBAAK,QAAQ,GAAG,GAAG;AACrC;AAAA,UACA;AAEY,uBAAAA,gBAAK,KAAK,YAAY,IAAI;AAAA,QACvC;AAED,YACE,MAAM,WAAWA,gBAAK,KAAK,YAAY,OAAO,CAAC,KAC/C,MAAM,WAAWA,gBAAK,KAAK,YAAY,cAAc,CAAC,GACtD;AACM,iBAAA;AAAA,QACP;AAAA,MACF;AAEM,YAAA,aAAa,MAAM;AAEzB,UAAI,CAAC,YAAY;AAChB,aACC,2IAA2I;AAG5I;AAAA,MACA;AAED,YAAM,gBAAgBA,gBAAK,KAC1B,YACA,SACA,SACA,aAAa;AAGd,UAAI,CAAE,MAAM,WAAW,aAAa,GAAI;AACvC,aAAK,6DAA6D;AAElE;AAAA,MACA;AAED,YAAM,eAAe,MAAMF,cAAG,QAAQ,aAAa;AAEnD,YAAM,QAAQ,IACb,aAAa,IAAI,OAAO,UAAS;AAC5B,YAAA;AACG,gBAAA,WAAW,MAAMA,cAAG,SACzBE,gBAAK,KAAK,eAAe,KAAK,GAC9B,MAAM;AAED,gBAAA,UAAU,KAAK,MAAM,QAAQ;AAE/B,cAAA,QAAQ,SAAS,SAAS;AAC7B;AAAA,UACA;AAEK,gBAAA,cAAc,KAAK,MACxBC,YAAO,OAAA,KAAK,QAAQ,KAAK,MAAM,QAAQ,EAAE,SAAU,CAAA;AAIpD,cAAI,uBAAuB,KAAK,YAAY,cAAc,GAAG;AAC5D,kBAAMH,cAAG,OAAOE,gBAAK,KAAK,eAAe,KAAK,CAAC;AAE/C,iBAAK,0CAA0C,OAAO;AAAA,UACtD;AAAA,iBACO;QAER;AAAA,MACD,CAAA,CAAC;AAGH,WACC,+GAA+G;AAGhH;AAAA,IACA;AAAA,IAED,SAAS;AACR,UAAI,YAAY,CAAC,KAAK,WAAW,CAAC,KAAK,OAAO;AAC7C,aAAK,oBAAoB;AAAA,MACzB;AAED,UAAI,KAAK,SAAS;AACT,gBAAA,KAAKE,SAAAA,OAAW;AAExB;AAAA,MACA;AAED,cAAQ,KACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQF,KAAM,CAAA;AAAA,IAEL;AAAA,EACD;AACF;;"}