@delmaredigital/payload-puck 0.6.30 → 0.8.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/README.md +52 -1
- package/dist/editor/PuckEditorImpl.client.js +36 -3
- package/dist/editor/plugins/VersionHistoryPanel.d.ts +6 -2
- package/dist/editor/plugins/VersionHistoryPanel.js +3 -2
- package/dist/editor/plugins/versionHistoryPlugin.d.ts +4 -3
- package/dist/editor/utils/index.d.ts +1 -0
- package/dist/editor/utils/index.js +1 -0
- package/dist/editor/utils/isDataEqual.d.ts +15 -0
- package/dist/editor/utils/isDataEqual.js +57 -0
- package/dist/plugin/index.d.ts +0 -1
- package/dist/plugin/index.js +5 -23
- package/dist/types/index.d.ts +29 -27
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +19 -28
- package/dist/endpoints/postcss.d.js +0 -4
- package/dist/endpoints/styles.d.ts +0 -19
- package/dist/endpoints/styles.js +0 -153
- package/dist/next/index.d.ts +0 -64
- package/dist/next/index.js +0 -155
package/README.md
CHANGED
|
@@ -32,7 +32,8 @@ pnpm add @delmaredigital/payload-puck @puckeditor/core
|
|
|
32
32
|
|
|
33
33
|
| Dependency | Version |
|
|
34
34
|
|------------|---------|
|
|
35
|
-
|
|
|
35
|
+
| `node` | >= 20.9.0 |
|
|
36
|
+
| `@puckeditor/core` | >= 0.23.0 |
|
|
36
37
|
| `payload` | >= 3.69.0 |
|
|
37
38
|
| `@payloadcms/next` | >= 3.69.0 |
|
|
38
39
|
| `next` | >= 15.4.8 (see security note below) |
|
|
@@ -42,6 +43,56 @@ pnpm add @delmaredigital/payload-puck @puckeditor/core
|
|
|
42
43
|
|
|
43
44
|
> **Security:** If your app uses Next.js middleware (or proxy.ts) to protect dynamic routes, use `next` >= 15.5.16 / 16.2.5 to pick up the fix for [CVE-2026-44574](https://github.com/vercel/next.js/security/advisories/GHSA-492v-c6pp-mqqv) (middleware bypass via dynamic route parameter injection). Turbopack users need >= 15.5.18 / 16.2.6.
|
|
44
45
|
|
|
46
|
+
### Upgrading to 0.8.0 (breaking)
|
|
47
|
+
|
|
48
|
+
**Editor CSS is now built by your app, not by this plugin.** Three options collapse into one, and `withPuckCSS` is gone.
|
|
49
|
+
|
|
50
|
+
Add a build step using Tailwind's own CLI:
|
|
51
|
+
|
|
52
|
+
```jsonc
|
|
53
|
+
// package.json
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build:puck-css": "tailwindcss -i ./src/app/(frontend)/globals.css -o ./public/puck-editor-styles.css",
|
|
56
|
+
"build": "pnpm build:puck-css && next build",
|
|
57
|
+
"dev": "pnpm build:puck-css --watch & next dev"
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Then pass the URL:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// before
|
|
65
|
+
createPuckPlugin({
|
|
66
|
+
editorStylesheet: 'src/app/(frontend)/globals.css',
|
|
67
|
+
editorStylesheetCompiled: '/puck-editor-styles.css',
|
|
68
|
+
editorStylesheetUrls: ['https://fonts.googleapis.com/css2?family=Inter'],
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// after
|
|
72
|
+
createPuckPlugin({
|
|
73
|
+
editorStylesheets: ['/puck-editor-styles.css', 'https://fonts.googleapis.com/css2?family=Inter'],
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Finally, remove the `withPuckCSS` import and wrapper from `next.config.js`, and drop any `editorStylesheets` prop on `PuckConfigProvider` — the plugin wires it through automatically now.
|
|
78
|
+
|
|
79
|
+
> **Why:** the old approach compiled CSS at runtime in dev and via a **webpack plugin** in production. Next.js 16 defaults to Turbopack, which never runs `webpack()` hooks — so the production stylesheet was silently never generated and the editor rendered unstyled, while local dev looked perfect. One artifact, built by your own toolchain, now resolves identically everywhere.
|
|
80
|
+
|
|
81
|
+
Also removed: the `/api/puck/styles` endpoint, the `/next` entry point, and the `postcss` / `postcss-load-config` peer dependencies.
|
|
82
|
+
|
|
83
|
+
### Upgrading to 0.7.0 (breaking)
|
|
84
|
+
|
|
85
|
+
`0.7.0` raises two floors. Both are a one-line change for most projects:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pnpm add @puckeditor/core@^0.23.0 # peer floor moved from >=0.21.0
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- **`@puckeditor/core` now requires >= 0.23.0.** The Puck plugins this package bundles are versioned in lockstep with Puck core and import it from the host, so running them against an older core is not a supported combination. Puck 0.23 ships a rewritten canvas drag-and-drop engine and a redesigned outline — **the editing experience changes visibly**, even though no API you call has changed. Worth a pass through your editor after upgrading. See the [Puck 0.23 release notes](https://puckeditor.com/blog/puck-023).
|
|
92
|
+
- **Node 18 is no longer supported;** the floor is now `>=20.9.0`. Node 18 is end-of-life and Puck core 0.23 itself requires `>=20.0.0`.
|
|
93
|
+
|
|
94
|
+
No exports, props, or configuration options were removed or renamed. Full detail in the [changelog](./CHANGELOG.md).
|
|
95
|
+
|
|
45
96
|
---
|
|
46
97
|
|
|
47
98
|
## Quick Start
|
|
@@ -16,6 +16,7 @@ import { IframeWrapper } from './components/IframeWrapper.js';
|
|
|
16
16
|
import { PreviewModal } from './components/PreviewModal.js';
|
|
17
17
|
import { DarkModeStyles } from './components/DarkModeStyles.js';
|
|
18
18
|
import { useUnsavedChanges } from './hooks/useUnsavedChanges.js';
|
|
19
|
+
import { isDataEqual } from './utils/isDataEqual.js';
|
|
19
20
|
import { createVersionHistoryPlugin } from './plugins/versionHistoryPlugin.js';
|
|
20
21
|
import { ThemeProvider } from '../theme/index.js';
|
|
21
22
|
import { usePuckConfig } from '../views/PuckConfigContext.js';
|
|
@@ -117,6 +118,11 @@ import { useAiPrompts } from '../ai/hooks/useAiPrompts.js';
|
|
|
117
118
|
]);
|
|
118
119
|
// Use a ref to track latest data without causing re-renders
|
|
119
120
|
const latestDataRef = useRef(dataWithSlug);
|
|
121
|
+
// Track the last loaded/saved data so we can distinguish real user edits from
|
|
122
|
+
// no-op onChange dispatches (e.g. Puck's mount-time resolve pass). Refreshed at
|
|
123
|
+
// every markClean() site (save / publish) so subsequent edits diff against the
|
|
124
|
+
// most recently persisted state.
|
|
125
|
+
const savedDataRef = useRef(dataWithSlug);
|
|
120
126
|
// Get editor stylesheets from PuckConfigProvider context (as fallback)
|
|
121
127
|
const { editorStylesheets: contextStylesheets, editorCss: contextCss } = usePuckConfig();
|
|
122
128
|
// Props take precedence over context
|
|
@@ -239,6 +245,7 @@ import { useAiPrompts } from '../ai/hooks/useAiPrompts.js';
|
|
|
239
245
|
setSaveError(null); // Clear any previous error
|
|
240
246
|
// After saving as draft, update status to draft (shows "Unpublished Changes" if was published)
|
|
241
247
|
setDocumentStatus('draft');
|
|
248
|
+
savedDataRef.current = typedData;
|
|
242
249
|
markClean();
|
|
243
250
|
onSaveSuccess?.(data);
|
|
244
251
|
} catch (error) {
|
|
@@ -287,6 +294,7 @@ import { useAiPrompts } from '../ai/hooks/useAiPrompts.js';
|
|
|
287
294
|
setSaveError(null); // Clear any previous error
|
|
288
295
|
setDocumentStatus('published'); // Update status after successful publish
|
|
289
296
|
setWasPublished(true); // Mark as having been published
|
|
297
|
+
savedDataRef.current = typedData;
|
|
290
298
|
markClean();
|
|
291
299
|
onSaveSuccess?.(data);
|
|
292
300
|
} catch (error) {
|
|
@@ -340,9 +348,19 @@ import { useAiPrompts } from '../ai/hooks/useAiPrompts.js';
|
|
|
340
348
|
// Handle data change
|
|
341
349
|
const handleChange = useCallback((data)=>{
|
|
342
350
|
latestDataRef.current = data;
|
|
343
|
-
|
|
351
|
+
// Only mark dirty when the data actually differs from the last loaded/saved
|
|
352
|
+
// state. Puck fires onChange for no-op mount-time resolves (which can differ
|
|
353
|
+
// from the loaded data only by undefined-valued keys); treating those as
|
|
354
|
+
// edits produces a spurious "Unsaved" flag on load. isDataEqual is
|
|
355
|
+
// undefined-tolerant so those resolves compare equal and clear the flag.
|
|
356
|
+
if (isDataEqual(data, savedDataRef.current)) {
|
|
357
|
+
markClean();
|
|
358
|
+
} else {
|
|
359
|
+
markDirty();
|
|
360
|
+
}
|
|
344
361
|
onChangeProp?.(data);
|
|
345
362
|
}, [
|
|
363
|
+
markClean,
|
|
346
364
|
markDirty,
|
|
347
365
|
onChangeProp
|
|
348
366
|
]);
|
|
@@ -415,6 +433,7 @@ import { useAiPrompts } from '../ai/hooks/useAiPrompts.js';
|
|
|
415
433
|
setLastSaved(new Date());
|
|
416
434
|
setSaveError(null);
|
|
417
435
|
setDocumentStatus('draft');
|
|
436
|
+
savedDataRef.current = data;
|
|
418
437
|
markClean();
|
|
419
438
|
onSaveSuccess?.(data);
|
|
420
439
|
} catch (error) {
|
|
@@ -505,18 +524,32 @@ import { useAiPrompts } from '../ai/hooks/useAiPrompts.js';
|
|
|
505
524
|
const defaultPlugins = [
|
|
506
525
|
headingAnalyzer
|
|
507
526
|
];
|
|
527
|
+
// Restoring a version has already persisted that data server-side, so it
|
|
528
|
+
// becomes the new saved baseline. Without this, the `setData` dispatch the
|
|
529
|
+
// restore performs fires an onChange that diffs against the pre-restore
|
|
530
|
+
// baseline and immediately re-flags the (already saved) document as dirty.
|
|
531
|
+
const handleRestoreSuccess = useCallback((restoredData)=>{
|
|
532
|
+
if (restoredData) {
|
|
533
|
+
const typedData = restoredData;
|
|
534
|
+
savedDataRef.current = typedData;
|
|
535
|
+
latestDataRef.current = typedData;
|
|
536
|
+
}
|
|
537
|
+
markClean();
|
|
538
|
+
}, [
|
|
539
|
+
markClean
|
|
540
|
+
]);
|
|
508
541
|
// Version history plugin for the plugin rail
|
|
509
542
|
const versionHistoryPlugin = useMemo(()=>{
|
|
510
543
|
if (!pageId) return null;
|
|
511
544
|
return createVersionHistoryPlugin({
|
|
512
545
|
pageId,
|
|
513
546
|
apiEndpoint,
|
|
514
|
-
onRestoreSuccess:
|
|
547
|
+
onRestoreSuccess: handleRestoreSuccess
|
|
515
548
|
});
|
|
516
549
|
}, [
|
|
517
550
|
pageId,
|
|
518
551
|
apiEndpoint,
|
|
519
|
-
|
|
552
|
+
handleRestoreSuccess
|
|
520
553
|
]);
|
|
521
554
|
// Fetch AI prompts client-side when prompts collection is enabled
|
|
522
555
|
// This allows prompts to update in real-time when edited via the prompt editor panel
|
|
@@ -29,9 +29,13 @@ export interface VersionHistoryPanelProps {
|
|
|
29
29
|
*/
|
|
30
30
|
apiEndpoint?: string;
|
|
31
31
|
/**
|
|
32
|
-
* Callback after successful restore (e.g., to mark editor as clean)
|
|
32
|
+
* Callback after successful restore (e.g., to mark editor as clean).
|
|
33
|
+
*
|
|
34
|
+
* Receives the restored data that was dispatched into the editor, so the
|
|
35
|
+
* caller can treat it as the new "last saved" baseline — the restore endpoint
|
|
36
|
+
* has already persisted it server-side.
|
|
33
37
|
*/
|
|
34
|
-
onRestoreSuccess?: () => void;
|
|
38
|
+
onRestoreSuccess?: (restoredData?: Data) => void;
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
37
41
|
* Version history panel for the Puck plugin rail
|
|
@@ -256,8 +256,9 @@ const styles = {
|
|
|
256
256
|
// Show success message
|
|
257
257
|
setSuccessMessage(`Restored version from ${formatDate(version.updatedAt)}`);
|
|
258
258
|
setTimeout(()=>setSuccessMessage(null), 3000);
|
|
259
|
-
// Notify parent to mark as clean
|
|
260
|
-
|
|
259
|
+
// Notify parent to mark as clean, handing over the restored data so it
|
|
260
|
+
// becomes the new saved baseline (the restore already persisted it).
|
|
261
|
+
onRestoreSuccess?.(restoredDoc?.puckData);
|
|
261
262
|
// Refresh version list
|
|
262
263
|
fetchVersions();
|
|
263
264
|
} catch (err) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Plugin } from '@puckeditor/core';
|
|
1
|
+
import type { Data, Plugin } from '@puckeditor/core';
|
|
2
2
|
export interface VersionHistoryPluginOptions {
|
|
3
3
|
/**
|
|
4
4
|
* Page ID to fetch versions for
|
|
@@ -10,9 +10,10 @@ export interface VersionHistoryPluginOptions {
|
|
|
10
10
|
*/
|
|
11
11
|
apiEndpoint?: string;
|
|
12
12
|
/**
|
|
13
|
-
* Callback after successful restore (e.g., to mark editor as clean)
|
|
13
|
+
* Callback after successful restore (e.g., to mark editor as clean).
|
|
14
|
+
* Receives the restored data that was dispatched into the editor.
|
|
14
15
|
*/
|
|
15
|
-
onRestoreSuccess?: () => void;
|
|
16
|
+
onRestoreSuccess?: (restoredData?: Data) => void;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
19
|
* Creates a Puck plugin for version history
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Undefined-tolerant deep equality for Puck data.
|
|
3
|
+
*
|
|
4
|
+
* Puck's mount-time resolve pass (`resolveAndCommitData`) can dispatch a `replace`
|
|
5
|
+
* even when the net content is unchanged — e.g. a resolved node differs from the
|
|
6
|
+
* loaded node only by an `undefined`-valued key. Strict deep-equal libraries such
|
|
7
|
+
* as `fast-equals` treat `{}` and `{ k: undefined }` as different, so that no-op
|
|
8
|
+
* resolve looks like a real edit and spuriously marks the document dirty.
|
|
9
|
+
*
|
|
10
|
+
* This comparison treats an absent key and an `undefined`-valued key as equal, so
|
|
11
|
+
* a resolve that only adds/removes `undefined` values compares equal to the loaded
|
|
12
|
+
* data. Object key order is irrelevant (as with any deep equal); array order is
|
|
13
|
+
* significant (Puck content order is meaningful).
|
|
14
|
+
*/
|
|
15
|
+
export declare function isDataEqual(a: unknown, b: unknown): boolean;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Undefined-tolerant deep equality for Puck data.
|
|
3
|
+
*
|
|
4
|
+
* Puck's mount-time resolve pass (`resolveAndCommitData`) can dispatch a `replace`
|
|
5
|
+
* even when the net content is unchanged — e.g. a resolved node differs from the
|
|
6
|
+
* loaded node only by an `undefined`-valued key. Strict deep-equal libraries such
|
|
7
|
+
* as `fast-equals` treat `{}` and `{ k: undefined }` as different, so that no-op
|
|
8
|
+
* resolve looks like a real edit and spuriously marks the document dirty.
|
|
9
|
+
*
|
|
10
|
+
* This comparison treats an absent key and an `undefined`-valued key as equal, so
|
|
11
|
+
* a resolve that only adds/removes `undefined` values compares equal to the loaded
|
|
12
|
+
* data. Object key order is irrelevant (as with any deep equal); array order is
|
|
13
|
+
* significant (Puck content order is meaningful).
|
|
14
|
+
*/ export function isDataEqual(a, b) {
|
|
15
|
+
if (Object.is(a, b)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
// NaN is handled by Object.is above; remaining primitives that differ are unequal.
|
|
19
|
+
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const aIsArray = Array.isArray(a);
|
|
23
|
+
const bIsArray = Array.isArray(b);
|
|
24
|
+
if (aIsArray !== bIsArray) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (aIsArray && bIsArray) {
|
|
28
|
+
if (a.length !== b.length) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
for(let i = 0; i < a.length; i++){
|
|
32
|
+
if (!isDataEqual(a[i], b[i])) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const aObj = a;
|
|
39
|
+
const bObj = b;
|
|
40
|
+
// Compare the union of keys, treating a missing key and an `undefined` value as
|
|
41
|
+
// equivalent so that `{}` and `{ k: undefined }` are considered equal.
|
|
42
|
+
const keys = new Set([
|
|
43
|
+
...Object.keys(aObj),
|
|
44
|
+
...Object.keys(bObj)
|
|
45
|
+
]);
|
|
46
|
+
for (const key of keys){
|
|
47
|
+
const aVal = aObj[key];
|
|
48
|
+
const bVal = bObj[key];
|
|
49
|
+
if (aVal === undefined && bVal === undefined) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!isDataEqual(aVal, bVal)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -42,7 +42,6 @@ export { generatePagesCollection } from './collections/Pages.js';
|
|
|
42
42
|
export { TemplatesCollection } from '../collections/Templates.js';
|
|
43
43
|
export { getPuckFields, getPuckCollectionConfig, puckDataField, editorVersionField, createEditorVersionField, pageLayoutField, createPageLayoutField, isHomepageField, seoFieldGroup, conversionFieldGroup, } from './fields/index.js';
|
|
44
44
|
export { generatePuckEditField };
|
|
45
|
-
export { PUCK_STYLES_ENDPOINT } from '../endpoints/styles.js';
|
|
46
45
|
export { createIsHomepageUniqueHook, unsetHomepage, HomepageConflictError, } from './hooks/index.js';
|
|
47
46
|
export type { IsHomepageUniqueHookOptions } from './hooks/index.js';
|
|
48
47
|
export type { PuckPluginOptions, PuckAdminConfig } from '../types/index.js';
|
package/dist/plugin/index.js
CHANGED
|
@@ -5,7 +5,6 @@ import { AiContextCollection } from '../ai/collections/AiContext.js';
|
|
|
5
5
|
import { getPuckFields } from './fields/index.js';
|
|
6
6
|
import { createIsHomepageUniqueHook } from './hooks/isHomepageUnique.js';
|
|
7
7
|
import { createListHandler, createCreateHandler, createGetHandler, createUpdateHandler, createDeleteHandler, createVersionsHandler, createRestoreHandler } from '../endpoints/index.js';
|
|
8
|
-
import { createStylesHandler, PUCK_STYLES_ENDPOINT } from '../endpoints/styles.js';
|
|
9
8
|
import { createAiEndpointHandler } from '../endpoints/ai.js';
|
|
10
9
|
import { createPromptsListHandler, createPromptsCreateHandler, createPromptsUpdateHandler, createPromptsDeleteHandler } from '../endpoints/prompts.js';
|
|
11
10
|
import { createContextListHandler, createContextCreateHandler, createContextUpdateHandler, createContextDeleteHandler } from '../endpoints/context.js';
|
|
@@ -105,7 +104,7 @@ import { createContextListHandler, createContextCreateHandler, createContextUpda
|
|
|
105
104
|
* })
|
|
106
105
|
* ```
|
|
107
106
|
*/ export function createPuckPlugin(options = {}) {
|
|
108
|
-
const { pagesCollection = 'pages', autoGenerateCollection = true, admin: pluginAdminConfig = {}, enableAdminView = true, adminViewPath = '/puck-editor', enableEndpoints = true, pageTreeIntegration,
|
|
107
|
+
const { pagesCollection = 'pages', autoGenerateCollection = true, admin: pluginAdminConfig = {}, enableAdminView = true, adminViewPath = '/puck-editor', enableEndpoints = true, pageTreeIntegration, editorStylesheets: editorStylesheetsOption = [], ai: aiConfig, previewUrl, rootPropsMapping } = options;
|
|
109
108
|
const { addEditButton = true } = pluginAdminConfig;
|
|
110
109
|
// Parse page-tree integration config
|
|
111
110
|
// - undefined: auto-detect at runtime (null stored, view will check for pageSegment field)
|
|
@@ -274,32 +273,17 @@ import { createContextListHandler, createContextCreateHandler, createContextUpda
|
|
|
274
273
|
'/puck/:collection/:id/versions',
|
|
275
274
|
'/puck/:collection/:id/restore'
|
|
276
275
|
]);
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
const isProduction = process.env.NODE_ENV === 'production';
|
|
281
|
-
const useCompiledCss = isProduction && editorStylesheetCompiled;
|
|
276
|
+
// Stylesheet URLs for the editor preview iframe. These are passed straight
|
|
277
|
+
// through: the same URLs resolve in development and production, so the
|
|
278
|
+
// editor can no longer look correct locally and unstyled in production.
|
|
282
279
|
const editorStylesheets = [
|
|
283
|
-
...
|
|
284
|
-
editorStylesheetCompiled
|
|
285
|
-
] : editorStylesheet ? [
|
|
286
|
-
PUCK_STYLES_ENDPOINT
|
|
287
|
-
] : [],
|
|
288
|
-
...editorStylesheetUrls
|
|
280
|
+
...editorStylesheetsOption
|
|
289
281
|
];
|
|
290
282
|
// Filter out parameterized puck endpoints from previous plugin instances
|
|
291
283
|
// so we can re-register them with the merged collections list
|
|
292
284
|
const incomingEndpoints = (incomingConfig.endpoints || []).filter((ep)=>!parameterizedPuckPaths.has(ep.path));
|
|
293
285
|
const endpoints = enableEndpoints ? [
|
|
294
286
|
...incomingEndpoints,
|
|
295
|
-
// Styles endpoint MUST be first - exact match before parameterized routes
|
|
296
|
-
...editorStylesheet ? [
|
|
297
|
-
{
|
|
298
|
-
path: '/puck/styles',
|
|
299
|
-
method: 'get',
|
|
300
|
-
handler: createStylesHandler(editorStylesheet)
|
|
301
|
-
}
|
|
302
|
-
] : [],
|
|
303
287
|
// AI endpoint (exact match, before parameterized routes)
|
|
304
288
|
...aiConfig?.enabled ? [
|
|
305
289
|
{
|
|
@@ -441,7 +425,5 @@ export { TemplatesCollection } from '../collections/Templates.js';
|
|
|
441
425
|
export { getPuckFields, getPuckCollectionConfig, puckDataField, editorVersionField, createEditorVersionField, pageLayoutField, createPageLayoutField, isHomepageField, seoFieldGroup, conversionFieldGroup } from './fields/index.js';
|
|
442
426
|
// Export the edit button generator for hybrid collections
|
|
443
427
|
export { generatePuckEditField };
|
|
444
|
-
// Export styles endpoint constant
|
|
445
|
-
export { PUCK_STYLES_ENDPOINT } from '../endpoints/styles.js';
|
|
446
428
|
// Re-export hooks for hybrid collection integration
|
|
447
429
|
export { createIsHomepageUniqueHook, unsetHomepage, HomepageConflictError } from './hooks/index.js';
|
package/dist/types/index.d.ts
CHANGED
|
@@ -141,42 +141,44 @@ export interface PuckPluginOptions {
|
|
|
141
141
|
*/
|
|
142
142
|
pageTreeIntegration?: boolean | PageTreeIntegrationOptions;
|
|
143
143
|
/**
|
|
144
|
-
*
|
|
145
|
-
* The plugin compiles this file with PostCSS/Tailwind and serves it at /api/puck/styles.
|
|
146
|
-
* This allows the editor preview to display your frontend styles (CSS variables, Tailwind utilities).
|
|
144
|
+
* Stylesheet URLs to load inside the editor preview iframe, in order.
|
|
147
145
|
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
* Additional stylesheet URLs to load in the editor iframe.
|
|
154
|
-
* Use this for external stylesheets like Google Fonts that can't be compiled.
|
|
146
|
+
* These are plain URLs the browser fetches — a static file your build emits,
|
|
147
|
+
* or any external stylesheet. The plugin does not compile CSS: your app's own
|
|
148
|
+
* toolchain already does that far better than we can, and compiling it a
|
|
149
|
+
* second time was the source of a dev/production split where the editor
|
|
150
|
+
* looked correct locally and unstyled in production.
|
|
155
151
|
*
|
|
156
|
-
*
|
|
157
|
-
|
|
158
|
-
editorStylesheetUrls?: string[];
|
|
159
|
-
/**
|
|
160
|
-
* Path to pre-compiled CSS file for production use.
|
|
161
|
-
* When set, the editor will load this static file instead of using the runtime compilation endpoint.
|
|
162
|
-
* Use with `withPuckCSS()` from `@delmaredigital/payload-puck/next` to compile CSS at build time.
|
|
152
|
+
* Generate the file with Tailwind's own CLI as part of your build, so the
|
|
153
|
+
* editor loads byte-identical CSS in every environment:
|
|
163
154
|
*
|
|
164
|
-
*
|
|
155
|
+
* ```jsonc
|
|
156
|
+
* // package.json
|
|
157
|
+
* {
|
|
158
|
+
* "scripts": {
|
|
159
|
+
* "build:puck-css": "tailwindcss -i ./src/app/(frontend)/globals.css -o ./public/puck-editor-styles.css",
|
|
160
|
+
* "build": "pnpm build:puck-css && next build",
|
|
161
|
+
* "dev": "pnpm build:puck-css --watch & next dev"
|
|
162
|
+
* }
|
|
163
|
+
* }
|
|
164
|
+
* ```
|
|
165
165
|
*
|
|
166
|
-
* @example
|
|
167
166
|
* ```typescript
|
|
168
|
-
* // next.config.js
|
|
169
|
-
* import { withPuckCSS } from '@delmaredigital/payload-puck/next'
|
|
170
|
-
* export default withPuckCSS({ cssInput: 'src/globals.css' })(nextConfig)
|
|
171
|
-
*
|
|
172
|
-
* // payload.config.ts
|
|
173
167
|
* createPuckPlugin({
|
|
174
|
-
*
|
|
175
|
-
*
|
|
168
|
+
* editorStylesheets: [
|
|
169
|
+
* '/puck-editor-styles.css',
|
|
170
|
+
* 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700',
|
|
171
|
+
* ],
|
|
176
172
|
* })
|
|
177
173
|
* ```
|
|
174
|
+
*
|
|
175
|
+
* Resolved URLs are published on `config.custom.puck.editorStylesheets` and
|
|
176
|
+
* passed to the editor automatically — you do not need to repeat them on
|
|
177
|
+
* `PuckConfigProvider`.
|
|
178
|
+
*
|
|
179
|
+
* @example ['/puck-editor-styles.css']
|
|
178
180
|
*/
|
|
179
|
-
|
|
181
|
+
editorStylesheets?: string[];
|
|
180
182
|
/**
|
|
181
183
|
* AI configuration for the plugin.
|
|
182
184
|
* Enables AI-powered page generation in the editor.
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.8.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by scripts/generate-version.js - do not edit manually
|
|
2
|
-
export const VERSION = '0.
|
|
2
|
+
export const VERSION = '0.8.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delmaredigital/payload-puck",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Puck visual page builder plugin for Payload CMS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -82,11 +82,6 @@
|
|
|
82
82
|
"types": "./dist/ai/index.d.ts",
|
|
83
83
|
"default": "./dist/ai/index.js"
|
|
84
84
|
},
|
|
85
|
-
"./next": {
|
|
86
|
-
"import": "./dist/next/index.js",
|
|
87
|
-
"types": "./dist/next/index.d.ts",
|
|
88
|
-
"default": "./dist/next/index.js"
|
|
89
|
-
},
|
|
90
85
|
"./email": {
|
|
91
86
|
"import": "./dist/email/index.js",
|
|
92
87
|
"types": "./dist/email/index.d.ts",
|
|
@@ -104,25 +99,25 @@
|
|
|
104
99
|
"scripts": {
|
|
105
100
|
"dev-publish": "pnpm build && pnpm version prerelease --preid=dev --no-git-tag-version && pnpm publish --registry http://localhost:4873 --no-git-checks --tag dev",
|
|
106
101
|
"generate-version": "node scripts/generate-version.js",
|
|
107
|
-
"
|
|
108
|
-
"build": "pnpm build:swc && pnpm build:types && pnpm copyfiles",
|
|
102
|
+
"build": "pnpm generate-version && pnpm build:swc && pnpm build:types && pnpm copyfiles",
|
|
109
103
|
"build:swc": "swc ./src -d ./dist --strip-leading-paths",
|
|
110
104
|
"build:types": "tsc --outDir dist",
|
|
111
105
|
"copyfiles": "copyfiles -u 1 \"src/**/*.css\" dist",
|
|
112
106
|
"dev": "pnpm generate-version && swc ./src -d ./dist --strip-leading-paths --watch",
|
|
113
|
-
"typecheck": "tsc --noEmit",
|
|
107
|
+
"typecheck": "pnpm generate-version && tsc --noEmit && tsc -p tsconfig.test.json",
|
|
108
|
+
"test": "pnpm generate-version && vitest run",
|
|
109
|
+
"test:watch": "pnpm generate-version && vitest",
|
|
110
|
+
"test:coverage": "pnpm generate-version && vitest run --coverage",
|
|
114
111
|
"clean": "rm -rf dist",
|
|
115
112
|
"prepublishOnly": "pnpm build"
|
|
116
113
|
},
|
|
117
114
|
"peerDependencies": {
|
|
118
115
|
"@payloadcms/next": ">=3.69.0",
|
|
119
116
|
"@payloadcms/ui": ">=3.69.0",
|
|
120
|
-
"@puckeditor/core": ">=0.
|
|
117
|
+
"@puckeditor/core": ">=0.23.0",
|
|
121
118
|
"@tailwindcss/postcss": ">=4.0.0",
|
|
122
119
|
"next": ">=15.4.8",
|
|
123
120
|
"payload": ">=3.69.0",
|
|
124
|
-
"postcss": ">=8.0.0",
|
|
125
|
-
"postcss-load-config": ">=4.0.0",
|
|
126
121
|
"react": ">=19.2.1",
|
|
127
122
|
"react-dom": ">=19.2.1",
|
|
128
123
|
"tailwindcss": ">=3.0.0 || >=4.0.0",
|
|
@@ -135,12 +130,6 @@
|
|
|
135
130
|
"@payloadcms/next": {
|
|
136
131
|
"optional": true
|
|
137
132
|
},
|
|
138
|
-
"postcss": {
|
|
139
|
-
"optional": true
|
|
140
|
-
},
|
|
141
|
-
"postcss-load-config": {
|
|
142
|
-
"optional": true
|
|
143
|
-
},
|
|
144
133
|
"tailwindcss": {
|
|
145
134
|
"optional": true
|
|
146
135
|
},
|
|
@@ -152,9 +141,9 @@
|
|
|
152
141
|
}
|
|
153
142
|
},
|
|
154
143
|
"dependencies": {
|
|
155
|
-
"@puckeditor/cloud-client": "^0.
|
|
156
|
-
"@puckeditor/plugin-ai": "^0.
|
|
157
|
-
"@puckeditor/plugin-heading-analyzer": "^0.
|
|
144
|
+
"@puckeditor/cloud-client": "^0.8.2",
|
|
145
|
+
"@puckeditor/plugin-ai": "^0.8.2",
|
|
146
|
+
"@puckeditor/plugin-heading-analyzer": "^0.23.0",
|
|
158
147
|
"@radix-ui/react-popover": "^1.1.15",
|
|
159
148
|
"@tiptap/core": "^3.20.1",
|
|
160
149
|
"@tiptap/extension-color": "^3.20.1",
|
|
@@ -171,20 +160,22 @@
|
|
|
171
160
|
"lucide-react": "^0.469.0"
|
|
172
161
|
},
|
|
173
162
|
"devDependencies": {
|
|
174
|
-
"@payloadcms/next": "^3.
|
|
175
|
-
"@payloadcms/ui": "^3.
|
|
176
|
-
"@puckeditor/core": "^0.
|
|
163
|
+
"@payloadcms/next": "^3.87.1",
|
|
164
|
+
"@payloadcms/ui": "^3.87.1",
|
|
165
|
+
"@puckeditor/core": "^0.23.0",
|
|
177
166
|
"@swc/cli": "^0.6.0",
|
|
178
167
|
"@swc/core": "^1.15.18",
|
|
179
168
|
"@types/node": "^24.12.0",
|
|
180
169
|
"@types/react": "^19.2.14",
|
|
181
170
|
"@types/react-dom": "^19.2.3",
|
|
171
|
+
"@vitest/coverage-v8": "^3.2.7",
|
|
182
172
|
"copyfiles": "^2.4.1",
|
|
183
|
-
"next": "^16.
|
|
184
|
-
"payload": "^3.
|
|
173
|
+
"next": "^16.3.0",
|
|
174
|
+
"payload": "^3.87.1",
|
|
185
175
|
"react": "^19.2.4",
|
|
186
176
|
"react-dom": "^19.2.4",
|
|
187
|
-
"typescript": "^5.9.3"
|
|
177
|
+
"typescript": "^5.9.3",
|
|
178
|
+
"vitest": "^3.2.7"
|
|
188
179
|
},
|
|
189
180
|
"keywords": [
|
|
190
181
|
"payload",
|
|
@@ -196,7 +187,7 @@
|
|
|
196
187
|
"cms"
|
|
197
188
|
],
|
|
198
189
|
"engines": {
|
|
199
|
-
"node": "
|
|
190
|
+
"node": ">=20.9.0"
|
|
200
191
|
},
|
|
201
192
|
"author": "Delmare Digital",
|
|
202
193
|
"repository": {
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Styles Endpoint Handler
|
|
3
|
-
*
|
|
4
|
-
* Compiles and serves CSS for the editor iframe.
|
|
5
|
-
* Uses the consumer's PostCSS/Tailwind installation via peer dependencies.
|
|
6
|
-
* Loads the project's postcss.config.js for proper plugin configuration.
|
|
7
|
-
*/
|
|
8
|
-
import type { PayloadHandler } from 'payload';
|
|
9
|
-
/**
|
|
10
|
-
* Creates a handler that serves compiled CSS for the editor iframe
|
|
11
|
-
*
|
|
12
|
-
* @param cssFilePath - Path to CSS file relative to project root
|
|
13
|
-
* @returns PayloadHandler that serves compiled CSS
|
|
14
|
-
*/
|
|
15
|
-
export declare function createStylesHandler(cssFilePath: string): PayloadHandler;
|
|
16
|
-
/**
|
|
17
|
-
* Helper constant for the styles endpoint URL
|
|
18
|
-
*/
|
|
19
|
-
export declare const PUCK_STYLES_ENDPOINT = "/api/puck/styles";
|
package/dist/endpoints/styles.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Styles Endpoint Handler
|
|
3
|
-
*
|
|
4
|
-
* Compiles and serves CSS for the editor iframe.
|
|
5
|
-
* Uses the consumer's PostCSS/Tailwind installation via peer dependencies.
|
|
6
|
-
* Loads the project's postcss.config.js for proper plugin configuration.
|
|
7
|
-
*/ import { readFileSync, statSync, existsSync } from 'fs';
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
const cssCache = new Map();
|
|
10
|
-
/**
|
|
11
|
-
* Compile CSS using PostCSS with the project's configuration
|
|
12
|
-
* Loads postcss.config.js from project root for proper plugin setup
|
|
13
|
-
* Falls back to minimal Tailwind-only config if no config file found
|
|
14
|
-
*/ async function compileCss(css, filePath) {
|
|
15
|
-
try {
|
|
16
|
-
// Dynamic import to use consumer's PostCSS installation
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
-
let postcss;
|
|
19
|
-
try {
|
|
20
|
-
postcss = (await import(/* webpackIgnore: true */ 'postcss')).default;
|
|
21
|
-
} catch {
|
|
22
|
-
console.warn('[payload-puck] PostCSS not found. CSS will not be processed. Install postcss as a dependency.');
|
|
23
|
-
return css;
|
|
24
|
-
}
|
|
25
|
-
// Try to load the project's postcss.config.js using postcss-load-config
|
|
26
|
-
// This ensures all plugins (typography, etc.) are properly loaded
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
-
let processor;
|
|
29
|
-
let usedProjectConfig = false;
|
|
30
|
-
try {
|
|
31
|
-
// Dynamic import of postcss-load-config (optional peer dependency)
|
|
32
|
-
// This package is commonly installed alongside PostCSS
|
|
33
|
-
const loadConfigModule = await import(/* webpackIgnore: true */ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
34
|
-
// @ts-ignore - optional dependency may not have types
|
|
35
|
-
'postcss-load-config');
|
|
36
|
-
const postcssLoadConfig = loadConfigModule.default;
|
|
37
|
-
// Load config from project root (where postcss.config.js lives)
|
|
38
|
-
const { plugins } = await postcssLoadConfig({}, process.cwd());
|
|
39
|
-
processor = postcss(plugins);
|
|
40
|
-
usedProjectConfig = true;
|
|
41
|
-
} catch {
|
|
42
|
-
// postcss-load-config not available or no config found - this is fine
|
|
43
|
-
// Fall back to direct Tailwind import silently
|
|
44
|
-
// Try Tailwind v4 first (@tailwindcss/postcss)
|
|
45
|
-
try {
|
|
46
|
-
const tailwindcss = (await import(/* webpackIgnore: true */ '@tailwindcss/postcss')).default;
|
|
47
|
-
processor = postcss([
|
|
48
|
-
tailwindcss
|
|
49
|
-
]);
|
|
50
|
-
} catch {
|
|
51
|
-
// Fall back to Tailwind v3 (tailwindcss)
|
|
52
|
-
try {
|
|
53
|
-
const tailwindcss = (await import(/* webpackIgnore: true */ 'tailwindcss')).default;
|
|
54
|
-
processor = postcss([
|
|
55
|
-
tailwindcss
|
|
56
|
-
]);
|
|
57
|
-
} catch {
|
|
58
|
-
// No Tailwind available - just return the CSS as-is
|
|
59
|
-
console.warn('[payload-puck] No Tailwind CSS installation found. CSS will not be processed.');
|
|
60
|
-
return css;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
const result = await processor.process(css, {
|
|
65
|
-
from: filePath
|
|
66
|
-
});
|
|
67
|
-
return result.css;
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error('[payload-puck] CSS compilation error:', error);
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Creates a handler that serves compiled CSS for the editor iframe
|
|
75
|
-
*
|
|
76
|
-
* @param cssFilePath - Path to CSS file relative to project root
|
|
77
|
-
* @returns PayloadHandler that serves compiled CSS
|
|
78
|
-
*/ export function createStylesHandler(cssFilePath) {
|
|
79
|
-
return async (req)=>{
|
|
80
|
-
try {
|
|
81
|
-
const fullPath = join(process.cwd(), cssFilePath);
|
|
82
|
-
// Check if file exists
|
|
83
|
-
if (!existsSync(fullPath)) {
|
|
84
|
-
console.error(`[payload-puck] CSS file not found: ${fullPath}`);
|
|
85
|
-
return new Response(`/* CSS file not found: ${cssFilePath} */`, {
|
|
86
|
-
status: 404,
|
|
87
|
-
headers: {
|
|
88
|
-
'Content-Type': 'text/css'
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
// Get file modification time for cache invalidation
|
|
93
|
-
const stats = statSync(fullPath);
|
|
94
|
-
const mtime = stats.mtimeMs;
|
|
95
|
-
// ETag derived from mtime - changes whenever the source file changes,
|
|
96
|
-
// whether from a dev-mode edit or a version upgrade recompiling output.
|
|
97
|
-
const etag = `"${mtime}"`;
|
|
98
|
-
// If the browser's cached copy is still fresh, tell it so without
|
|
99
|
-
// doing any file read/compilation work.
|
|
100
|
-
const ifNoneMatch = req.headers?.get('if-none-match');
|
|
101
|
-
if (ifNoneMatch === etag) {
|
|
102
|
-
return new Response(null, {
|
|
103
|
-
status: 304,
|
|
104
|
-
headers: {
|
|
105
|
-
ETag: etag,
|
|
106
|
-
'Cache-Control': 'no-cache'
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
// Check cache
|
|
111
|
-
const cached = cssCache.get(cssFilePath);
|
|
112
|
-
if (cached && cached.mtime === mtime) {
|
|
113
|
-
return new Response(cached.css, {
|
|
114
|
-
headers: {
|
|
115
|
-
'Content-Type': 'text/css',
|
|
116
|
-
'Cache-Control': 'no-cache',
|
|
117
|
-
ETag: etag,
|
|
118
|
-
'Last-Modified': new Date(mtime).toUTCString(),
|
|
119
|
-
'X-Puck-Cache': 'hit'
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
// Read and compile CSS
|
|
124
|
-
const rawCss = readFileSync(fullPath, 'utf-8');
|
|
125
|
-
const compiledCss = await compileCss(rawCss, fullPath);
|
|
126
|
-
// Update cache
|
|
127
|
-
cssCache.set(cssFilePath, {
|
|
128
|
-
css: compiledCss,
|
|
129
|
-
mtime
|
|
130
|
-
});
|
|
131
|
-
return new Response(compiledCss, {
|
|
132
|
-
headers: {
|
|
133
|
-
'Content-Type': 'text/css',
|
|
134
|
-
'Cache-Control': 'no-cache',
|
|
135
|
-
ETag: etag,
|
|
136
|
-
'Last-Modified': new Date(mtime).toUTCString(),
|
|
137
|
-
'X-Puck-Cache': 'miss'
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.error('[payload-puck] Styles endpoint error:', error);
|
|
142
|
-
return new Response(`/* Error compiling CSS: ${error instanceof Error ? error.message : 'Unknown error'} */`, {
|
|
143
|
-
status: 500,
|
|
144
|
-
headers: {
|
|
145
|
-
'Content-Type': 'text/css'
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Helper constant for the styles endpoint URL
|
|
153
|
-
*/ export const PUCK_STYLES_ENDPOINT = '/api/puck/styles';
|
package/dist/next/index.d.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Next.js Configuration Wrapper for Puck CSS
|
|
3
|
-
*
|
|
4
|
-
* Compiles CSS at build time using the project's PostCSS/Tailwind configuration.
|
|
5
|
-
* This ensures the editor iframe styles work in production (Vercel, etc.) where
|
|
6
|
-
* source files aren't available at runtime.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```js
|
|
10
|
-
* // next.config.js
|
|
11
|
-
* import { withPuckCSS } from '@delmaredigital/payload-puck/next'
|
|
12
|
-
* import { withPayload } from '@payloadcms/next/withPayload'
|
|
13
|
-
*
|
|
14
|
-
* export default withPuckCSS({
|
|
15
|
-
* cssInput: 'src/app/(frontend)/globals.css',
|
|
16
|
-
* })(withPayload(nextConfig))
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
import type { NextConfig } from 'next';
|
|
20
|
-
/**
|
|
21
|
-
* Options for the withPuckCSS wrapper
|
|
22
|
-
*/
|
|
23
|
-
export interface WithPuckCSSOptions {
|
|
24
|
-
/**
|
|
25
|
-
* Path to the source CSS file (relative to project root)
|
|
26
|
-
* @example 'src/app/(frontend)/globals.css'
|
|
27
|
-
*/
|
|
28
|
-
cssInput: string;
|
|
29
|
-
/**
|
|
30
|
-
* Output path for compiled CSS (relative to public/)
|
|
31
|
-
* @default 'puck-editor-styles.css'
|
|
32
|
-
*/
|
|
33
|
-
cssOutput?: string;
|
|
34
|
-
/**
|
|
35
|
-
* Whether to skip compilation in development
|
|
36
|
-
* @default true
|
|
37
|
-
*/
|
|
38
|
-
skipInDev?: boolean;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Default output filename for compiled CSS
|
|
42
|
-
*/
|
|
43
|
-
export declare const PUCK_CSS_OUTPUT_DEFAULT = "puck-editor-styles.css";
|
|
44
|
-
/**
|
|
45
|
-
* Next.js configuration wrapper that compiles Puck editor CSS at build time
|
|
46
|
-
*
|
|
47
|
-
* @param options - Configuration options
|
|
48
|
-
* @returns A function that wraps your Next.js config
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```js
|
|
52
|
-
* import { withPuckCSS } from '@delmaredigital/payload-puck/next'
|
|
53
|
-
*
|
|
54
|
-
* export default withPuckCSS({
|
|
55
|
-
* cssInput: 'src/app/(frontend)/globals.css',
|
|
56
|
-
* })(nextConfig)
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
export declare function withPuckCSS(options: WithPuckCSSOptions): (nextConfig: NextConfig) => NextConfig;
|
|
60
|
-
/**
|
|
61
|
-
* Get the URL path for the compiled CSS file
|
|
62
|
-
* Use this in your plugin configuration
|
|
63
|
-
*/
|
|
64
|
-
export declare function getPuckCSSPath(cssOutput?: string): string;
|
package/dist/next/index.js
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Next.js Configuration Wrapper for Puck CSS
|
|
3
|
-
*
|
|
4
|
-
* Compiles CSS at build time using the project's PostCSS/Tailwind configuration.
|
|
5
|
-
* This ensures the editor iframe styles work in production (Vercel, etc.) where
|
|
6
|
-
* source files aren't available at runtime.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```js
|
|
10
|
-
* // next.config.js
|
|
11
|
-
* import { withPuckCSS } from '@delmaredigital/payload-puck/next'
|
|
12
|
-
* import { withPayload } from '@payloadcms/next/withPayload'
|
|
13
|
-
*
|
|
14
|
-
* export default withPuckCSS({
|
|
15
|
-
* cssInput: 'src/app/(frontend)/globals.css',
|
|
16
|
-
* })(withPayload(nextConfig))
|
|
17
|
-
* ```
|
|
18
|
-
*/ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
19
|
-
import { dirname, resolve } from 'path';
|
|
20
|
-
/**
|
|
21
|
-
* Default output filename for compiled CSS
|
|
22
|
-
*/ export const PUCK_CSS_OUTPUT_DEFAULT = 'puck-editor-styles.css';
|
|
23
|
-
/**
|
|
24
|
-
* Compile CSS using PostCSS with the project's configuration
|
|
25
|
-
*/ async function compileCss(css, filePath) {
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
-
let postcss;
|
|
28
|
-
try {
|
|
29
|
-
postcss = (await import('postcss')).default;
|
|
30
|
-
} catch {
|
|
31
|
-
console.warn('[payload-puck] PostCSS not found. CSS will not be compiled.');
|
|
32
|
-
return css;
|
|
33
|
-
}
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
-
let processor;
|
|
36
|
-
try {
|
|
37
|
-
// Try to load project's postcss.config.js
|
|
38
|
-
const loadConfigModule = await import('postcss-load-config');
|
|
39
|
-
const postcssLoadConfig = loadConfigModule.default;
|
|
40
|
-
const { plugins } = await postcssLoadConfig({}, process.cwd());
|
|
41
|
-
processor = postcss(plugins);
|
|
42
|
-
} catch {
|
|
43
|
-
// Fall back to direct Tailwind import
|
|
44
|
-
try {
|
|
45
|
-
const tailwindcss = (await import('@tailwindcss/postcss')).default;
|
|
46
|
-
processor = postcss([
|
|
47
|
-
tailwindcss
|
|
48
|
-
]);
|
|
49
|
-
} catch {
|
|
50
|
-
try {
|
|
51
|
-
const tailwindcss = (await import('tailwindcss')).default;
|
|
52
|
-
processor = postcss([
|
|
53
|
-
tailwindcss
|
|
54
|
-
]);
|
|
55
|
-
} catch {
|
|
56
|
-
console.warn('[payload-puck] No Tailwind CSS found. CSS will not be compiled.');
|
|
57
|
-
return css;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
const result = await processor.process(css, {
|
|
62
|
-
from: filePath
|
|
63
|
-
});
|
|
64
|
-
return result.css;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Webpack plugin that compiles CSS at build time
|
|
68
|
-
*/ class PuckCSSWebpackPlugin {
|
|
69
|
-
options;
|
|
70
|
-
compiled = false;
|
|
71
|
-
constructor(options){
|
|
72
|
-
this.options = options;
|
|
73
|
-
}
|
|
74
|
-
apply(compiler) {
|
|
75
|
-
compiler.hooks.beforeCompile.tapPromise('PuckCSSWebpackPlugin', async ()=>{
|
|
76
|
-
// Only compile once per build
|
|
77
|
-
if (this.compiled) return;
|
|
78
|
-
this.compiled = true;
|
|
79
|
-
const { cssInput, cssOutput, skipInDev } = this.options;
|
|
80
|
-
// Skip in development if configured
|
|
81
|
-
if (skipInDev && process.env.NODE_ENV === 'development') {
|
|
82
|
-
console.log('[payload-puck] Skipping CSS compilation in development');
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
const inputPath = resolve(process.cwd(), cssInput);
|
|
86
|
-
const outputPath = resolve(process.cwd(), 'public', cssOutput);
|
|
87
|
-
// Check if source file exists
|
|
88
|
-
if (!existsSync(inputPath)) {
|
|
89
|
-
console.error(`[payload-puck] CSS source file not found: ${inputPath}`);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
try {
|
|
93
|
-
console.log(`[payload-puck] Compiling CSS: ${cssInput} -> public/${cssOutput}`);
|
|
94
|
-
// Read source CSS
|
|
95
|
-
const rawCss = readFileSync(inputPath, 'utf-8');
|
|
96
|
-
// Compile with PostCSS/Tailwind
|
|
97
|
-
const compiledCss = await compileCss(rawCss, inputPath);
|
|
98
|
-
// Ensure public directory exists
|
|
99
|
-
const outputDir = dirname(outputPath);
|
|
100
|
-
if (!existsSync(outputDir)) {
|
|
101
|
-
mkdirSync(outputDir, {
|
|
102
|
-
recursive: true
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
// Write compiled CSS
|
|
106
|
-
writeFileSync(outputPath, compiledCss, 'utf-8');
|
|
107
|
-
console.log(`[payload-puck] CSS compiled successfully (${(compiledCss.length / 1024).toFixed(1)}KB)`);
|
|
108
|
-
} catch (error) {
|
|
109
|
-
console.error('[payload-puck] CSS compilation failed:', error);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Next.js configuration wrapper that compiles Puck editor CSS at build time
|
|
116
|
-
*
|
|
117
|
-
* @param options - Configuration options
|
|
118
|
-
* @returns A function that wraps your Next.js config
|
|
119
|
-
*
|
|
120
|
-
* @example
|
|
121
|
-
* ```js
|
|
122
|
-
* import { withPuckCSS } from '@delmaredigital/payload-puck/next'
|
|
123
|
-
*
|
|
124
|
-
* export default withPuckCSS({
|
|
125
|
-
* cssInput: 'src/app/(frontend)/globals.css',
|
|
126
|
-
* })(nextConfig)
|
|
127
|
-
* ```
|
|
128
|
-
*/ export function withPuckCSS(options) {
|
|
129
|
-
const resolvedOptions = {
|
|
130
|
-
cssInput: options.cssInput,
|
|
131
|
-
cssOutput: options.cssOutput ?? PUCK_CSS_OUTPUT_DEFAULT,
|
|
132
|
-
skipInDev: options.skipInDev ?? true
|
|
133
|
-
};
|
|
134
|
-
return (nextConfig)=>{
|
|
135
|
-
return {
|
|
136
|
-
...nextConfig,
|
|
137
|
-
webpack: (webpackConfig, context)=>{
|
|
138
|
-
// Add our CSS compilation plugin
|
|
139
|
-
webpackConfig.plugins = webpackConfig.plugins || [];
|
|
140
|
-
webpackConfig.plugins.push(new PuckCSSWebpackPlugin(resolvedOptions));
|
|
141
|
-
// Call existing webpack config if present
|
|
142
|
-
if (typeof nextConfig.webpack === 'function') {
|
|
143
|
-
return nextConfig.webpack(webpackConfig, context);
|
|
144
|
-
}
|
|
145
|
-
return webpackConfig;
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Get the URL path for the compiled CSS file
|
|
152
|
-
* Use this in your plugin configuration
|
|
153
|
-
*/ export function getPuckCSSPath(cssOutput) {
|
|
154
|
-
return `/${cssOutput ?? PUCK_CSS_OUTPUT_DEFAULT}`;
|
|
155
|
-
}
|