ampless 1.0.0-beta.53 → 1.0.0-beta.55
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.d.ts +15 -1
- package/dist/index.js +20 -0
- package/docs/plugin-author-guide.ja.md +45 -2
- package/docs/plugin-author-guide.md +48 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -17,12 +17,20 @@ type SiteSettingsEventType = 'site.settings.updated';
|
|
|
17
17
|
*/
|
|
18
18
|
type PostIndexEventType = 'post.index.refresh';
|
|
19
19
|
type EventType = ContentEventType | MediaEventType | SiteSettingsEventType | PostIndexEventType;
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Minimal projection of a Post item carried in events (no body, to keep
|
|
22
|
+
* payloads small). `format` / `excerpt` are included so the denormalized
|
|
23
|
+
* PostTag index can render tag-page summaries faithfully — without them the
|
|
24
|
+
* `listPostsByTag` resolver can't know a post's real format and would
|
|
25
|
+
* mislabel non-markdown posts.
|
|
26
|
+
*/
|
|
21
27
|
interface ContentEventPayload {
|
|
22
28
|
postId: string;
|
|
23
29
|
slug: string;
|
|
24
30
|
title: string;
|
|
25
31
|
status: 'draft' | 'published';
|
|
32
|
+
format?: ContentFormat;
|
|
33
|
+
excerpt?: string;
|
|
26
34
|
publishedAt?: string;
|
|
27
35
|
tags?: string[];
|
|
28
36
|
}
|
|
@@ -1352,6 +1360,12 @@ interface AmplessPlugin {
|
|
|
1352
1360
|
* capability.
|
|
1353
1361
|
*/
|
|
1354
1362
|
publicPostScript?(post: Post, ctx: PluginPublicRenderContext): readonly PublicPostScriptDescriptor[];
|
|
1363
|
+
/**
|
|
1364
|
+
* Server-safe map of TipTap nodeType -> markdown serialiser, used by the
|
|
1365
|
+
* public runtime (postToMarkdown) — same shape the admin registry consumes.
|
|
1366
|
+
* Keep implementations free of '@tiptap/*' imports and 'use client'.
|
|
1367
|
+
*/
|
|
1368
|
+
tiptapNodeToMarkdown?: TiptapNodeMarkdownAdapters;
|
|
1355
1369
|
/**
|
|
1356
1370
|
* Dynamic OG image renderer. The dispatcher route (e.g.
|
|
1357
1371
|
* `app/og/[slug]/route.ts`) reads this and feeds the element into
|
package/dist/index.js
CHANGED
|
@@ -258,6 +258,16 @@ function definePlugin(p) {
|
|
|
258
258
|
);
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
|
+
if (p.contentFields) {
|
|
262
|
+
for (const field of p.contentFields) {
|
|
263
|
+
if (field.kind !== "tiptap") continue;
|
|
264
|
+
if (typeof p.tiptapNodeToMarkdown?.[field.nodeType] !== "function") {
|
|
265
|
+
console.warn(
|
|
266
|
+
`[ampless] Plugin "${p.name}": contentFields declares tiptap nodeType "${field.nodeType}" but tiptapNodeToMarkdown has no adapter for it. The public runtime's markdown export will fall back to the default switch for this node. Add an adapter to tiptapNodeToMarkdown or ignore this if the fallback output is acceptable.`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
261
271
|
if (p.trust_level === "privileged") {
|
|
262
272
|
const hasHooks = p.hooks && Object.keys(p.hooks).length > 0 || (p.capabilities?.includes("eventHooks") ?? false);
|
|
263
273
|
if (hasHooks) {
|
|
@@ -529,6 +539,11 @@ function compareRows(a, b, sort) {
|
|
|
529
539
|
return b.title.localeCompare(a.title);
|
|
530
540
|
case "title-asc":
|
|
531
541
|
return a.title.localeCompare(b.title);
|
|
542
|
+
default: {
|
|
543
|
+
const _exhaustive = sort;
|
|
544
|
+
void _exhaustive;
|
|
545
|
+
return 0;
|
|
546
|
+
}
|
|
532
547
|
}
|
|
533
548
|
}
|
|
534
549
|
function compareOptionalIso(a, b, direction) {
|
|
@@ -766,6 +781,11 @@ function validateThemeValue(field, raw) {
|
|
|
766
781
|
const sanitized = v.replace(/[\x00-\x1f<>]/g, "");
|
|
767
782
|
return sanitized.length <= max ? sanitized : sanitized.slice(0, max);
|
|
768
783
|
}
|
|
784
|
+
default: {
|
|
785
|
+
const _exhaustive = field;
|
|
786
|
+
void _exhaustive;
|
|
787
|
+
return null;
|
|
788
|
+
}
|
|
769
789
|
}
|
|
770
790
|
}
|
|
771
791
|
var DANGEROUS_URL_RE = /^\s*(javascript|data|vbscript):/i;
|
|
@@ -186,6 +186,7 @@ interface AmplessPlugin {
|
|
|
186
186
|
publicBodyEnd?(ctx): readonly PublicBodyDescriptor[]
|
|
187
187
|
publicBodyForPost?(post: Post, ctx): readonly PublicPostBodyDescriptor[]
|
|
188
188
|
publicHtmlForPost?(post: Post, ctx): readonly PublicPostHtmlDescriptor[]
|
|
189
|
+
tiptapNodeToMarkdown?: TiptapNodeMarkdownAdapters // server-safe markdown export adapters
|
|
189
190
|
ogImage?: OgImageConfig
|
|
190
191
|
settings?: {
|
|
191
192
|
public?: readonly PluginSettingField[]
|
|
@@ -884,6 +885,48 @@ export function EditorBootstrap({ children }: { children: React.ReactNode }) {
|
|
|
884
885
|
}
|
|
885
886
|
```
|
|
886
887
|
|
|
888
|
+
### アダプターの置き場所: `./editor.tsx` ではなく `./adapters.ts`
|
|
889
|
+
|
|
890
|
+
`./editor.tsx` は `'use client'` かつ `@tiptap/core` を import している —
|
|
891
|
+
admin バンドルには問題ないが、server コード(や将来の `postToMarkdown`
|
|
892
|
+
public runtime)からこれを import すると両方を巻き込んでしまう。アダプター
|
|
893
|
+
(および `Node.renderHTML` と共有する `placeholderAttrs` のような plain
|
|
894
|
+
object ヘルパ)は、tiptap 非依存の別モジュール `./adapters.ts` に置き、
|
|
895
|
+
`./editor.tsx` からは 2 つの named symbol を **re-export** する:
|
|
896
|
+
|
|
897
|
+
```ts
|
|
898
|
+
// packages/plugin-youtube/src/adapters.ts — 'use client' なし、@tiptap/* import なし
|
|
899
|
+
export const tiptapNodeToMarkdown: TiptapNodeMarkdownAdapters = { /* ... */ }
|
|
900
|
+
export const tiptapNodeToHtml: TiptapNodeHtmlAdapters = { /* ... */ }
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
```ts
|
|
904
|
+
// packages/plugin-youtube/src/editor.tsx
|
|
905
|
+
export { tiptapNodeToMarkdown, tiptapNodeToHtml } from './adapters.js'
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
この re-export により `update-ampless` の codegen 契約は無改修で維持される
|
|
909
|
+
(下記「配線」の通り、`./editor` の namespace import から `ns.tiptapNodeToMarkdown ?? {}` /
|
|
910
|
+
`ns.tiptapNodeToHtml ?? {}` を読む挙動は変わらない)。同時に server 側
|
|
911
|
+
entry(`./index.tsx`)も同じアダプターを `./adapters.js` から import して
|
|
912
|
+
manifest に設定できる:
|
|
913
|
+
|
|
914
|
+
```ts
|
|
915
|
+
// packages/plugin-youtube/src/index.tsx
|
|
916
|
+
import { tiptapNodeToMarkdown } from './adapters.js'
|
|
917
|
+
|
|
918
|
+
export default function youtubePlugin(opts = {}) {
|
|
919
|
+
return definePlugin({
|
|
920
|
+
// ...
|
|
921
|
+
tiptapNodeToMarkdown, // AmplessPlugin.tiptapNodeToMarkdown — server 側 canonical
|
|
922
|
+
})
|
|
923
|
+
}
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
`definePlugin()` は `contentFields` の `kind: 'tiptap'` エントリの
|
|
927
|
+
`nodeType` に対応する `tiptapNodeToMarkdown` のキーが無い場合に warn する
|
|
928
|
+
— 両方をセットで宣言し、warning が出ない状態を保つ。
|
|
929
|
+
|
|
887
930
|
### フォーマット切り替えの可逆アダプター(`tiptapNodeToMarkdown` + `tiptapNodeToHtml`)
|
|
888
931
|
|
|
889
932
|
operator が admin UI でポストのフォーマットを切り替える場合(例: `tiptap → markdown`、`tiptap → html`)、admin はボディコンテンツを変換する必要がある。通常の prose node は tiptap の built-in レンダラーで処理されるが、**atom node**(`amplessYoutube` のような embed ブロック)は子要素を持たず、children が空のまま fallthrough し、embed が無音で消えてしまう。
|
|
@@ -920,7 +963,7 @@ embed node を持つが `htmlPlaceholder` を**宣言しない** plugin は従
|
|
|
920
963
|
**文字列**(空文字 `''` も含む)を返すと、その出力を使う。**`null`** を返すとデフォルトの switch へ fallthrough する(対応しない node や video id が欠損している場合などに使う)。
|
|
921
964
|
|
|
922
965
|
```ts
|
|
923
|
-
// packages/plugin-youtube/src/
|
|
966
|
+
// packages/plugin-youtube/src/adapters.ts
|
|
924
967
|
import type { TiptapNodeMarkdownAdapters, TiptapNodeHtmlAdapters } from 'ampless'
|
|
925
968
|
|
|
926
969
|
export const tiptapNodeToMarkdown: TiptapNodeMarkdownAdapters = {
|
|
@@ -951,7 +994,7 @@ export const tiptapNodeToHtml: TiptapNodeHtmlAdapters = {
|
|
|
951
994
|
|
|
952
995
|
#### 配線
|
|
953
996
|
|
|
954
|
-
`update-ampless` が各プラグインの `./editor` モジュールから `tiptapNodeToMarkdown` と `tiptapNodeToHtml` の named export を読み取り(namespace import `* as` 経由)、両方の install に自動で配線する。**手動での配線は不要。** プラグインがいずれかのマップを export しない場合、生成ファイルの `?? {}` fallback が no-op
|
|
997
|
+
`update-ampless` が各プラグインの `./editor` モジュールから `tiptapNodeToMarkdown` と `tiptapNodeToHtml` の named export を読み取り(namespace import `* as` 経由)、両方の install に自動で配線する。**手動での配線は不要。** プラグインがいずれかのマップを export しない場合、生成ファイルの `?? {}` fallback が no-op になる。上記の通り、`./editor` が re-export するこれらの export は通常 `./adapters.ts` に実装されている — codegen は `./editor` の namespace から到達可能であることだけを見ており、定義場所は問わない。
|
|
955
998
|
|
|
956
999
|
#### `markdown → html` 2-hop
|
|
957
1000
|
|
|
@@ -214,6 +214,7 @@ interface AmplessPlugin {
|
|
|
214
214
|
publicBodyEnd?(ctx): readonly PublicBodyDescriptor[]
|
|
215
215
|
publicBodyForPost?(post: Post, ctx): readonly PublicPostBodyDescriptor[]
|
|
216
216
|
publicHtmlForPost?(post: Post, ctx): readonly PublicPostHtmlDescriptor[]
|
|
217
|
+
tiptapNodeToMarkdown?: TiptapNodeMarkdownAdapters // server-safe markdown export adapters
|
|
217
218
|
ogImage?: OgImageConfig
|
|
218
219
|
settings?: {
|
|
219
220
|
public?: readonly PluginSettingField[]
|
|
@@ -1142,6 +1143,48 @@ export function EditorBootstrap({ children }: { children: React.ReactNode }) {
|
|
|
1142
1143
|
}
|
|
1143
1144
|
```
|
|
1144
1145
|
|
|
1146
|
+
### Where the adapters live: `./adapters.ts`, not `./editor.tsx`
|
|
1147
|
+
|
|
1148
|
+
`./editor.tsx` carries `'use client'` and imports `@tiptap/core` — fine for
|
|
1149
|
+
the admin bundle, but importing it from server code (or the future
|
|
1150
|
+
`postToMarkdown` public runtime) would drag both along. Put the adapters
|
|
1151
|
+
(and any plain-object helpers they share with `Node.renderHTML`, like
|
|
1152
|
+
`placeholderAttrs`) in a separate, tiptap-free module — `./adapters.ts` —
|
|
1153
|
+
and have `./editor.tsx` **re-export** the two named symbols:
|
|
1154
|
+
|
|
1155
|
+
```ts
|
|
1156
|
+
// packages/plugin-youtube/src/adapters.ts — no 'use client', no @tiptap/* import
|
|
1157
|
+
export const tiptapNodeToMarkdown: TiptapNodeMarkdownAdapters = { /* ... */ }
|
|
1158
|
+
export const tiptapNodeToHtml: TiptapNodeHtmlAdapters = { /* ... */ }
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
```ts
|
|
1162
|
+
// packages/plugin-youtube/src/editor.tsx
|
|
1163
|
+
export { tiptapNodeToMarkdown, tiptapNodeToHtml } from './adapters.js'
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
The re-export keeps `update-ampless`'s codegen contract intact (it still
|
|
1167
|
+
reads `ns.tiptapNodeToMarkdown ?? {}` / `ns.tiptapNodeToHtml ?? {}` from a
|
|
1168
|
+
namespace import of `./editor` — see Wiring below) while also letting your
|
|
1169
|
+
server entry (`./index.tsx`) import the same adapter from `./adapters.js`
|
|
1170
|
+
and set it on the manifest:
|
|
1171
|
+
|
|
1172
|
+
```ts
|
|
1173
|
+
// packages/plugin-youtube/src/index.tsx
|
|
1174
|
+
import { tiptapNodeToMarkdown } from './adapters.js'
|
|
1175
|
+
|
|
1176
|
+
export default function youtubePlugin(opts = {}) {
|
|
1177
|
+
return definePlugin({
|
|
1178
|
+
// ...
|
|
1179
|
+
tiptapNodeToMarkdown, // AmplessPlugin.tiptapNodeToMarkdown — server-side canonical
|
|
1180
|
+
})
|
|
1181
|
+
}
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
`definePlugin()` warns if a `contentFields` `kind: 'tiptap'` entry's
|
|
1185
|
+
`nodeType` has no matching key in `tiptapNodeToMarkdown` — declare both
|
|
1186
|
+
together so the warning stays silent.
|
|
1187
|
+
|
|
1145
1188
|
### Lossless format-switch adapters (`tiptapNodeToMarkdown` + `tiptapNodeToHtml`)
|
|
1146
1189
|
|
|
1147
1190
|
When the operator switches the post format in the admin UI (e.g. `tiptap →
|
|
@@ -1206,7 +1249,7 @@ Return **`null`** to fall through to the default switch (useful for nodes
|
|
|
1206
1249
|
you don't handle or for degenerate inputs like a missing video id).
|
|
1207
1250
|
|
|
1208
1251
|
```ts
|
|
1209
|
-
// packages/plugin-youtube/src/
|
|
1252
|
+
// packages/plugin-youtube/src/adapters.ts
|
|
1210
1253
|
import type { TiptapNodeMarkdownAdapters, TiptapNodeHtmlAdapters } from 'ampless'
|
|
1211
1254
|
|
|
1212
1255
|
export const tiptapNodeToMarkdown: TiptapNodeMarkdownAdapters = {
|
|
@@ -1244,7 +1287,10 @@ export const tiptapNodeToHtml: TiptapNodeHtmlAdapters = {
|
|
|
1244
1287
|
named exports from each plugin's `./editor` module (via namespace import `*
|
|
1245
1288
|
as`) and wires them into both installs automatically — **no hand-wiring
|
|
1246
1289
|
required**. If your plugin does not export one of the maps, the `?? {}`
|
|
1247
|
-
fallback in the generated file is a no-op.
|
|
1290
|
+
fallback in the generated file is a no-op. As noted above, the exports
|
|
1291
|
+
`./editor` re-exports normally live in `./adapters.ts` — the codegen only
|
|
1292
|
+
cares that they're reachable from the `./editor` namespace, not where
|
|
1293
|
+
they're defined.
|
|
1248
1294
|
|
|
1249
1295
|
#### `markdown → html` 2-hop
|
|
1250
1296
|
|