vestrycms 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -2
  3. package/dist/config/fields.d.ts +30 -0
  4. package/dist/config/fields.js +75 -0
  5. package/dist/config/index.d.ts +9 -0
  6. package/dist/config/index.js +18 -0
  7. package/dist/config/types.d.ts +403 -0
  8. package/dist/config/types.js +10 -0
  9. package/dist/core/api.d.ts +67 -0
  10. package/dist/core/api.js +538 -0
  11. package/dist/core/auth.d.ts +1561 -0
  12. package/dist/core/auth.js +234 -0
  13. package/dist/core/cache.d.ts +27 -0
  14. package/dist/core/cache.js +52 -0
  15. package/dist/core/entries.d.ts +19 -0
  16. package/dist/core/entries.js +127 -0
  17. package/dist/core/index-types.d.ts +20 -0
  18. package/dist/core/index-types.js +1 -0
  19. package/dist/core/index.d.ts +13 -0
  20. package/dist/core/index.js +13 -0
  21. package/dist/core/mail.d.ts +28 -0
  22. package/dist/core/mail.js +62 -0
  23. package/dist/core/paths.d.ts +35 -0
  24. package/dist/core/paths.js +148 -0
  25. package/dist/core/roles.d.ts +24 -0
  26. package/dist/core/roles.js +15 -0
  27. package/dist/core/service.d.ts +102 -0
  28. package/dist/core/service.js +478 -0
  29. package/dist/core/source.d.ts +82 -0
  30. package/dist/core/source.js +75 -0
  31. package/dist/core/sources/github-app.d.ts +26 -0
  32. package/dist/core/sources/github-app.js +161 -0
  33. package/dist/core/sources/github.d.ts +40 -0
  34. package/dist/core/sources/github.js +278 -0
  35. package/dist/core/sources/local.d.ts +16 -0
  36. package/dist/core/sources/local.js +98 -0
  37. package/dist/core/values.d.ts +22 -0
  38. package/dist/core/values.js +123 -0
  39. package/dist/env.d.ts +19 -0
  40. package/dist/index.d.ts +18 -0
  41. package/dist/index.js +167 -0
  42. package/dist/integration/build-index.d.ts +8 -0
  43. package/dist/integration/build-index.js +67 -0
  44. package/dist/integration/client-deps.d.ts +10 -0
  45. package/dist/integration/client-deps.js +43 -0
  46. package/dist/integration/dev-middleware.d.ts +6 -0
  47. package/dist/integration/dev-middleware.js +48 -0
  48. package/dist/integration/formatter.d.ts +7 -0
  49. package/dist/integration/formatter.js +56 -0
  50. package/dist/mdx/expressions.d.ts +14 -0
  51. package/dist/mdx/expressions.js +141 -0
  52. package/dist/mdx/index.d.ts +4 -0
  53. package/dist/mdx/index.js +4 -0
  54. package/dist/mdx/parse.d.ts +6 -0
  55. package/dist/mdx/parse.js +378 -0
  56. package/dist/mdx/serialize.d.ts +9 -0
  57. package/dist/mdx/serialize.js +396 -0
  58. package/dist/mdx/types.d.ts +40 -0
  59. package/dist/mdx/types.js +6 -0
  60. package/dist/pages/admin.astro +27 -0
  61. package/dist/pages/api.d.ts +3 -0
  62. package/dist/pages/api.js +31 -0
  63. package/dist/ui/App.d.ts +4 -0
  64. package/dist/ui/App.js +70 -0
  65. package/dist/ui/api.d.ts +83 -0
  66. package/dist/ui/api.js +97 -0
  67. package/dist/ui/auth-client.d.ts +27 -0
  68. package/dist/ui/auth-client.js +29 -0
  69. package/dist/ui/components/AssetPicker.d.ts +27 -0
  70. package/dist/ui/components/AssetPicker.js +97 -0
  71. package/dist/ui/components/Icon.d.ts +11 -0
  72. package/dist/ui/components/Icon.js +46 -0
  73. package/dist/ui/components/Layout.d.ts +15 -0
  74. package/dist/ui/components/Layout.js +87 -0
  75. package/dist/ui/components/editor/EditorContext.d.ts +15 -0
  76. package/dist/ui/components/editor/EditorContext.js +9 -0
  77. package/dist/ui/components/editor/NodeViews.d.ts +4 -0
  78. package/dist/ui/components/editor/NodeViews.js +127 -0
  79. package/dist/ui/components/editor/RichTextEditor.d.ts +24 -0
  80. package/dist/ui/components/editor/RichTextEditor.js +253 -0
  81. package/dist/ui/components/editor/extensions.d.ts +20 -0
  82. package/dist/ui/components/editor/extensions.js +156 -0
  83. package/dist/ui/components/fields/CodeInput.d.ts +10 -0
  84. package/dist/ui/components/fields/CodeInput.js +8 -0
  85. package/dist/ui/components/fields/CodeMirrorInput.d.ts +2 -0
  86. package/dist/ui/components/fields/CodeMirrorInput.js +44 -0
  87. package/dist/ui/components/fields/FieldRenderer.d.ts +41 -0
  88. package/dist/ui/components/fields/FieldRenderer.js +299 -0
  89. package/dist/ui/components/fields/ImageTools.d.ts +31 -0
  90. package/dist/ui/components/fields/ImageTools.js +296 -0
  91. package/dist/ui/components/fields/MarkdownField.d.ts +7 -0
  92. package/dist/ui/components/fields/MarkdownField.js +16 -0
  93. package/dist/ui/components/fields/TreeInput.d.ts +13 -0
  94. package/dist/ui/components/fields/TreeInput.js +221 -0
  95. package/dist/ui/components/ui.d.ts +63 -0
  96. package/dist/ui/components/ui.js +113 -0
  97. package/dist/ui/context.d.ts +17 -0
  98. package/dist/ui/context.js +13 -0
  99. package/dist/ui/lib/format.d.ts +5 -0
  100. package/dist/ui/lib/format.js +11 -0
  101. package/dist/ui/lib/utils.d.ts +24 -0
  102. package/dist/ui/lib/utils.js +255 -0
  103. package/dist/ui/main.d.ts +1 -0
  104. package/dist/ui/main.js +11 -0
  105. package/dist/ui/pages/Assets.d.ts +1 -0
  106. package/dist/ui/pages/Assets.js +69 -0
  107. package/dist/ui/pages/CollectionList.d.ts +6 -0
  108. package/dist/ui/pages/CollectionList.js +115 -0
  109. package/dist/ui/pages/Dashboard.d.ts +1 -0
  110. package/dist/ui/pages/Dashboard.js +57 -0
  111. package/dist/ui/pages/EntryEditor.d.ts +3 -0
  112. package/dist/ui/pages/EntryEditor.js +313 -0
  113. package/dist/ui/pages/Login.d.ts +6 -0
  114. package/dist/ui/pages/Login.js +88 -0
  115. package/dist/ui/pages/Users.d.ts +1 -0
  116. package/dist/ui/pages/Users.js +102 -0
  117. package/dist/ui/styles.css +2536 -0
  118. package/package.json +121 -3
