chore(live-preview): strongly types message events (#10148)
Live Preview message events were typed with the generic `MessageEvent` interface without passing any of the Live Preview specific properties, leading to unknown types upon use. To fix this, there is a new `LivePreviewMessageEvent` which properly extends the underlying `MessageEvent` interface, providing much needed type safety to these functions. In the same vein, the `UpdatedDocument` type was not being properly shared across packages, leading to multiple independent definitions of this type. This type is now exported from `payload` itself and renamed to `DocumentEvent` for improved semantics. Same with the `FieldSchemaJSON` type. This PR also adjusts where globally scoped variables are set, putting them within the shared `_payloadLivePreview` namespace instead of setting them individually at the top-level.
This commit is contained in:
@@ -1,33 +1,40 @@
|
|||||||
|
import type { LivePreviewMessageEvent } from './types.js'
|
||||||
|
|
||||||
import { isLivePreviewEvent } from './isLivePreviewEvent.js'
|
import { isLivePreviewEvent } from './isLivePreviewEvent.js'
|
||||||
import { mergeData } from './mergeData.js'
|
import { mergeData } from './mergeData.js'
|
||||||
|
|
||||||
// For performance reasons, `fieldSchemaJSON` will only be sent once on the initial message
|
const _payloadLivePreview = {
|
||||||
// We need to cache this value so that it can be used across subsequent messages
|
/**
|
||||||
// To do this, save `fieldSchemaJSON` when it arrives as a global variable
|
* For performance reasons, `fieldSchemaJSON` will only be sent once on the initial message
|
||||||
// Send this cached value to `mergeData`, instead of `eventData.fieldSchemaJSON` directly
|
* We need to cache this value so that it can be used across subsequent messages
|
||||||
let payloadLivePreviewFieldSchema = undefined // TODO: type this from `fieldSchemaToJSON` return type
|
* To do this, save `fieldSchemaJSON` when it arrives as a global variable
|
||||||
|
* Send this cached value to `mergeData`, instead of `eventData.fieldSchemaJSON` directly
|
||||||
// Each time the data is merged, cache the result as a `previousData` variable
|
*/
|
||||||
// This will ensure changes compound overtop of each other
|
fieldSchema: undefined,
|
||||||
let payloadLivePreviewPreviousData = undefined
|
/**
|
||||||
|
* Each time the data is merged, cache the result as a `previousData` variable
|
||||||
|
* This will ensure changes compound overtop of each other
|
||||||
|
*/
|
||||||
|
previousData: undefined,
|
||||||
|
}
|
||||||
|
|
||||||
export const handleMessage = async <T>(args: {
|
export const handleMessage = async <T>(args: {
|
||||||
apiRoute?: string
|
apiRoute?: string
|
||||||
depth?: number
|
depth?: number
|
||||||
event: MessageEvent
|
event: LivePreviewMessageEvent<T>
|
||||||
initialData: T
|
initialData: T
|
||||||
serverURL: string
|
serverURL: string
|
||||||
}): Promise<T> => {
|
}): Promise<T> => {
|
||||||
const { apiRoute, depth, event, initialData, serverURL } = args
|
const { apiRoute, depth, event, initialData, serverURL } = args
|
||||||
|
|
||||||
if (isLivePreviewEvent(event, serverURL)) {
|
if (isLivePreviewEvent(event, serverURL)) {
|
||||||
const { data, externallyUpdatedRelationship, fieldSchemaJSON } = event.data
|
const { data, externallyUpdatedRelationship, fieldSchemaJSON, locale } = event.data
|
||||||
|
|
||||||
if (!payloadLivePreviewFieldSchema && fieldSchemaJSON) {
|
if (!_payloadLivePreview?.fieldSchema && fieldSchemaJSON) {
|
||||||
payloadLivePreviewFieldSchema = fieldSchemaJSON
|
_payloadLivePreview.fieldSchema = fieldSchemaJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!payloadLivePreviewFieldSchema) {
|
if (!_payloadLivePreview?.fieldSchema) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn(
|
console.warn(
|
||||||
'Payload Live Preview: No `fieldSchemaJSON` was received from the parent window. Unable to merge data.',
|
'Payload Live Preview: No `fieldSchemaJSON` was received from the parent window. Unable to merge data.',
|
||||||
@@ -40,14 +47,14 @@ export const handleMessage = async <T>(args: {
|
|||||||
apiRoute,
|
apiRoute,
|
||||||
depth,
|
depth,
|
||||||
externallyUpdatedRelationship,
|
externallyUpdatedRelationship,
|
||||||
fieldSchema: payloadLivePreviewFieldSchema,
|
fieldSchema: _payloadLivePreview.fieldSchema,
|
||||||
incomingData: data,
|
incomingData: data,
|
||||||
initialData: payloadLivePreviewPreviousData || initialData,
|
initialData: _payloadLivePreview?.previousData || initialData,
|
||||||
locale: event.data.locale,
|
locale,
|
||||||
serverURL,
|
serverURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
payloadLivePreviewPreviousData = mergedData
|
_payloadLivePreview.previousData = mergedData
|
||||||
|
|
||||||
return mergedData
|
return mergedData
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ export { mergeData } from './mergeData.js'
|
|||||||
export { ready } from './ready.js'
|
export { ready } from './ready.js'
|
||||||
export { subscribe } from './subscribe.js'
|
export { subscribe } from './subscribe.js'
|
||||||
export { traverseRichText } from './traverseRichText.js'
|
export { traverseRichText } from './traverseRichText.js'
|
||||||
|
export type { LivePreviewMessageEvent } from './types.js'
|
||||||
export { unsubscribe } from './unsubscribe.js'
|
export { unsubscribe } from './unsubscribe.js'
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { PaginatedDocs } from 'payload'
|
import type { DocumentEvent, FieldSchemaJSON, PaginatedDocs } from 'payload'
|
||||||
import type { fieldSchemaToJSON } from 'payload/shared'
|
|
||||||
|
|
||||||
import type { PopulationsByCollection, UpdatedDocument } from './types.js'
|
import type { PopulationsByCollection } from './types.js'
|
||||||
|
|
||||||
import { traverseFields } from './traverseFields.js'
|
import { traverseFields } from './traverseFields.js'
|
||||||
|
|
||||||
@@ -32,8 +31,8 @@ export const mergeData = async <T>(args: {
|
|||||||
serverURL: string
|
serverURL: string
|
||||||
}) => Promise<Response>
|
}) => Promise<Response>
|
||||||
depth?: number
|
depth?: number
|
||||||
externallyUpdatedRelationship?: UpdatedDocument
|
externallyUpdatedRelationship?: DocumentEvent
|
||||||
fieldSchema: ReturnType<typeof fieldSchemaToJSON>
|
fieldSchema: FieldSchemaJSON
|
||||||
incomingData: Partial<T>
|
incomingData: Partial<T>
|
||||||
initialData: T
|
initialData: T
|
||||||
locale?: string
|
locale?: string
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
|
import type { DocumentEvent } from 'payload'
|
||||||
import type { fieldSchemaToJSON } from 'payload/shared'
|
import type { fieldSchemaToJSON } from 'payload/shared'
|
||||||
|
|
||||||
import type { PopulationsByCollection, UpdatedDocument } from './types.js'
|
import type { PopulationsByCollection } from './types.js'
|
||||||
|
|
||||||
import { traverseRichText } from './traverseRichText.js'
|
import { traverseRichText } from './traverseRichText.js'
|
||||||
|
|
||||||
export const traverseFields = <T>(args: {
|
export const traverseFields = <T>(args: {
|
||||||
externallyUpdatedRelationship?: UpdatedDocument
|
externallyUpdatedRelationship?: DocumentEvent
|
||||||
fieldSchema: ReturnType<typeof fieldSchemaToJSON>
|
fieldSchema: ReturnType<typeof fieldSchemaToJSON>
|
||||||
incomingData: T
|
incomingData: T
|
||||||
localeChanged: boolean
|
localeChanged: boolean
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import type { PopulationsByCollection, UpdatedDocument } from './types.js'
|
import type { DocumentEvent } from 'payload'
|
||||||
|
|
||||||
|
import type { PopulationsByCollection } from './types.js'
|
||||||
|
|
||||||
export const traverseRichText = ({
|
export const traverseRichText = ({
|
||||||
externallyUpdatedRelationship,
|
externallyUpdatedRelationship,
|
||||||
@@ -6,7 +8,7 @@ export const traverseRichText = ({
|
|||||||
populationsByCollection,
|
populationsByCollection,
|
||||||
result,
|
result,
|
||||||
}: {
|
}: {
|
||||||
externallyUpdatedRelationship?: UpdatedDocument
|
externallyUpdatedRelationship?: DocumentEvent
|
||||||
incomingData: any
|
incomingData: any
|
||||||
populationsByCollection: PopulationsByCollection
|
populationsByCollection: PopulationsByCollection
|
||||||
result: any
|
result: any
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { DocumentEvent, FieldSchemaJSON } from 'payload'
|
||||||
|
|
||||||
export type LivePreviewArgs = {}
|
export type LivePreviewArgs = {}
|
||||||
|
|
||||||
export type LivePreview = void
|
export type LivePreview = void
|
||||||
@@ -10,9 +12,10 @@ export type PopulationsByCollection = {
|
|||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: import this from `payload/admin/components/utilities/DocumentEvents/types.ts`
|
export type LivePreviewMessageEvent<T> = MessageEvent<{
|
||||||
export type UpdatedDocument = {
|
data: T
|
||||||
entitySlug: string
|
externallyUpdatedRelationship?: DocumentEvent
|
||||||
id?: number | string
|
fieldSchemaJSON: FieldSchemaJSON
|
||||||
updatedAt: string
|
locale?: string
|
||||||
}
|
type: 'payload-live-preview'
|
||||||
|
}>
|
||||||
|
|||||||
@@ -507,3 +507,9 @@ export type ClientFieldSchemaMap = Map<
|
|||||||
| ClientField
|
| ClientField
|
||||||
| ClientTab
|
| ClientTab
|
||||||
>
|
>
|
||||||
|
|
||||||
|
export type DocumentEvent = {
|
||||||
|
entitySlug: string
|
||||||
|
id?: number | string
|
||||||
|
updatedAt: string
|
||||||
|
}
|
||||||
|
|||||||
@@ -1338,6 +1338,7 @@ export {
|
|||||||
type CustomVersionParser,
|
type CustomVersionParser,
|
||||||
} from './utilities/dependencies/dependencyChecker.js'
|
} from './utilities/dependencies/dependencyChecker.js'
|
||||||
export { getDependencies } from './utilities/dependencies/getDependencies.js'
|
export { getDependencies } from './utilities/dependencies/getDependencies.js'
|
||||||
|
export type { FieldSchemaJSON } from './utilities/fieldSchemaToJSON.js'
|
||||||
export {
|
export {
|
||||||
findUp,
|
findUp,
|
||||||
findUpSync,
|
findUpSync,
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { createContext, useContext, useState } from 'react'
|
import type { DocumentEvent } from 'payload'
|
||||||
|
|
||||||
export type UpdatedDocument = {
|
import React, { createContext, useContext, useState } from 'react'
|
||||||
entitySlug: string
|
|
||||||
id?: number | string
|
|
||||||
updatedAt: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const Context = createContext({
|
const Context = createContext({
|
||||||
mostRecentUpdate: null,
|
mostRecentUpdate: null,
|
||||||
reportUpdate: (doc: UpdatedDocument) => null,
|
reportUpdate: (doc: DocumentEvent) => null,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const DocumentEventsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
export const DocumentEventsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
const [mostRecentUpdate, reportUpdate] = useState<UpdatedDocument>(null)
|
const [mostRecentUpdate, reportUpdate] = useState<DocumentEvent>(null)
|
||||||
|
|
||||||
return <Context.Provider value={{ mostRecentUpdate, reportUpdate }}>{children}</Context.Provider>
|
return <Context.Provider value={{ mostRecentUpdate, reportUpdate }}>{children}</Context.Provider>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import type { Payload } from 'payload'
|
import type { Payload } from 'payload'
|
||||||
|
|
||||||
import { handleMessage, mergeData, traverseRichText } from '@payloadcms/live-preview'
|
import {
|
||||||
|
handleMessage,
|
||||||
|
type LivePreviewMessageEvent,
|
||||||
|
mergeData,
|
||||||
|
traverseRichText,
|
||||||
|
} from '@payloadcms/live-preview'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { getFileByPath } from 'payload'
|
import { getFileByPath } from 'payload'
|
||||||
import { fieldSchemaToJSON } from 'payload/shared'
|
import { fieldSchemaToJSON } from 'payload/shared'
|
||||||
@@ -97,7 +102,7 @@ describe('Collections - Live Preview', () => {
|
|||||||
type: 'payload-live-preview',
|
type: 'payload-live-preview',
|
||||||
},
|
},
|
||||||
origin: serverURL,
|
origin: serverURL,
|
||||||
} as MessageEvent,
|
} as MessageEvent as LivePreviewMessageEvent<Page>,
|
||||||
initialData: {
|
initialData: {
|
||||||
title: 'Test Page',
|
title: 'Test Page',
|
||||||
} as Page,
|
} as Page,
|
||||||
@@ -118,7 +123,7 @@ describe('Collections - Live Preview', () => {
|
|||||||
type: 'payload-live-preview',
|
type: 'payload-live-preview',
|
||||||
},
|
},
|
||||||
origin: serverURL,
|
origin: serverURL,
|
||||||
} as MessageEvent,
|
} as MessageEvent as LivePreviewMessageEvent<Page>,
|
||||||
initialData: {
|
initialData: {
|
||||||
title: 'Test Page',
|
title: 'Test Page',
|
||||||
} as Page,
|
} as Page,
|
||||||
|
|||||||
@@ -135,9 +135,6 @@ export interface Page {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('primary' | 'secondary') | null;
|
appearance?: ('primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -172,9 +169,6 @@ export interface Page {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('default' | 'primary' | 'secondary') | null;
|
appearance?: ('default' | 'primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -208,18 +202,12 @@ export interface Page {
|
|||||||
value: string | Post;
|
value: string | Post;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
/**
|
|
||||||
* This field is auto-populated after-read
|
|
||||||
*/
|
|
||||||
populatedDocs?:
|
populatedDocs?:
|
||||||
| {
|
| {
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: string | Post;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
/**
|
|
||||||
* This field is auto-populated after-read
|
|
||||||
*/
|
|
||||||
populatedDocsTotal?: number | null;
|
populatedDocsTotal?: number | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
@@ -379,9 +367,6 @@ export interface Post {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('primary' | 'secondary') | null;
|
appearance?: ('primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -416,9 +401,6 @@ export interface Post {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('default' | 'primary' | 'secondary') | null;
|
appearance?: ('default' | 'primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -452,18 +434,12 @@ export interface Post {
|
|||||||
value: string | Post;
|
value: string | Post;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
/**
|
|
||||||
* This field is auto-populated after-read
|
|
||||||
*/
|
|
||||||
populatedDocs?:
|
populatedDocs?:
|
||||||
| {
|
| {
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: string | Post;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
/**
|
|
||||||
* This field is auto-populated after-read
|
|
||||||
*/
|
|
||||||
populatedDocsTotal?: number | null;
|
populatedDocsTotal?: number | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
@@ -534,9 +510,6 @@ export interface Ssr {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('primary' | 'secondary') | null;
|
appearance?: ('primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -571,9 +544,6 @@ export interface Ssr {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('default' | 'primary' | 'secondary') | null;
|
appearance?: ('default' | 'primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -607,18 +577,12 @@ export interface Ssr {
|
|||||||
value: string | Post;
|
value: string | Post;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
/**
|
|
||||||
* This field is auto-populated after-read
|
|
||||||
*/
|
|
||||||
populatedDocs?:
|
populatedDocs?:
|
||||||
| {
|
| {
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: string | Post;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
/**
|
|
||||||
* This field is auto-populated after-read
|
|
||||||
*/
|
|
||||||
populatedDocsTotal?: number | null;
|
populatedDocsTotal?: number | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
@@ -677,9 +641,6 @@ export interface SsrAutosave {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('primary' | 'secondary') | null;
|
appearance?: ('primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -714,9 +675,6 @@ export interface SsrAutosave {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('default' | 'primary' | 'secondary') | null;
|
appearance?: ('default' | 'primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -750,18 +708,12 @@ export interface SsrAutosave {
|
|||||||
value: string | Post;
|
value: string | Post;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
/**
|
|
||||||
* This field is auto-populated after-read
|
|
||||||
*/
|
|
||||||
populatedDocs?:
|
populatedDocs?:
|
||||||
| {
|
| {
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: string | Post;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
/**
|
|
||||||
* This field is auto-populated after-read
|
|
||||||
*/
|
|
||||||
populatedDocsTotal?: number | null;
|
populatedDocsTotal?: number | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
@@ -1393,9 +1345,6 @@ export interface Header {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('default' | 'primary' | 'secondary') | null;
|
appearance?: ('default' | 'primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
@@ -1426,9 +1375,6 @@ export interface Footer {
|
|||||||
} | null);
|
} | null);
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
label: string;
|
label: string;
|
||||||
/**
|
|
||||||
* Choose how the link should be rendered.
|
|
||||||
*/
|
|
||||||
appearance?: ('default' | 'primary' | 'secondary') | null;
|
appearance?: ('default' | 'primary' | 'secondary') | null;
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@payload-config": ["./test/database/config.ts"],
|
"@payload-config": ["./test/live-preview/config.ts"],
|
||||||
"@payloadcms/live-preview": ["./packages/live-preview/src"],
|
"@payloadcms/live-preview": ["./packages/live-preview/src"],
|
||||||
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
|
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
|
||||||
"@payloadcms/live-preview-vue": ["./packages/live-preview-vue/src/index.ts"],
|
"@payloadcms/live-preview-vue": ["./packages/live-preview-vue/src/index.ts"],
|
||||||
|
|||||||
Reference in New Issue
Block a user