create-ampless 1.0.0-alpha.128 → 1.0.0-alpha.131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +28 -10
- package/dist/templates/_shared/app/(admin)/admin/posts/[postId]/page.tsx +1 -2
- package/dist/templates/_shared/app/(admin)/admin/posts/new/page.tsx +1 -2
- package/dist/templates/_shared/app/(admin)/admin/preview/route.tsx +66 -0
- package/dist/templates/_shared/docs/plugin-author-guide.ja.md +31 -9
- package/dist/templates/_shared/docs/plugin-author-guide.md +51 -10
- package/dist/templates/_shared/package.json +15 -14
- package/package.json +1 -1
- package/dist/templates/_shared/app/(admin)/admin/_actions/render-preview.tsx +0 -32
package/dist/index.js
CHANGED
|
@@ -1885,23 +1885,41 @@ var AMPLESS_MANAGED_APP_PATHS = [
|
|
|
1885
1885
|
"app/login",
|
|
1886
1886
|
"app/site"
|
|
1887
1887
|
];
|
|
1888
|
-
var AMPLESS_RETIRED_PATHS = [
|
|
1888
|
+
var AMPLESS_RETIRED_PATHS = [
|
|
1889
|
+
// Phase 7 preview pipeline migrated from a `'use server'` action to a
|
|
1890
|
+
// Route Handler at `app/(admin)/admin/preview/route.tsx` (default
|
|
1891
|
+
// endpoint: `/admin/preview`). The old action made Next.js 15+ refuse
|
|
1892
|
+
// to compile the edit-post page
|
|
1893
|
+
// because the import graph traced `react-dom/server` from Client
|
|
1894
|
+
// Components through Server Action modules. Sites scaffolded before
|
|
1895
|
+
// this change pick the new endpoint up on their next `update-ampless`
|
|
1896
|
+
// — the new route file is seeded as part of the normal template
|
|
1897
|
+
// sync, and the old action file is retired by this entry.
|
|
1898
|
+
"app/(admin)/admin/_actions/render-preview.tsx"
|
|
1899
|
+
];
|
|
1889
1900
|
var AMPLESS_PACKAGES = /* @__PURE__ */ new Set([
|
|
1890
1901
|
"ampless",
|
|
1891
1902
|
"@ampless/admin",
|
|
1892
1903
|
"@ampless/backend",
|
|
1893
|
-
"@ampless/runtime",
|
|
1894
|
-
"@ampless/plugin-seo",
|
|
1895
|
-
"@ampless/plugin-rss",
|
|
1896
|
-
"@ampless/plugin-schema-jsonld",
|
|
1897
|
-
"@ampless/plugin-webhook",
|
|
1898
|
-
"@ampless/plugin-og-image",
|
|
1899
1904
|
"@ampless/plugin-analytics-ga4",
|
|
1905
|
+
"@ampless/plugin-cookie-consent",
|
|
1900
1906
|
"@ampless/plugin-gtm",
|
|
1907
|
+
"@ampless/plugin-og-image",
|
|
1901
1908
|
"@ampless/plugin-plausible",
|
|
1902
|
-
"@ampless/plugin-
|
|
1903
|
-
"@ampless/plugin-
|
|
1909
|
+
"@ampless/plugin-reading-time",
|
|
1910
|
+
"@ampless/plugin-rss",
|
|
1911
|
+
"@ampless/plugin-schema-jsonld",
|
|
1912
|
+
"@ampless/plugin-seo",
|
|
1913
|
+
"@ampless/plugin-webhook",
|
|
1914
|
+
"@ampless/plugin-x-embed",
|
|
1915
|
+
"@ampless/plugin-youtube",
|
|
1916
|
+
"@ampless/runtime"
|
|
1904
1917
|
]);
|
|
1918
|
+
var AMPLESS_MANAGED_TRANSITIVE_PREFIXES = ["@tiptap/"];
|
|
1919
|
+
function isManagedDep(name) {
|
|
1920
|
+
if (AMPLESS_PACKAGES.has(name)) return true;
|
|
1921
|
+
return AMPLESS_MANAGED_TRANSITIVE_PREFIXES.some((p3) => name.startsWith(p3));
|
|
1922
|
+
}
|
|
1905
1923
|
var AMPLESS_MANAGED_SCRIPTS = /* @__PURE__ */ new Set([
|
|
1906
1924
|
"sandbox",
|
|
1907
1925
|
"sandbox:dev",
|
|
@@ -2231,7 +2249,7 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
2231
2249
|
const templateDeps = templatePkg[section] ?? {};
|
|
2232
2250
|
const projectDeps = projectPkg[section] ?? {};
|
|
2233
2251
|
for (const [name, version] of Object.entries(templateDeps)) {
|
|
2234
|
-
if (!
|
|
2252
|
+
if (!isManagedDep(name)) continue;
|
|
2235
2253
|
projectDeps[name] = version;
|
|
2236
2254
|
}
|
|
2237
2255
|
if (Object.keys(projectDeps).length > 0) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { admin } from '@/lib/admin'
|
|
2
2
|
import { createEditPostPage } from '@ampless/admin/pages'
|
|
3
|
-
import { renderPreviewHtml } from '../../_actions/render-preview'
|
|
4
3
|
|
|
5
|
-
export default createEditPostPage(admin
|
|
4
|
+
export default createEditPostPage(admin)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { admin } from '@/lib/admin'
|
|
2
2
|
import { createNewPostPage } from '@ampless/admin/pages'
|
|
3
|
-
import { renderPreviewHtml } from '../../_actions/render-preview'
|
|
4
3
|
|
|
5
|
-
export default createNewPostPage(admin
|
|
4
|
+
export default createNewPostPage(admin)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Post } from 'ampless'
|
|
2
|
+
import { admin } from '@/lib/admin'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Preview-only Route Handler. Client-side `<PostForm>` / `<PostHistoryPanel>`
|
|
6
|
+
* POST a draft Post to this endpoint while the preview tab is open; we
|
|
7
|
+
* render the body + page-level scripts via `ampless.renderBody` /
|
|
8
|
+
* `publicPostScriptsForPage` and return a fully-rendered HTML string
|
|
9
|
+
* that the admin shows in an iframe (`sandbox="allow-scripts"`).
|
|
10
|
+
*
|
|
11
|
+
* Why a Route Handler instead of a Server Action: Next.js 15+
|
|
12
|
+
* refuses to compile Client Components that reach `react-dom/server`
|
|
13
|
+
* through a `'use server'` module, because the build traces the
|
|
14
|
+
* import graph from Client Components through Server Action modules.
|
|
15
|
+
* Putting this rendering behind a Route Handler decouples it from
|
|
16
|
+
* that graph entirely — the form fetches a plain HTTP endpoint and
|
|
17
|
+
* the bundler never walks from `<PostForm>` into here. The endpoint
|
|
18
|
+
* also gets an explicit `admin.isEditor()` gate so a future change
|
|
19
|
+
* to the `(admin)` route-group middleware can't silently turn
|
|
20
|
+
* preview into a content-leak vector for unpublished drafts.
|
|
21
|
+
*
|
|
22
|
+
* The `react-dom/server` import itself is loaded via dynamic
|
|
23
|
+
* `import()` inside the handler. Next.js 16's Turbopack flags any
|
|
24
|
+
* top-level static `import 'react-dom/server'` reached from the app
|
|
25
|
+
* router build (Route Handlers included), so we deliberately defer
|
|
26
|
+
* the resolution to request time — the module is still pulled from
|
|
27
|
+
* the same Node.js subpath that `next start` ships, just not visible
|
|
28
|
+
* to the build-time import-graph walker.
|
|
29
|
+
*
|
|
30
|
+
* Auth: locked to authenticated editors. Anonymous + reader access is
|
|
31
|
+
* 403. This matches the rest of `/admin/**`, which is gated by the
|
|
32
|
+
* `(admin)` route group + middleware, but we add an explicit check
|
|
33
|
+
* here as defence-in-depth against the middleware gate being
|
|
34
|
+
* misconfigured.
|
|
35
|
+
*/
|
|
36
|
+
export async function POST(req: Request): Promise<Response> {
|
|
37
|
+
const session = await admin.getServerSession()
|
|
38
|
+
if (!admin.isEditor(session)) {
|
|
39
|
+
return new Response('Forbidden', { status: 403 })
|
|
40
|
+
}
|
|
41
|
+
let draft: Post
|
|
42
|
+
try {
|
|
43
|
+
draft = (await req.json()) as Post
|
|
44
|
+
} catch {
|
|
45
|
+
return new Response('Bad Request', { status: 400 })
|
|
46
|
+
}
|
|
47
|
+
const ampless = await admin.getAmpless()
|
|
48
|
+
// IMPORTANT: include BOTH the body and the page-level scripts so
|
|
49
|
+
// widgets like x.com's `widgets.js` get a chance to hydrate in the
|
|
50
|
+
// iframe.
|
|
51
|
+
const node = (
|
|
52
|
+
<>
|
|
53
|
+
{await ampless.renderBody(draft)}
|
|
54
|
+
{await ampless.publicPostScriptsForPage([draft])}
|
|
55
|
+
</>
|
|
56
|
+
)
|
|
57
|
+
// Dynamic import: see top-of-file comment. The module is server-only
|
|
58
|
+
// and resolved at request time.
|
|
59
|
+
const { renderToStaticMarkup } = await import('react-dom/server')
|
|
60
|
+
return new Response(renderToStaticMarkup(node), {
|
|
61
|
+
headers: {
|
|
62
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
63
|
+
'Cache-Control': 'no-store',
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
}
|
|
@@ -808,16 +808,24 @@ export default createAdminLayout(admin, { editorBootstrap: EditorBootstrap })
|
|
|
808
808
|
|
|
809
809
|
### エディタプレビューのパイプライン
|
|
810
810
|
|
|
811
|
-
admin の edit / new post フォームは preview ペインを `<iframe sandbox="allow-scripts">` で表示する。`srcDoc` はテンプレート側
|
|
811
|
+
admin の edit / new post フォームは preview ペインを `<iframe sandbox="allow-scripts">` で表示する。`srcDoc` はテンプレート側 preview Route Handler が返す HTML。テンプレート scaffold は handler を `app/(admin)/admin/preview/route.tsx` に同梱する:
|
|
812
812
|
|
|
813
813
|
```tsx
|
|
814
|
-
// templates/_shared/app/(admin)/admin/
|
|
815
|
-
'use server'
|
|
816
|
-
import { renderToStaticMarkup } from 'react-dom/server'
|
|
814
|
+
// templates/_shared/app/(admin)/admin/preview/route.tsx
|
|
817
815
|
import type { Post } from 'ampless'
|
|
818
816
|
import { admin } from '@/lib/admin'
|
|
819
817
|
|
|
820
|
-
export async function
|
|
818
|
+
export async function POST(req: Request): Promise<Response> {
|
|
819
|
+
const session = await admin.getServerSession()
|
|
820
|
+
if (!admin.isEditor(session)) {
|
|
821
|
+
return new Response('Forbidden', { status: 403 })
|
|
822
|
+
}
|
|
823
|
+
let draft: Post
|
|
824
|
+
try {
|
|
825
|
+
draft = (await req.json()) as Post
|
|
826
|
+
} catch {
|
|
827
|
+
return new Response('Bad Request', { status: 400 })
|
|
828
|
+
}
|
|
821
829
|
const ampless = await admin.getAmpless()
|
|
822
830
|
const node = (
|
|
823
831
|
<>
|
|
@@ -825,21 +833,35 @@ export async function renderPreviewHtml(draft: Post): Promise<string> {
|
|
|
825
833
|
{await ampless.publicPostScriptsForPage([draft])}
|
|
826
834
|
</>
|
|
827
835
|
)
|
|
828
|
-
|
|
836
|
+
// dynamic import: 下の「Server Action ではなく Route Handler を採用した理由」参照。
|
|
837
|
+
const { renderToStaticMarkup } = await import('react-dom/server')
|
|
838
|
+
return new Response(renderToStaticMarkup(node), {
|
|
839
|
+
headers: {
|
|
840
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
841
|
+
'Cache-Control': 'no-store',
|
|
842
|
+
},
|
|
843
|
+
})
|
|
829
844
|
}
|
|
830
845
|
```
|
|
831
846
|
|
|
832
|
-
|
|
847
|
+
`<PostForm>` / `<PostHistoryPanel>` は素のままで `/admin/preview` に draft を POST する — call site で追加の wiring は不要:
|
|
833
848
|
|
|
834
849
|
```tsx
|
|
835
850
|
// templates/_shared/app/(admin)/admin/posts/[postId]/page.tsx
|
|
836
851
|
import { admin } from '@/lib/admin'
|
|
837
852
|
import { createEditPostPage } from '@ampless/admin/pages'
|
|
838
|
-
import { renderPreviewHtml } from '../../_actions/render-preview'
|
|
839
853
|
|
|
840
|
-
export default createEditPostPage(admin
|
|
854
|
+
export default createEditPostPage(admin)
|
|
841
855
|
```
|
|
842
856
|
|
|
857
|
+
admin が非デフォルトパス(Next.js `basePath` やカスタム prefix)にマウントされている場合は、page factory の `previewEndpoint` option で endpoint を上書きできる:
|
|
858
|
+
|
|
859
|
+
```tsx
|
|
860
|
+
export default createEditPostPage(admin, { previewEndpoint: '/cms/admin/preview' })
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Server Action ではなく Route Handler を採用した理由: `'use server'` モジュール内部で `react-dom/server` 由来の rendering を行うと、Next.js 15+ が edit-post page の compile に失敗する。build 時に Client Component から Server Action モジュール経由で import graph を辿るため、その経路上で `react-dom/server` に到達した時点で「You're importing a component that imports react-dom/server」チェックが発火する。Route Handler に切り出すことで rendering を import graph から完全に切り離せる — `<PostForm>` は plain な HTTP endpoint を fetch するだけで、bundler は form から handler 側へ踏み込まない。さらに handler 自身が `admin.isEditor()` を明示的にチェックすることで、将来 `(admin)` route-group gate が誤設定された場合の安全側のフェールセーフになる。`react-dom/server` の import 自体を dynamic にしているのは Next.js 16 の Turbopack が、Route Handler を含む app router build 経路で reach できる top-level の `react-dom/server` static import を一律で flag するため。request 時に解決することで build-time import-graph walker から外しつつ、runtime では同じ Node.js subpath からロードする。
|
|
864
|
+
|
|
843
865
|
iframe の `sandbox="allow-scripts"` は `allow-same-origin` を含まないので、preview の中身は opaque origin で動作する → widget script は admin の DOM に触れない。トレードオフ: widget が自身の origin の storage にアクセスできない場合に正しく動かない可能性がある。Phase 7 時点の dogfood では YouTube iframe / x.com widgets.js とも opaque origin で動作することを確認済み。将来 non-opaque origin が必要な widget が出てきた場合、escape hatch は別 subdomain の preview route + 適切な CSP — sandbox flag を緩めるのは取らない。
|
|
844
866
|
|
|
845
867
|
---
|
|
@@ -1062,16 +1062,25 @@ registered list onto its built-in extensions on every render.
|
|
|
1062
1062
|
|
|
1063
1063
|
The admin's edit / new post forms render the preview pane in an
|
|
1064
1064
|
`<iframe sandbox="allow-scripts">` whose `srcDoc` is the HTML returned
|
|
1065
|
-
by the template's
|
|
1065
|
+
by the template's preview Route Handler. The template scaffold ships
|
|
1066
|
+
the handler at `app/(admin)/admin/preview/route.tsx`:
|
|
1066
1067
|
|
|
1067
1068
|
```tsx
|
|
1068
|
-
// templates/_shared/app/(admin)/admin/
|
|
1069
|
-
'use server'
|
|
1070
|
-
import { renderToStaticMarkup } from 'react-dom/server'
|
|
1069
|
+
// templates/_shared/app/(admin)/admin/preview/route.tsx
|
|
1071
1070
|
import type { Post } from 'ampless'
|
|
1072
1071
|
import { admin } from '@/lib/admin'
|
|
1073
1072
|
|
|
1074
|
-
export async function
|
|
1073
|
+
export async function POST(req: Request): Promise<Response> {
|
|
1074
|
+
const session = await admin.getServerSession()
|
|
1075
|
+
if (!admin.isEditor(session)) {
|
|
1076
|
+
return new Response('Forbidden', { status: 403 })
|
|
1077
|
+
}
|
|
1078
|
+
let draft: Post
|
|
1079
|
+
try {
|
|
1080
|
+
draft = (await req.json()) as Post
|
|
1081
|
+
} catch {
|
|
1082
|
+
return new Response('Bad Request', { status: 400 })
|
|
1083
|
+
}
|
|
1075
1084
|
const ampless = await admin.getAmpless()
|
|
1076
1085
|
const node = (
|
|
1077
1086
|
<>
|
|
@@ -1079,22 +1088,54 @@ export async function renderPreviewHtml(draft: Post): Promise<string> {
|
|
|
1079
1088
|
{await ampless.publicPostScriptsForPage([draft])}
|
|
1080
1089
|
</>
|
|
1081
1090
|
)
|
|
1082
|
-
|
|
1091
|
+
// Dynamic import: see "Why a Route Handler" below.
|
|
1092
|
+
const { renderToStaticMarkup } = await import('react-dom/server')
|
|
1093
|
+
return new Response(renderToStaticMarkup(node), {
|
|
1094
|
+
headers: {
|
|
1095
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
1096
|
+
'Cache-Control': 'no-store',
|
|
1097
|
+
},
|
|
1098
|
+
})
|
|
1083
1099
|
}
|
|
1084
1100
|
```
|
|
1085
1101
|
|
|
1086
|
-
|
|
1087
|
-
|
|
1102
|
+
`<PostForm>` / `<PostHistoryPanel>` POST the draft to `/admin/preview`
|
|
1103
|
+
out of the box — no extra wiring at the call site:
|
|
1088
1104
|
|
|
1089
1105
|
```tsx
|
|
1090
1106
|
// templates/_shared/app/(admin)/admin/posts/[postId]/page.tsx
|
|
1091
1107
|
import { admin } from '@/lib/admin'
|
|
1092
1108
|
import { createEditPostPage } from '@ampless/admin/pages'
|
|
1093
|
-
import { renderPreviewHtml } from '../../_actions/render-preview'
|
|
1094
1109
|
|
|
1095
|
-
export default createEditPostPage(admin
|
|
1110
|
+
export default createEditPostPage(admin)
|
|
1096
1111
|
```
|
|
1097
1112
|
|
|
1113
|
+
If the admin is mounted at a non-default path (Next.js `basePath` or
|
|
1114
|
+
a custom prefix), override the endpoint via the page factory's
|
|
1115
|
+
`previewEndpoint` option:
|
|
1116
|
+
|
|
1117
|
+
```tsx
|
|
1118
|
+
export default createEditPostPage(admin, { previewEndpoint: '/cms/admin/preview' })
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
Why a Route Handler rather than a Server Action: putting
|
|
1122
|
+
`react-dom/server`-driven rendering inside a `'use server'` module
|
|
1123
|
+
makes Next.js 15+ refuse to compile the edit-post page, because the
|
|
1124
|
+
build traces the import graph from Client Components through Server
|
|
1125
|
+
Action modules and any reach to `react-dom/server` along that path
|
|
1126
|
+
trips the "You're importing a component that imports
|
|
1127
|
+
react-dom/server" check. A Route Handler decouples the rendering
|
|
1128
|
+
from that graph entirely — `<PostForm>` fetches a plain HTTP
|
|
1129
|
+
endpoint and the bundler never walks from the form into here. The
|
|
1130
|
+
handler's explicit `admin.isEditor()` check also defends against
|
|
1131
|
+
accidental exposure if the `(admin)` route-group gate is ever
|
|
1132
|
+
misconfigured. The `react-dom/server` import itself is dynamic
|
|
1133
|
+
because Next.js 16's Turbopack flags any top-level static import of
|
|
1134
|
+
`react-dom/server` reached from the app router build, Route Handlers
|
|
1135
|
+
included; deferring it to request time keeps the module outside the
|
|
1136
|
+
build-time import-graph walker while still loading it from the same
|
|
1137
|
+
Node.js subpath at runtime.
|
|
1138
|
+
|
|
1098
1139
|
The iframe's `sandbox="allow-scripts"` (without `allow-same-origin`)
|
|
1099
1140
|
keeps preview content in an opaque origin so widget scripts cannot
|
|
1100
1141
|
reach the admin's DOM. The trade-off is that some widgets may misbehave
|
|
@@ -20,26 +20,27 @@
|
|
|
20
20
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
21
21
|
"@radix-ui/react-label": "^2.1.8",
|
|
22
22
|
"@radix-ui/react-slot": "^1.2.4",
|
|
23
|
+
"@tiptap/core": "^3.23.6",
|
|
23
24
|
"@tiptap/extension-image": "^3.23.6",
|
|
24
25
|
"@tiptap/extension-link": "^3.23.6",
|
|
25
26
|
"@tiptap/pm": "^3.23.6",
|
|
26
27
|
"@tiptap/react": "^3.23.6",
|
|
27
28
|
"@tiptap/starter-kit": "^3.23.6",
|
|
28
|
-
"@ampless/plugin-analytics-ga4": "^0.2.0-alpha.
|
|
29
|
-
"@ampless/plugin-cookie-consent": "^0.1.0-alpha.
|
|
30
|
-
"@ampless/plugin-gtm": "^0.2.0-alpha.
|
|
31
|
-
"@ampless/plugin-og-image": "^0.2.0-alpha.
|
|
32
|
-
"@ampless/plugin-plausible": "^0.2.0-alpha.
|
|
33
|
-
"@ampless/plugin-reading-time": "^0.1.0-alpha.
|
|
34
|
-
"@ampless/plugin-rss": "^0.2.0-alpha.
|
|
35
|
-
"@ampless/plugin-schema-jsonld": "^0.1.1-alpha.
|
|
36
|
-
"@ampless/plugin-seo": "^0.2.0-alpha.
|
|
37
|
-
"@ampless/plugin-webhook": "^0.2.0-alpha.
|
|
38
|
-
"@ampless/admin": "^1.0.0-alpha.
|
|
39
|
-
"@ampless/backend": "^1.0.0-alpha.
|
|
40
|
-
"@ampless/runtime": "^1.0.0-alpha.
|
|
29
|
+
"@ampless/plugin-analytics-ga4": "^0.2.0-alpha.27",
|
|
30
|
+
"@ampless/plugin-cookie-consent": "^0.1.0-alpha.18",
|
|
31
|
+
"@ampless/plugin-gtm": "^0.2.0-alpha.26",
|
|
32
|
+
"@ampless/plugin-og-image": "^0.2.0-alpha.43",
|
|
33
|
+
"@ampless/plugin-plausible": "^0.2.0-alpha.26",
|
|
34
|
+
"@ampless/plugin-reading-time": "^0.1.0-alpha.16",
|
|
35
|
+
"@ampless/plugin-rss": "^0.2.0-alpha.43",
|
|
36
|
+
"@ampless/plugin-schema-jsonld": "^0.1.1-alpha.22",
|
|
37
|
+
"@ampless/plugin-seo": "^0.2.0-alpha.43",
|
|
38
|
+
"@ampless/plugin-webhook": "^0.2.0-alpha.44",
|
|
39
|
+
"@ampless/admin": "^1.0.0-alpha.76",
|
|
40
|
+
"@ampless/backend": "^1.0.0-alpha.65",
|
|
41
|
+
"@ampless/runtime": "^1.0.0-alpha.52",
|
|
41
42
|
"@digital-go-jp/tailwind-theme-plugin": "^0.3.4",
|
|
42
|
-
"ampless": "^1.0.0-alpha.
|
|
43
|
+
"ampless": "^1.0.0-alpha.43",
|
|
43
44
|
"aws-amplify": "^6.17.0",
|
|
44
45
|
"class-variance-authority": "^0.7.1",
|
|
45
46
|
"clsx": "^2.1.1",
|
package/package.json
CHANGED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
'use server'
|
|
2
|
-
|
|
3
|
-
// Phase 7 preview pipeline. <PostForm> / <PostHistoryPanel> calls this
|
|
4
|
-
// server action with the in-flight draft Post; we render the body +
|
|
5
|
-
// page-level scripts on the server (where async ReactNode +
|
|
6
|
-
// `contentFields` plugin renderers can run) and return a complete HTML
|
|
7
|
-
// string that the admin shows in an iframe (sandbox=`allow-scripts`).
|
|
8
|
-
//
|
|
9
|
-
// Server-side is the only place this can live: `ampless.renderBody`
|
|
10
|
-
// returns a Promise<ReactNode>, so client code can't call it inline.
|
|
11
|
-
// The action also has access to the `Ampless` instance via
|
|
12
|
-
// `admin.getAmpless()` which the client never sees.
|
|
13
|
-
|
|
14
|
-
import { renderToStaticMarkup } from 'react-dom/server'
|
|
15
|
-
import type { Post } from 'ampless'
|
|
16
|
-
import { admin } from '@/lib/admin'
|
|
17
|
-
|
|
18
|
-
export async function renderPreviewHtml(draft: Post): Promise<string> {
|
|
19
|
-
const ampless = await admin.getAmpless()
|
|
20
|
-
// IMPORTANT: include BOTH the body and the page-level scripts so
|
|
21
|
-
// widgets like x.com's `widgets.js` get a chance to hydrate in the
|
|
22
|
-
// iframe. Without `publicPostScriptsForPage([draft])` the embed
|
|
23
|
-
// blockquote would render but never load the widget JS, and authors
|
|
24
|
-
// wouldn't see what the public page actually looks like.
|
|
25
|
-
const node = (
|
|
26
|
-
<>
|
|
27
|
-
{await ampless.renderBody(draft)}
|
|
28
|
-
{await ampless.publicPostScriptsForPage([draft])}
|
|
29
|
-
</>
|
|
30
|
-
)
|
|
31
|
-
return renderToStaticMarkup(node)
|
|
32
|
-
}
|