@@ -0,0 +1,141 @@
1
+ import { isRawExpression } from "./types.js";
2
+ /**
3
+ * Convert a parsed JSX attribute expression to a plain JSON value when it is
4
+ * made only of literals; otherwise return a RawExpression holding the source.
5
+ */
6
+ export function estreeToValue(node, source) {
7
+ if (!node)
8
+ return { $raw: source };
9
+ try {
10
+ return literal(node);
11
+ }
12
+ catch {
13
+ return { $raw: source };
14
+ }
15
+ }
16
+ class NotLiteral extends Error {
17
+ }
18
+ function literal(node) {
19
+ switch (node.type) {
20
+ case "Program": {
21
+ const body = node.body;
22
+ if (body.length !== 1 || body[0].type !== "ExpressionStatement")
23
+ throw new NotLiteral();
24
+ return literal(body[0].expression);
25
+ }
26
+ case "ExpressionStatement":
27
+ return literal(node.expression);
28
+ case "Literal": {
29
+ const v = node.value;
30
+ if (v instanceof RegExp)
31
+ throw new NotLiteral();
32
+ return v ?? null;
33
+ }
34
+ case "TemplateLiteral": {
35
+ const t = node;
36
+ if (t.expressions.length)
37
+ throw new NotLiteral();
38
+ return t.quasis.map((q) => q.value.cooked ?? q.value.raw).join("");
39
+ }
40
+ case "UnaryExpression": {
41
+ const u = node;
42
+ const arg = literal(u.argument);
43
+ if (u.operator === "-" && typeof arg === "number")
44
+ return -arg;
45
+ if (u.operator === "+" && typeof arg === "number")
46
+ return arg;
47
+ if (u.operator === "!")
48
+ return !arg;
49
+ throw new NotLiteral();
50
+ }
51
+ case "Identifier": {
52
+ const name = node.name;
53
+ if (name === "undefined")
54
+ return undefined;
55
+ throw new NotLiteral();
56
+ }
57
+ case "ArrayExpression":
58
+ return node.elements.map((el) => {
59
+ if (!el || el.type === "SpreadElement")
60
+ throw new NotLiteral();
61
+ return literal(el);
62
+ });
63
+ case "ObjectExpression": {
64
+ const out = {};
65
+ for (const p of node.properties) {
66
+ if (p.type !== "Property" ||
67
+ p.kind !== "init" ||
68
+ p.method ||
69
+ p.computed)
70
+ throw new NotLiteral();
71
+ let key;
72
+ if (p.key.type === "Identifier")
73
+ key = p.key.name;
74
+ else if (p.key.type === "Literal")
75
+ key = String(p.key.value);
76
+ else
77
+ throw new NotLiteral();
78
+ out[key] = literal(p.value);
79
+ }
80
+ return out;
81
+ }
82
+ default:
83
+ throw new NotLiteral();
84
+ }
85
+ }
86
+ const IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
87
+ const PRINT_WIDTH = 80;
88
+ /**
89
+ * Serialize a JSON value (or RawExpression) to JS source for a JSX attribute,
90
+ * in the shape Prettier produces: multi-line objects and arrays with trailing
91
+ * commas, bare identifier keys, double-quoted strings.
92
+ */
93
+ export function valueToSource(value, indent = "") {
94
+ const inner = `${indent} `;
95
+ if (isRawExpression(value))
96
+ return value.$raw.trim();
97
+ if (value === undefined)
98
+ return "undefined";
99
+ if (value === null)
100
+ return "null";
101
+ if (typeof value === "string")
102
+ return JSON.stringify(value);
103
+ if (typeof value === "number" || typeof value === "boolean")
104
+ return String(value);
105
+ if (value instanceof Date)
106
+ return JSON.stringify(value.toISOString());
107
+ if (Array.isArray(value)) {
108
+ if (!value.length)
109
+ return "[]";
110
+ const simple = value.every((v) => isScalar(v));
111
+ const items = value.map((v) => valueToSource(v, inner));
112
+ if (simple) {
113
+ const oneLine = `[${items.join(", ")}]`;
114
+ if (oneLine.length + indent.length <= PRINT_WIDTH - 8)
115
+ return oneLine;
116
+ }
117
+ return `[\n${items.map((i) => `${inner}${i},`).join("\n")}\n${indent}]`;
118
+ }
119
+ if (typeof value === "object") {
120
+ const entries = Object.entries(value).filter(([, v]) => v !== undefined);
121
+ if (!entries.length)
122
+ return "{}";
123
+ const keyOf = (k) => (IDENT.test(k) ? k : JSON.stringify(k));
124
+ // Like Prettier: a flat object that fits on one line stays on one line.
125
+ if (entries.every(([, v]) => isScalar(v))) {
126
+ const oneLine = `{ ${entries.map(([k, v]) => `${keyOf(k)}: ${valueToSource(v)}`).join(", ")} }`;
127
+ if (oneLine.length + indent.length <= PRINT_WIDTH - 2)
128
+ return oneLine;
129
+ }
130
+ const lines = entries.map(([k, v]) => `${inner}${keyOf(k)}: ${valueToSource(v, inner)},`);
131
+ return `{\n${lines.join("\n")}\n${indent}}`;
132
+ }
133
+ return JSON.stringify(value) ?? "null";
134
+ }
135
+ function isScalar(v) {
136
+ return v === null || ["string", "number", "boolean"].includes(typeof v);
137
+ }
138
+ /** Whether a value can be written as a plain string attribute (`a="b"`) rather than an expression. */
139
+ export function isPlainString(value) {
140
+ return typeof value === "string" && !value.includes("\n");
141
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./types.js";
2
+ export { parseMdx } from "./parse.js";
3
+ export { serializeMdx } from "./serialize.js";
4
+ export { estreeToValue, valueToSource } from "./expressions.js";
@@ -0,0 +1,4 @@
1
+ export * from "./types.js";
2
+ export { parseMdx } from "./parse.js";
3
+ export { serializeMdx } from "./serialize.js";
4
+ export { estreeToValue, valueToSource } from "./expressions.js";
@@ -0,0 +1,6 @@
1
+ import type { ParsedDocument } from "./types.js";
2
+ export interface ParseOptions {
3
+ /** `md` disables JSX/ESM parsing (raw HTML is preserved as html nodes). */
4
+ format?: "md" | "mdx";
5
+ }
6
+ export declare function parseMdx(source: string, options?: ParseOptions): ParsedDocument;
@@ -0,0 +1,378 @@
1
+ import { fromMarkdown } from "mdast-util-from-markdown";
2
+ import { gfmFromMarkdown } from "mdast-util-gfm";
3
+ import { mdxFromMarkdown } from "mdast-util-mdx";
4
+ import { gfm } from "micromark-extension-gfm";
5
+ import { mdxjs } from "micromark-extension-mdxjs";
6
+ import { estreeToValue } from "./expressions.js";
7
+ const SUPERSCRIPT_TAGS = new Set(["sup"]);
8
+ const SUBSCRIPT_TAGS = new Set(["sub"]);
9
+ export function parseMdx(source, options = {}) {
10
+ const isMdx = options.format !== "md";
11
+ const tree = fromMarkdown(source, {
12
+ extensions: isMdx ? [mdxjs(), gfm()] : [gfm()],
13
+ mdastExtensions: isMdx
14
+ ? [mdxFromMarkdown(), gfmFromMarkdown()]
15
+ : [gfmFromMarkdown()],
16
+ });
17
+ const ctx = { imports: {}, definitions: new Map() };
18
+ collectDefinitions(tree, ctx);
19
+ const content = blocks(tree.children, ctx);
20
+ return {
21
+ doc: {
22
+ type: "doc",
23
+ content: content.length ? content : [{ type: "paragraph" }],
24
+ },
25
+ imports: ctx.imports,
26
+ };
27
+ }
28
+ function collectDefinitions(tree, ctx) {
29
+ const visit = (node) => {
30
+ if (node.type === "definition") {
31
+ const d = node;
32
+ ctx.definitions.set(d.identifier.toLowerCase(), d);
33
+ }
34
+ const children = node.children;
35
+ if (children)
36
+ children.forEach(visit);
37
+ };
38
+ visit(tree);
39
+ }
40
+ function blocks(nodes, ctx) {
41
+ const out = [];
42
+ for (const node of nodes) {
43
+ const converted = block(node, ctx);
44
+ if (converted)
45
+ out.push(...(Array.isArray(converted) ? converted : [converted]));
46
+ }
47
+ return out;
48
+ }
49
+ function block(node, ctx) {
50
+ switch (node.type) {
51
+ case "paragraph":
52
+ return {
53
+ type: "paragraph",
54
+ content: inlines(node.children, ctx),
55
+ };
56
+ case "heading":
57
+ return {
58
+ type: "heading",
59
+ attrs: { level: node.depth },
60
+ content: inlines(node.children, ctx),
61
+ };
62
+ case "thematicBreak":
63
+ return { type: "horizontalRule" };
64
+ case "blockquote":
65
+ return {
66
+ type: "blockquote",
67
+ content: ensureBlocks(blocks(node.children, ctx)),
68
+ };
69
+ case "list": {
70
+ const items = node.children.map((item) => listItem(item, ctx));
71
+ return node.ordered
72
+ ? {
73
+ type: "orderedList",
74
+ attrs: { start: node.start ?? 1 },
75
+ content: items,
76
+ }
77
+ : { type: "bulletList", content: items };
78
+ }
79
+ case "code":
80
+ return {
81
+ type: "codeBlock",
82
+ attrs: { language: node.lang ?? null, meta: node.meta ?? null },
83
+ content: node.value ? [{ type: "text", text: node.value }] : [],
84
+ };
85
+ case "html":
86
+ return { type: "rawHtml", attrs: { value: node.value } };
87
+ case "table":
88
+ return table(node, ctx);
89
+ case "definition":
90
+ case "footnoteDefinition":
91
+ return null;
92
+ case "mdxJsxFlowElement":
93
+ return jsxElement(node, ctx, false);
94
+ case "mdxFlowExpression":
95
+ return { type: "mdxExpression", attrs: { value: node.value } };
96
+ case "mdxjsEsm":
97
+ return esm(node, ctx);
98
+ case "yaml":
99
+ return null;
100
+ default: {
101
+ // Inline content at block level (shouldn't happen) – wrap in a paragraph.
102
+ const inline = inlineNode(node, ctx, []);
103
+ return inline.length ? { type: "paragraph", content: inline } : null;
104
+ }
105
+ }
106
+ }
107
+ function ensureBlocks(content) {
108
+ return content.length ? content : [{ type: "paragraph" }];
109
+ }
110
+ function listItem(item, ctx) {
111
+ const content = ensureBlocks(blocks(item.children, ctx));
112
+ return {
113
+ type: "listItem",
114
+ attrs: item.checked === null || item.checked === undefined
115
+ ? {}
116
+ : { checked: item.checked },
117
+ content,
118
+ };
119
+ }
120
+ function table(node, ctx) {
121
+ const rows = node.children.map((row, rowIdx) => ({
122
+ type: "tableRow",
123
+ content: row.children.map((cell, colIdx) => ({
124
+ type: rowIdx === 0 ? "tableHeader" : "tableCell",
125
+ attrs: { align: node.align?.[colIdx] ?? null },
126
+ content: [
127
+ {
128
+ type: "paragraph",
129
+ content: inlines(cell.children, ctx),
130
+ },
131
+ ],
132
+ })),
133
+ }));
134
+ return { type: "table", content: rows };
135
+ }
136
+ function esm(node, ctx) {
137
+ const program = node.data?.estree;
138
+ const leftovers = [];
139
+ if (program) {
140
+ for (const stmt of program.body) {
141
+ if (stmt.type === "ImportDeclaration" &&
142
+ typeof stmt.source.value === "string") {
143
+ let recognized = true;
144
+ for (const spec of stmt.specifiers) {
145
+ if (spec.type === "ImportDefaultSpecifier") {
146
+ ctx.imports[spec.local.name] = {
147
+ name: spec.local.name,
148
+ from: stmt.source.value,
149
+ };
150
+ }
151
+ else if (spec.type === "ImportSpecifier") {
152
+ const imported = spec.imported.type === "Identifier"
153
+ ? spec.imported.name
154
+ : String(spec.imported.value);
155
+ ctx.imports[spec.local.name] = {
156
+ name: spec.local.name,
157
+ from: stmt.source.value,
158
+ named: imported === spec.local.name ? true : undefined,
159
+ };
160
+ if (imported !== spec.local.name)
161
+ recognized = false;
162
+ }
163
+ else {
164
+ recognized = false;
165
+ }
166
+ }
167
+ if (recognized && stmt.specifiers.length)
168
+ continue;
169
+ }
170
+ const start = stmt.start;
171
+ const end = stmt.end;
172
+ leftovers.push(typeof start === "number" && typeof end === "number"
173
+ ? node.value.slice(start, end)
174
+ : node.value);
175
+ }
176
+ }
177
+ else {
178
+ leftovers.push(node.value);
179
+ }
180
+ if (!leftovers.length)
181
+ return null;
182
+ return { type: "mdxEsm", attrs: { value: leftovers.join("\n") } };
183
+ }
184
+ function attrsToProps(attributes) {
185
+ const props = {};
186
+ const spreads = [];
187
+ for (const attr of attributes) {
188
+ if (attr.type === "mdxJsxExpressionAttribute") {
189
+ spreads.push(attr.value);
190
+ continue;
191
+ }
192
+ if (attr.value === null || attr.value === undefined) {
193
+ props[attr.name] = true;
194
+ }
195
+ else if (typeof attr.value === "string") {
196
+ props[attr.name] = attr.value;
197
+ }
198
+ else {
199
+ const estree = attr.value.data?.estree;
200
+ props[attr.name] = estreeToValue(estree, attr.value.value);
201
+ }
202
+ }
203
+ return { props, spreads };
204
+ }
205
+ function jsxElement(node, ctx, inline) {
206
+ const name = node.name ?? "";
207
+ const { props, spreads } = attrsToProps(node.attributes);
208
+ const children = node.children;
209
+ if (inline) {
210
+ if (SUPERSCRIPT_TAGS.has(name) || SUBSCRIPT_TAGS.has(name)) {
211
+ const mark = {
212
+ type: SUPERSCRIPT_TAGS.has(name) ? "superscript" : "subscript",
213
+ };
214
+ return inlines(children, ctx, [mark]);
215
+ }
216
+ return {
217
+ type: "mdxInlineComponent",
218
+ attrs: { name, props, spreads, hasChildren: children.length > 0 },
219
+ content: inlines(children, ctx),
220
+ };
221
+ }
222
+ // Block element. Children may be flow content or phrasing content (MDX
223
+ // allows `<Foo>text</Foo>`); normalise to blocks.
224
+ const hasFlowChildren = children.some((c) => !isPhrasing(c));
225
+ const content = hasFlowChildren
226
+ ? blocks(children, ctx)
227
+ : children.length
228
+ ? [{ type: "paragraph", content: inlines(children, ctx) }]
229
+ : [];
230
+ return {
231
+ type: "mdxComponent",
232
+ attrs: { name, props, spreads, hasChildren: content.length > 0 },
233
+ content,
234
+ };
235
+ }
236
+ const PHRASING = new Set([
237
+ "text",
238
+ "emphasis",
239
+ "strong",
240
+ "delete",
241
+ "inlineCode",
242
+ "link",
243
+ "linkReference",
244
+ "image",
245
+ "imageReference",
246
+ "break",
247
+ "html",
248
+ "footnoteReference",
249
+ "mdxJsxTextElement",
250
+ "mdxTextExpression",
251
+ ]);
252
+ function isPhrasing(node) {
253
+ return PHRASING.has(node.type);
254
+ }
255
+ function inlines(nodes, ctx, marks = []) {
256
+ const out = [];
257
+ for (const node of nodes)
258
+ out.push(...inlineNode(node, ctx, marks));
259
+ return mergeText(out);
260
+ }
261
+ function withMarks(node, marks) {
262
+ return marks.length
263
+ ? { ...node, marks: [...(node.marks ?? []), ...marks] }
264
+ : node;
265
+ }
266
+ function inlineNode(node, ctx, marks) {
267
+ switch (node.type) {
268
+ case "text":
269
+ return node.value
270
+ ? [withMarks({ type: "text", text: node.value }, marks)]
271
+ : [];
272
+ case "emphasis":
273
+ return inlines(node.children, ctx, [
274
+ ...marks,
275
+ { type: "italic" },
276
+ ]);
277
+ case "strong":
278
+ return inlines(node.children, ctx, [
279
+ ...marks,
280
+ { type: "bold" },
281
+ ]);
282
+ case "delete":
283
+ return inlines(node.children, ctx, [
284
+ ...marks,
285
+ { type: "strike" },
286
+ ]);
287
+ case "inlineCode":
288
+ return [
289
+ withMarks({ type: "text", text: node.value }, [
290
+ ...marks,
291
+ { type: "code" },
292
+ ]),
293
+ ];
294
+ case "link":
295
+ return inlines(node.children, ctx, [
296
+ ...marks,
297
+ { type: "link", attrs: { href: node.url, title: node.title ?? null } },
298
+ ]);
299
+ case "linkReference": {
300
+ const def = ctx.definitions.get(node.identifier.toLowerCase());
301
+ if (!def)
302
+ return inlines(node.children, ctx, marks);
303
+ return inlines(node.children, ctx, [
304
+ ...marks,
305
+ { type: "link", attrs: { href: def.url, title: def.title ?? null } },
306
+ ]);
307
+ }
308
+ case "image":
309
+ return [
310
+ withMarks({
311
+ type: "image",
312
+ attrs: {
313
+ src: node.url,
314
+ alt: node.alt ?? null,
315
+ title: node.title ?? null,
316
+ },
317
+ }, marks),
318
+ ];
319
+ case "imageReference": {
320
+ const def = ctx.definitions.get(node.identifier.toLowerCase());
321
+ return def
322
+ ? [
323
+ withMarks({
324
+ type: "image",
325
+ attrs: {
326
+ src: def.url,
327
+ alt: node.alt ?? null,
328
+ title: def.title ?? null,
329
+ },
330
+ }, marks),
331
+ ]
332
+ : [];
333
+ }
334
+ case "break":
335
+ return [{ type: "hardBreak" }];
336
+ case "html":
337
+ return [{ type: "rawInlineHtml", attrs: { value: node.value } }];
338
+ case "footnoteReference":
339
+ return [
340
+ withMarks({ type: "text", text: `[^${node.identifier}]` }, marks),
341
+ ];
342
+ case "mdxJsxTextElement": {
343
+ const converted = jsxElement(node, ctx, true);
344
+ const arr = Array.isArray(converted) ? converted : [converted];
345
+ return arr.map((n) => (n.type === "text" ? withMarks(n, marks) : n));
346
+ }
347
+ case "mdxTextExpression":
348
+ return [{ type: "mdxInlineExpression", attrs: { value: node.value } }];
349
+ case "mdxJsxFlowElement": {
350
+ const converted = jsxElement(node, ctx, true);
351
+ return Array.isArray(converted) ? converted : [converted];
352
+ }
353
+ default: {
354
+ // Block content inside inline context: flatten to text.
355
+ const children = node.children;
356
+ return children ? inlines(children, ctx, marks) : [];
357
+ }
358
+ }
359
+ }
360
+ function mergeText(nodes) {
361
+ const out = [];
362
+ for (const n of nodes) {
363
+ const prev = out[out.length - 1];
364
+ if (prev &&
365
+ prev.type === "text" &&
366
+ n.type === "text" &&
367
+ sameMarks(prev.marks, n.marks)) {
368
+ prev.text = (prev.text ?? "") + (n.text ?? "");
369
+ }
370
+ else {
371
+ out.push({ ...n });
372
+ }
373
+ }
374
+ return out;
375
+ }
376
+ function sameMarks(a, b) {
377
+ return JSON.stringify(a ?? []) === JSON.stringify(b ?? []);
378
+ }
@@ -0,0 +1,9 @@
1
+ import type { ComponentRegistry, ImportSpec, PMNode } from "./types.js";
2
+ export interface SerializeOptions {
3
+ format?: "md" | "mdx";
4
+ /** Known components; used to emit imports for the ones the document uses. */
5
+ components?: ComponentRegistry;
6
+ /** Imports that were present in the source (used for components not in the registry). */
7
+ existingImports?: Record<string, ImportSpec>;
8
+ }
9
+ export declare function serializeMdx(doc: PMNode, options?: SerializeOptions): string;